Merge "Log the status code when adding or removing a tunnel mode listener fails." into main
diff --git a/apct-tests/perftests/core/src/android/libcore/regression/ByteBufferPerfTest.java b/apct-tests/perftests/core/src/android/libcore/regression/ByteBufferPerfTest.java
index 4cf46e5..f01ac02 100644
--- a/apct-tests/perftests/core/src/android/libcore/regression/ByteBufferPerfTest.java
+++ b/apct-tests/perftests/core/src/android/libcore/regression/ByteBufferPerfTest.java
@@ -565,7 +565,6 @@
}
@Test
- @Parameters(method = "getData")
public void time_new_byteArray() throws Exception {
final BenchmarkState state = mBenchmarkRule.getState();
while (state.keepRunning()) {
@@ -574,7 +573,6 @@
}
@Test
- @Parameters(method = "getData")
public void time_ByteBuffer_allocate() throws Exception {
final BenchmarkState state = mBenchmarkRule.getState();
while (state.keepRunning()) {
diff --git a/api/api.go b/api/api.go
index 5b7f534..e9f1fee 100644
--- a/api/api.go
+++ b/api/api.go
@@ -15,7 +15,7 @@
package api
import (
- "sort"
+ "slices"
"github.com/google/blueprint/proptools"
@@ -75,31 +75,25 @@
var PrepareForCombinedApisTest = android.FixtureRegisterWithContext(registerBuildComponents)
-func (a *CombinedApis) bootclasspath(ctx android.ConfigAndErrorContext) []string {
- return a.properties.Bootclasspath.GetOrDefault(a.ConfigurableEvaluator(ctx), nil)
-}
-
-func (a *CombinedApis) systemServerClasspath(ctx android.ConfigAndErrorContext) []string {
- return a.properties.System_server_classpath.GetOrDefault(a.ConfigurableEvaluator(ctx), nil)
-}
-
func (a *CombinedApis) apiFingerprintStubDeps(ctx android.BottomUpMutatorContext) []string {
- ret := []string{}
+ bootClasspath := a.properties.Bootclasspath.GetOrDefault(ctx, nil)
+ systemServerClasspath := a.properties.System_server_classpath.GetOrDefault(ctx, nil)
+ var ret []string
ret = append(
ret,
- transformArray(a.bootclasspath(ctx), "", ".stubs")...,
+ transformArray(bootClasspath, "", ".stubs")...,
)
ret = append(
ret,
- transformArray(a.bootclasspath(ctx), "", ".stubs.system")...,
+ transformArray(bootClasspath, "", ".stubs.system")...,
)
ret = append(
ret,
- transformArray(a.bootclasspath(ctx), "", ".stubs.module_lib")...,
+ transformArray(bootClasspath, "", ".stubs.module_lib")...,
)
ret = append(
ret,
- transformArray(a.systemServerClasspath(ctx), "", ".stubs.system_server")...,
+ transformArray(systemServerClasspath, "", ".stubs.system_server")...,
)
return ret
}
@@ -129,7 +123,7 @@
Cmd *string
Dists []android.Dist
Out []string
- Srcs []string
+ Srcs proptools.Configurable[[]string]
Tools []string
Visibility []string
}
@@ -137,7 +131,7 @@
type libraryProps struct {
Name *string
Sdk_version *string
- Static_libs []string
+ Static_libs proptools.Configurable[[]string]
Visibility []string
Defaults []string
Is_stubs_module *bool
@@ -145,7 +139,7 @@
type fgProps struct {
Name *string
- Srcs []string
+ Srcs proptools.Configurable[[]string]
Visibility []string
}
@@ -166,7 +160,7 @@
// The module for the non-updatable / non-module part of the api.
BaseTxt string
// The list of modules that are relevant for this merged txt.
- Modules []string
+ Modules proptools.Configurable[[]string]
// The output tag for each module to use.e.g. {.public.api.txt} for current.txt
ModuleTag string
// public, system, module-lib or system-server
@@ -190,7 +184,8 @@
props.Tools = []string{"metalava"}
props.Out = []string{filename}
props.Cmd = proptools.StringPtr(metalavaCmd + "$(in) --out $(out)")
- props.Srcs = append([]string{txt.BaseTxt}, createSrcs(txt.Modules, txt.ModuleTag)...)
+ props.Srcs = proptools.NewSimpleConfigurable([]string{txt.BaseTxt})
+ props.Srcs.Append(createSrcs(txt.Modules, txt.ModuleTag))
if doDist {
props.Dists = []android.Dist{
{
@@ -209,11 +204,11 @@
ctx.CreateModule(genrule.GenRuleFactory, &props)
}
-func createMergedAnnotationsFilegroups(ctx android.LoadHookContext, modules, system_server_modules []string) {
+func createMergedAnnotationsFilegroups(ctx android.LoadHookContext, modules, system_server_modules proptools.Configurable[[]string]) {
for _, i := range []struct {
name string
tag string
- modules []string
+ modules proptools.Configurable[[]string]
}{
{
name: "all-modules-public-annotations",
@@ -240,33 +235,39 @@
}
}
-func createMergedPublicStubs(ctx android.LoadHookContext, modules []string) {
+func createMergedPublicStubs(ctx android.LoadHookContext, modules proptools.Configurable[[]string]) {
+ modules = modules.Clone()
+ transformConfigurableArray(modules, "", ".stubs")
props := libraryProps{}
props.Name = proptools.StringPtr("all-modules-public-stubs")
- props.Static_libs = transformArray(modules, "", ".stubs")
+ props.Static_libs = modules
props.Sdk_version = proptools.StringPtr("module_current")
props.Visibility = []string{"//frameworks/base"}
props.Is_stubs_module = proptools.BoolPtr(true)
ctx.CreateModule(java.LibraryFactory, &props)
}
-func createMergedPublicExportableStubs(ctx android.LoadHookContext, modules []string) {
+func createMergedPublicExportableStubs(ctx android.LoadHookContext, modules proptools.Configurable[[]string]) {
+ modules = modules.Clone()
+ transformConfigurableArray(modules, "", ".stubs.exportable")
props := libraryProps{}
props.Name = proptools.StringPtr("all-modules-public-stubs-exportable")
- props.Static_libs = transformArray(modules, "", ".stubs.exportable")
+ props.Static_libs = modules
props.Sdk_version = proptools.StringPtr("module_current")
props.Visibility = []string{"//frameworks/base"}
props.Is_stubs_module = proptools.BoolPtr(true)
ctx.CreateModule(java.LibraryFactory, &props)
}
-func createMergedSystemStubs(ctx android.LoadHookContext, modules []string) {
+func createMergedSystemStubs(ctx android.LoadHookContext, modules proptools.Configurable[[]string]) {
// First create the all-updatable-modules-system-stubs
{
- updatable_modules := removeAll(modules, non_updatable_modules)
+ updatable_modules := modules.Clone()
+ removeAll(updatable_modules, non_updatable_modules)
+ transformConfigurableArray(updatable_modules, "", ".stubs.system")
props := libraryProps{}
props.Name = proptools.StringPtr("all-updatable-modules-system-stubs")
- props.Static_libs = transformArray(updatable_modules, "", ".stubs.system")
+ props.Static_libs = updatable_modules
props.Sdk_version = proptools.StringPtr("module_current")
props.Visibility = []string{"//frameworks/base"}
props.Is_stubs_module = proptools.BoolPtr(true)
@@ -275,10 +276,11 @@
// Now merge all-updatable-modules-system-stubs and stubs from non-updatable modules
// into all-modules-system-stubs.
{
+ static_libs := transformArray(non_updatable_modules, "", ".stubs.system")
+ static_libs = append(static_libs, "all-updatable-modules-system-stubs")
props := libraryProps{}
props.Name = proptools.StringPtr("all-modules-system-stubs")
- props.Static_libs = transformArray(non_updatable_modules, "", ".stubs.system")
- props.Static_libs = append(props.Static_libs, "all-updatable-modules-system-stubs")
+ props.Static_libs = proptools.NewSimpleConfigurable(static_libs)
props.Sdk_version = proptools.StringPtr("module_current")
props.Visibility = []string{"//frameworks/base"}
props.Is_stubs_module = proptools.BoolPtr(true)
@@ -286,13 +288,15 @@
}
}
-func createMergedSystemExportableStubs(ctx android.LoadHookContext, modules []string) {
+func createMergedSystemExportableStubs(ctx android.LoadHookContext, modules proptools.Configurable[[]string]) {
// First create the all-updatable-modules-system-stubs
{
- updatable_modules := removeAll(modules, non_updatable_modules)
+ updatable_modules := modules.Clone()
+ removeAll(updatable_modules, non_updatable_modules)
+ transformConfigurableArray(updatable_modules, "", ".stubs.exportable.system")
props := libraryProps{}
props.Name = proptools.StringPtr("all-updatable-modules-system-stubs-exportable")
- props.Static_libs = transformArray(updatable_modules, "", ".stubs.exportable.system")
+ props.Static_libs = updatable_modules
props.Sdk_version = proptools.StringPtr("module_current")
props.Visibility = []string{"//frameworks/base"}
props.Is_stubs_module = proptools.BoolPtr(true)
@@ -301,10 +305,11 @@
// Now merge all-updatable-modules-system-stubs and stubs from non-updatable modules
// into all-modules-system-stubs.
{
+ static_libs := transformArray(non_updatable_modules, "", ".stubs.exportable.system")
+ static_libs = append(static_libs, "all-updatable-modules-system-stubs-exportable")
props := libraryProps{}
props.Name = proptools.StringPtr("all-modules-system-stubs-exportable")
- props.Static_libs = transformArray(non_updatable_modules, "", ".stubs.exportable.system")
- props.Static_libs = append(props.Static_libs, "all-updatable-modules-system-stubs-exportable")
+ props.Static_libs = proptools.NewSimpleConfigurable(static_libs)
props.Sdk_version = proptools.StringPtr("module_current")
props.Visibility = []string{"//frameworks/base"}
props.Is_stubs_module = proptools.BoolPtr(true)
@@ -315,7 +320,7 @@
func createMergedTestStubsForNonUpdatableModules(ctx android.LoadHookContext) {
props := libraryProps{}
props.Name = proptools.StringPtr("all-non-updatable-modules-test-stubs")
- props.Static_libs = transformArray(non_updatable_modules, "", ".stubs.test")
+ props.Static_libs = proptools.NewSimpleConfigurable(transformArray(non_updatable_modules, "", ".stubs.test"))
props.Sdk_version = proptools.StringPtr("module_current")
props.Visibility = []string{"//frameworks/base"}
props.Is_stubs_module = proptools.BoolPtr(true)
@@ -325,25 +330,27 @@
func createMergedTestExportableStubsForNonUpdatableModules(ctx android.LoadHookContext) {
props := libraryProps{}
props.Name = proptools.StringPtr("all-non-updatable-modules-test-stubs-exportable")
- props.Static_libs = transformArray(non_updatable_modules, "", ".stubs.exportable.test")
+ props.Static_libs = proptools.NewSimpleConfigurable(transformArray(non_updatable_modules, "", ".stubs.exportable.test"))
props.Sdk_version = proptools.StringPtr("module_current")
props.Visibility = []string{"//frameworks/base"}
props.Is_stubs_module = proptools.BoolPtr(true)
ctx.CreateModule(java.LibraryFactory, &props)
}
-func createMergedFrameworkImpl(ctx android.LoadHookContext, modules []string) {
+func createMergedFrameworkImpl(ctx android.LoadHookContext, modules proptools.Configurable[[]string]) {
+ modules = modules.Clone()
// This module is for the "framework-all" module, which should not include the core libraries.
- modules = removeAll(modules, core_libraries_modules)
+ removeAll(modules, core_libraries_modules)
// Remove the modules that belong to non-updatable APEXes since those are allowed to compile
// against unstable APIs.
- modules = removeAll(modules, non_updatable_modules)
+ removeAll(modules, non_updatable_modules)
// First create updatable-framework-module-impl, which contains all updatable modules.
// This module compiles against module_lib SDK.
{
+ transformConfigurableArray(modules, "", ".impl")
props := libraryProps{}
props.Name = proptools.StringPtr("updatable-framework-module-impl")
- props.Static_libs = transformArray(modules, "", ".impl")
+ props.Static_libs = modules
props.Sdk_version = proptools.StringPtr("module_current")
props.Visibility = []string{"//frameworks/base"}
ctx.CreateModule(java.LibraryFactory, &props)
@@ -352,65 +359,74 @@
// Now create all-framework-module-impl, which contains updatable-framework-module-impl
// and all non-updatable modules. This module compiles against hidden APIs.
{
+ static_libs := transformArray(non_updatable_modules, "", ".impl")
+ static_libs = append(static_libs, "updatable-framework-module-impl")
props := libraryProps{}
props.Name = proptools.StringPtr("all-framework-module-impl")
- props.Static_libs = transformArray(non_updatable_modules, "", ".impl")
- props.Static_libs = append(props.Static_libs, "updatable-framework-module-impl")
+ props.Static_libs = proptools.NewSimpleConfigurable(static_libs)
props.Sdk_version = proptools.StringPtr("core_platform")
props.Visibility = []string{"//frameworks/base"}
ctx.CreateModule(java.LibraryFactory, &props)
}
}
-func createMergedFrameworkModuleLibExportableStubs(ctx android.LoadHookContext, modules []string) {
+func createMergedFrameworkModuleLibExportableStubs(ctx android.LoadHookContext, modules proptools.Configurable[[]string]) {
+ modules = modules.Clone()
// 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)
+ removeAll(modules, core_libraries_modules)
+ removeAll(modules, non_updatable_modules)
+ transformConfigurableArray(modules, "", ".stubs.exportable.module_lib")
props := libraryProps{}
props.Name = proptools.StringPtr("framework-updatable-stubs-module_libs_api-exportable")
- props.Static_libs = transformArray(modules, "", ".stubs.exportable.module_lib")
+ props.Static_libs = modules
props.Sdk_version = proptools.StringPtr("module_current")
props.Visibility = []string{"//frameworks/base"}
props.Is_stubs_module = proptools.BoolPtr(true)
ctx.CreateModule(java.LibraryFactory, &props)
}
-func createMergedFrameworkModuleLibStubs(ctx android.LoadHookContext, modules []string) {
+func createMergedFrameworkModuleLibStubs(ctx android.LoadHookContext, modules proptools.Configurable[[]string]) {
+ modules = modules.Clone()
// 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)
+ removeAll(modules, core_libraries_modules)
+ removeAll(modules, non_updatable_modules)
+ transformConfigurableArray(modules, "", ".stubs.module_lib")
props := libraryProps{}
props.Name = proptools.StringPtr("framework-updatable-stubs-module_libs_api")
- props.Static_libs = transformArray(modules, "", ".stubs.module_lib")
+ props.Static_libs = modules
props.Sdk_version = proptools.StringPtr("module_current")
props.Visibility = []string{"//frameworks/base"}
props.Is_stubs_module = proptools.BoolPtr(true)
ctx.CreateModule(java.LibraryFactory, &props)
}
-func createMergedFrameworkSystemServerExportableStubs(ctx android.LoadHookContext, bootclasspath, system_server_classpath []string) {
+func createMergedFrameworkSystemServerExportableStubs(ctx android.LoadHookContext, bootclasspath, system_server_classpath proptools.Configurable[[]string]) {
// The user of this module compiles against the "core" SDK and against non-updatable bootclasspathModules,
// so remove to avoid dupes.
- bootclasspathModules := removeAll(bootclasspath, core_libraries_modules)
- bootclasspathModules = removeAll(bootclasspath, non_updatable_modules)
- modules := append(
- // Include all the module-lib APIs from the bootclasspath libraries.
- transformArray(bootclasspathModules, "", ".stubs.exportable.module_lib"),
- // Then add all the system-server APIs from the service-* libraries.
- transformArray(system_server_classpath, "", ".stubs.exportable.system_server")...,
- )
+ bootclasspathModules := bootclasspath.Clone()
+ removeAll(bootclasspathModules, core_libraries_modules)
+ removeAll(bootclasspathModules, non_updatable_modules)
+ transformConfigurableArray(bootclasspathModules, "", ".stubs.exportable.module_lib")
+
+ system_server_classpath = system_server_classpath.Clone()
+ transformConfigurableArray(system_server_classpath, "", ".stubs.exportable.system_server")
+
+ // Include all the module-lib APIs from the bootclasspath libraries.
+ // Then add all the system-server APIs from the service-* libraries.
+ bootclasspathModules.Append(system_server_classpath)
+
props := libraryProps{}
props.Name = proptools.StringPtr("framework-updatable-stubs-system_server_api-exportable")
- props.Static_libs = modules
+ props.Static_libs = bootclasspathModules
props.Sdk_version = proptools.StringPtr("system_server_current")
props.Visibility = []string{"//frameworks/base"}
props.Is_stubs_module = proptools.BoolPtr(true)
ctx.CreateModule(java.LibraryFactory, &props)
}
-func createPublicStubsSourceFilegroup(ctx android.LoadHookContext, modules []string) {
+func createPublicStubsSourceFilegroup(ctx android.LoadHookContext, modules proptools.Configurable[[]string]) {
props := fgProps{}
props.Name = proptools.StringPtr("all-modules-public-stubs-source")
props.Srcs = createSrcs(modules, "{.public.stubs.source}")
@@ -418,7 +434,14 @@
ctx.CreateModule(android.FileGroupFactory, &props)
}
-func createMergedTxts(ctx android.LoadHookContext, bootclasspath, system_server_classpath []string, baseTxtModulePrefix, stubsTypeSuffix string, doDist bool) {
+func createMergedTxts(
+ ctx android.LoadHookContext,
+ bootclasspath proptools.Configurable[[]string],
+ system_server_classpath proptools.Configurable[[]string],
+ baseTxtModulePrefix string,
+ stubsTypeSuffix string,
+ doDist bool,
+) {
var textFiles []MergedTxtDefinition
tagSuffix := []string{".api.txt}", ".removed-api.txt}"}
@@ -463,11 +486,10 @@
}
func (a *CombinedApis) createInternalModules(ctx android.LoadHookContext) {
- bootclasspath := a.bootclasspath(ctx)
- system_server_classpath := a.systemServerClasspath(ctx)
+ bootclasspath := a.properties.Bootclasspath.Clone()
+ system_server_classpath := a.properties.System_server_classpath.Clone()
if ctx.Config().VendorConfig("ANDROID").Bool("include_nonpublic_framework_api") {
- bootclasspath = append(bootclasspath, a.properties.Conditional_bootclasspath...)
- sort.Strings(bootclasspath)
+ bootclasspath.AppendSimpleValue(a.properties.Conditional_bootclasspath)
}
createMergedTxts(ctx, bootclasspath, system_server_classpath, "non-updatable-", "-", false)
createMergedTxts(ctx, bootclasspath, system_server_classpath, "non-updatable-exportable-", "-exportable-", true)
@@ -500,8 +522,10 @@
// Various utility methods below.
// Creates an array of ":<m><tag>" for each m in <modules>.
-func createSrcs(modules []string, tag string) []string {
- return transformArray(modules, ":", tag)
+func createSrcs(modules proptools.Configurable[[]string], tag string) proptools.Configurable[[]string] {
+ result := modules.Clone()
+ transformConfigurableArray(result, ":", tag)
+ return result
}
// Creates an array of "<prefix><m><suffix>", for each m in <modules>.
@@ -513,11 +537,23 @@
return a
}
-func removeAll(s []string, vs []string) []string {
- for _, v := range vs {
- s = remove(s, v)
- }
- return s
+// Creates an array of "<prefix><m><suffix>", for each m in <modules>.
+func transformConfigurableArray(modules proptools.Configurable[[]string], prefix, suffix string) {
+ modules.AddPostProcessor(func(s []string) []string {
+ return transformArray(s, prefix, suffix)
+ })
+}
+
+func removeAll(s proptools.Configurable[[]string], vs []string) {
+ s.AddPostProcessor(func(s []string) []string {
+ a := make([]string, 0, len(s))
+ for _, module := range s {
+ if !slices.Contains(vs, module) {
+ a = append(a, module)
+ }
+ }
+ return a
+ })
}
func remove(s []string, v string) []string {
diff --git a/cmds/uinput/examples/test-touchpad.evemu b/cmds/uinput/examples/test-touchpad.evemu
new file mode 100644
index 0000000..34ee572
--- /dev/null
+++ b/cmds/uinput/examples/test-touchpad.evemu
@@ -0,0 +1,44 @@
+# EVEMU 1.2
+# This is an evemu "recording" of an Apple Magic Trackpad (1st generation), but
+# that doesn't actually make any movements. It just runs for a very long time,
+# to make Android think a touchpad is connected. This is useful for testing
+# things like the settings in System > Touchpad, which only appear when one is
+# connected.
+#
+# It can be played by piping it to the uinput command over ADB:
+# $ adb shell uinput - < test-touchpad.evemu
+N: Fake touchpad
+I: 0005 05ac 030e 0160
+P: 05 00 00 00 00 00 00 00
+B: 00 0b 00 00 00 00 00 00 00
+B: 01 00 00 00 00 00 00 00 00
+B: 01 00 00 00 00 00 00 00 00
+B: 01 00 00 00 00 00 00 00 00
+B: 01 00 00 00 00 00 00 00 00
+B: 01 00 00 01 00 00 00 00 00
+B: 01 20 e5 00 00 00 00 00 00
+B: 01 00 00 00 00 00 00 00 00
+B: 01 00 00 00 00 00 00 00 00
+B: 01 00 00 00 00 00 00 00 00
+B: 01 00 00 00 00 00 00 00 00
+B: 01 00 00 00 00 00 00 00 00
+B: 01 00 00 00 00 00 00 00 00
+B: 02 00 00 00 00 00 00 00 00
+B: 03 03 00 00 00 00 80 73 02
+B: 04 10 00 00 00 00 00 00 00
+B: 05 00 00 00 00 00 00 00 00
+B: 11 00 00 00 00 00 00 00 00
+B: 12 00 00 00 00 00 00 00 00
+A: 00 -2909 3167 4 0 46
+A: 01 -2456 2565 4 0 45
+A: 2f 0 15 0 0 0
+A: 30 0 1020 4 0 0
+A: 31 0 1020 4 0 0
+A: 34 -31 32 1 0 0
+A: 35 -2909 3167 4 0 46
+A: 36 -2456 2565 4 0 45
+A: 39 0 65535 0 0 0
+E: 0.000001 0004 0005 1234
+E: 0.000001 0000 0000 0000
+E: 1000000000.000000 0004 0005 1235
+E: 1000000000.000000 0000 0000 0000
diff --git a/core/api/current.txt b/core/api/current.txt
index 5685221..5e8febe 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -145,12 +145,12 @@
field public static final String MANAGE_DEVICE_POLICY_AUDIO_OUTPUT = "android.permission.MANAGE_DEVICE_POLICY_AUDIO_OUTPUT";
field public static final String MANAGE_DEVICE_POLICY_AUTOFILL = "android.permission.MANAGE_DEVICE_POLICY_AUTOFILL";
field public static final String MANAGE_DEVICE_POLICY_BACKUP_SERVICE = "android.permission.MANAGE_DEVICE_POLICY_BACKUP_SERVICE";
- field @FlaggedApi("android.app.admin.flags.dedicated_device_control_api_enabled") public static final String MANAGE_DEVICE_POLICY_BLOCK_UNINSTALL = "android.permission.MANAGE_DEVICE_POLICY_BLOCK_UNINSTALL";
+ field public static final String MANAGE_DEVICE_POLICY_BLOCK_UNINSTALL = "android.permission.MANAGE_DEVICE_POLICY_BLOCK_UNINSTALL";
field public static final String MANAGE_DEVICE_POLICY_BLUETOOTH = "android.permission.MANAGE_DEVICE_POLICY_BLUETOOTH";
field public static final String MANAGE_DEVICE_POLICY_BUGREPORT = "android.permission.MANAGE_DEVICE_POLICY_BUGREPORT";
field public static final String MANAGE_DEVICE_POLICY_CALLS = "android.permission.MANAGE_DEVICE_POLICY_CALLS";
field public static final String MANAGE_DEVICE_POLICY_CAMERA = "android.permission.MANAGE_DEVICE_POLICY_CAMERA";
- field @FlaggedApi("android.app.admin.flags.dedicated_device_control_api_enabled") public static final String MANAGE_DEVICE_POLICY_CAMERA_TOGGLE = "android.permission.MANAGE_DEVICE_POLICY_CAMERA_TOGGLE";
+ field public static final String MANAGE_DEVICE_POLICY_CAMERA_TOGGLE = "android.permission.MANAGE_DEVICE_POLICY_CAMERA_TOGGLE";
field public static final String MANAGE_DEVICE_POLICY_CERTIFICATES = "android.permission.MANAGE_DEVICE_POLICY_CERTIFICATES";
field public static final String MANAGE_DEVICE_POLICY_COMMON_CRITERIA_MODE = "android.permission.MANAGE_DEVICE_POLICY_COMMON_CRITERIA_MODE";
field @FlaggedApi("android.view.contentprotection.flags.manage_device_policy_enabled") public static final String MANAGE_DEVICE_POLICY_CONTENT_PROTECTION = "android.permission.MANAGE_DEVICE_POLICY_CONTENT_PROTECTION";
@@ -172,7 +172,7 @@
field public static final String MANAGE_DEVICE_POLICY_MANAGED_SUBSCRIPTIONS = "android.permission.MANAGE_DEVICE_POLICY_MANAGED_SUBSCRIPTIONS";
field public static final String MANAGE_DEVICE_POLICY_METERED_DATA = "android.permission.MANAGE_DEVICE_POLICY_METERED_DATA";
field public static final String MANAGE_DEVICE_POLICY_MICROPHONE = "android.permission.MANAGE_DEVICE_POLICY_MICROPHONE";
- field @FlaggedApi("android.app.admin.flags.dedicated_device_control_api_enabled") public static final String MANAGE_DEVICE_POLICY_MICROPHONE_TOGGLE = "android.permission.MANAGE_DEVICE_POLICY_MICROPHONE_TOGGLE";
+ field public static final String MANAGE_DEVICE_POLICY_MICROPHONE_TOGGLE = "android.permission.MANAGE_DEVICE_POLICY_MICROPHONE_TOGGLE";
field public static final String MANAGE_DEVICE_POLICY_MOBILE_NETWORK = "android.permission.MANAGE_DEVICE_POLICY_MOBILE_NETWORK";
field public static final String MANAGE_DEVICE_POLICY_MODIFY_USERS = "android.permission.MANAGE_DEVICE_POLICY_MODIFY_USERS";
field public static final String MANAGE_DEVICE_POLICY_MTE = "android.permission.MANAGE_DEVICE_POLICY_MTE";
@@ -8121,7 +8121,7 @@
method public boolean isLogoutEnabled();
method public boolean isManagedProfile(@NonNull android.content.ComponentName);
method public boolean isMasterVolumeMuted(@NonNull android.content.ComponentName);
- method @FlaggedApi("android.app.admin.flags.is_mte_policy_enforced") public static boolean isMtePolicyEnforced();
+ method public static boolean isMtePolicyEnforced();
method public boolean isNetworkLoggingEnabled(@Nullable android.content.ComponentName);
method public boolean isOrganizationOwnedDeviceWithManagedProfile();
method public boolean isOverrideApnEnabled(@NonNull android.content.ComponentName);
@@ -8597,7 +8597,7 @@
field public static final int TAG_ADB_SHELL_CMD = 210002; // 0x33452
field public static final int TAG_ADB_SHELL_INTERACTIVE = 210001; // 0x33451
field public static final int TAG_APP_PROCESS_START = 210005; // 0x33455
- field @FlaggedApi("android.app.admin.flags.backup_service_security_log_event_enabled") public static final int TAG_BACKUP_SERVICE_TOGGLED = 210044; // 0x3347c
+ field public static final int TAG_BACKUP_SERVICE_TOGGLED = 210044; // 0x3347c
field public static final int TAG_BLUETOOTH_CONNECTION = 210039; // 0x33477
field public static final int TAG_BLUETOOTH_DISCONNECTION = 210040; // 0x33478
field public static final int TAG_CAMERA_POLICY_SET = 210034; // 0x33472
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index ba16037..60dc52b 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -1322,7 +1322,7 @@
method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public android.os.UserHandle getDeviceOwnerUser();
method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public android.app.admin.DevicePolicyState getDevicePolicyState();
method @Nullable @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public String getFinancedDeviceKioskRoleHolder();
- method @FlaggedApi("android.app.admin.flags.device_policy_size_tracking_enabled") @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public int getMaxPolicyStorageLimit();
+ method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public int getMaxPolicyStorageLimit();
method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_ADMIN_POLICY}) public java.util.List<java.lang.String> getPermittedAccessibilityServices(int);
method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_ADMIN_POLICY}) public java.util.List<java.lang.String> getPermittedInputMethodsForCurrentUser();
method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public java.util.List<android.os.UserHandle> getPolicyManagedProfiles(@NonNull android.os.UserHandle);
@@ -1349,7 +1349,7 @@
method @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_POLICY_AUDIT_LOGGING) public void setAuditLogEventCallback(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.util.List<android.app.admin.SecurityLog.SecurityEvent>>);
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setDeviceProvisioningConfigApplied();
method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void setDpcDownloaded(boolean);
- method @FlaggedApi("android.app.admin.flags.device_policy_size_tracking_enabled") @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void setMaxPolicyStorageLimit(int);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void setMaxPolicyStorageLimit(int);
method @Deprecated @RequiresPermission(value=android.Manifest.permission.GRANT_PROFILE_OWNER_DEVICE_IDS_ACCESS, conditional=true) public void setProfileOwnerCanAccessDeviceIds(@NonNull android.content.ComponentName);
method public void setSecondaryLockscreenEnabled(@NonNull android.content.ComponentName, boolean);
method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void setUserProvisioningState(int, @NonNull android.os.UserHandle);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 42f7615..4fc7076 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -2003,6 +2003,7 @@
method public void setRampingRingerEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED) public void setRs2Value(float);
method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public void setTestDeviceConnectionState(@NonNull android.media.AudioDeviceAttributes, boolean);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED) public void setVolumeControllerLongPressTimeoutEnabled(boolean);
method @FlaggedApi("android.media.audio.focus_exclusive_with_recording") @RequiresPermission(android.Manifest.permission.QUERY_AUDIO_STATE) public boolean shouldNotificationSoundPlay(@NonNull android.media.AudioAttributes);
}
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 8fd3326..f27dc32 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -79,7 +79,6 @@
import android.provider.DeviceConfig;
import android.util.ArrayMap;
import android.util.ArraySet;
-import android.util.Log;
import android.util.LongSparseArray;
import android.util.LongSparseLongArray;
import android.util.Pools;
@@ -3156,12 +3155,6 @@
/** @hide */
public static final String KEY_HISTORICAL_OPS = "historical_ops";
- /** System properties for debug logging of noteOp call sites */
- private static final String DEBUG_LOGGING_ENABLE_PROP = "appops.logging_enabled";
- private static final String DEBUG_LOGGING_PACKAGES_PROP = "appops.logging_packages";
- private static final String DEBUG_LOGGING_OPS_PROP = "appops.logging_ops";
- private static final String DEBUG_LOGGING_TAG = "AppOpsManager";
-
/**
* Retrieve the op switch that controls the given operation.
* @hide
@@ -8066,14 +8059,6 @@
@RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES)
public void setUidMode(int code, int uid, @Mode int mode) {
try {
- // TODO(b/302609140): Remove extra logging after this issue is diagnosed.
- if (code == OP_BLUETOOTH_CONNECT) {
- Log.i(DEBUG_LOGGING_TAG,
- "setUidMode called for OP_BLUETOOTH_CONNECT with mode: " + mode
- + " for uid: " + uid + " calling uid: " + Binder.getCallingUid()
- + " trace: "
- + Arrays.toString(Thread.currentThread().getStackTrace()));
- }
mService.setUidMode(code, uid, mode);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -8094,15 +8079,6 @@
@RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES)
public void setUidMode(@NonNull String appOp, int uid, @Mode int mode) {
try {
- // TODO(b/302609140): Remove extra logging after this issue is diagnosed.
- if (appOp.equals(OPSTR_BLUETOOTH_CONNECT)) {
- Log.i(DEBUG_LOGGING_TAG,
- "setUidMode called for OPSTR_BLUETOOTH_CONNECT with mode: " + mode
- + " for uid: " + uid + " calling uid: " + Binder.getCallingUid()
- + " trace: "
- + Arrays.toString(Thread.currentThread().getStackTrace()));
- }
-
mService.setUidMode(AppOpsManager.strOpToOp(appOp), uid, mode);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -8143,14 +8119,6 @@
@RequiresPermission(android.Manifest.permission.MANAGE_APP_OPS_MODES)
public void setMode(int code, int uid, String packageName, @Mode int mode) {
try {
- // TODO(b/302609140): Remove extra logging after this issue is diagnosed.
- if (code == OP_BLUETOOTH_CONNECT) {
- Log.i(DEBUG_LOGGING_TAG,
- "setMode called for OPSTR_BLUETOOTH_CONNECT with mode: " + mode
- + " for uid: " + uid + " calling uid: " + Binder.getCallingUid()
- + " trace: "
- + Arrays.toString(Thread.currentThread().getStackTrace()));
- }
mService.setMode(code, uid, packageName, mode);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -8173,14 +8141,6 @@
public void setMode(@NonNull String op, int uid, @Nullable String packageName,
@Mode int mode) {
try {
- // TODO(b/302609140): Remove extra logging after this issue is diagnosed.
- if (op.equals(OPSTR_BLUETOOTH_CONNECT)) {
- Log.i(DEBUG_LOGGING_TAG,
- "setMode called for OPSTR_BLUETOOTH_CONNECT with mode: " + mode
- + " for uid: " + uid + " calling uid: " + Binder.getCallingUid()
- + " trace: "
- + Arrays.toString(Thread.currentThread().getStackTrace()));
- }
mService.setMode(strOpToOp(op), uid, packageName, mode);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
diff --git a/core/java/android/app/activity_manager.aconfig b/core/java/android/app/activity_manager.aconfig
index 4c97dbb..4d61f41 100644
--- a/core/java/android/app/activity_manager.aconfig
+++ b/core/java/android/app/activity_manager.aconfig
@@ -106,6 +106,17 @@
flag {
namespace: "backstage_power"
+ name: "use_app_info_not_launched"
+ description: "Use the notLaunched state from ApplicationInfo instead of current value"
+ is_fixed_read_only: true
+ bug: "362516211"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
+ namespace: "backstage_power"
name: "cache_get_current_user_id"
description: "Add caching for getCurrentUserId"
is_fixed_read_only: true
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 0f54cb7..daa15f0 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -55,10 +55,8 @@
import static android.Manifest.permission.SET_TIME_ZONE;
import static android.app.admin.DeviceAdminInfo.HEADLESS_DEVICE_OWNER_MODE_UNSUPPORTED;
import static android.app.admin.flags.Flags.FLAG_DEVICE_THEFT_API_ENABLED;
-import static android.app.admin.flags.Flags.FLAG_DEVICE_POLICY_SIZE_TRACKING_ENABLED;
import static android.app.admin.flags.Flags.onboardingBugreportV2Enabled;
import static android.app.admin.flags.Flags.onboardingConsentlessBugreports;
-import static android.app.admin.flags.Flags.FLAG_IS_MTE_POLICY_ENFORCED;
import static android.content.Intent.LOCAL_FLAG_FROM_SYSTEM;
import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_1;
import static android.os.Build.VERSION_CODES.UPSIDE_DOWN_CAKE;
@@ -4232,7 +4230,6 @@
*
* @return whether MTE is currently enabled on the device.
*/
- @FlaggedApi(FLAG_IS_MTE_POLICY_ENFORCED)
public static boolean isMtePolicyEnforced() {
return Zygote.nativeSupportsMemoryTagging();
}
@@ -8664,6 +8661,7 @@
* {@link DeviceAdminInfo#USES_POLICY_DISABLE_CAMERA}.
*/
@RequiresPermission(value = MANAGE_DEVICE_POLICY_CAMERA, conditional = true)
+ @SupportsCoexistence
public void setCameraDisabled(@Nullable ComponentName admin, boolean disabled) {
if (mService != null) {
try {
@@ -10249,6 +10247,7 @@
* permission {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_LOCK_TASK}.
*/
@RequiresPermission(value = MANAGE_DEVICE_POLICY_LOCK_TASK, conditional = true)
+ @SupportsCoexistence
public void clearPackagePersistentPreferredActivities(@Nullable ComponentName admin,
String packageName) {
throwIfParentInstance("clearPackagePersistentPreferredActivities");
@@ -11940,6 +11939,7 @@
* @throws SecurityException if {@code admin} is not a device or profile owner and if the caller
* has not been granted the permission to set the given user restriction.
*/
+ @SupportsCoexistence
public void addUserRestriction(@NonNull ComponentName admin,
@UserManager.UserRestrictionKey String key) {
if (mService != null) {
@@ -12021,6 +12021,7 @@
* @throws IllegalStateException if caller is not targeting Android
* {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE} or above.
*/
+ @SupportsCoexistence
public void addUserRestrictionGlobally(@NonNull @UserManager.UserRestrictionKey String key) {
throwIfParentInstance("addUserRestrictionGlobally");
if (mService != null) {
@@ -12076,6 +12077,7 @@
* @throws SecurityException if {@code admin} is not a device or profile owner and if the
* caller has not been granted the permission to set the given user restriction.
*/
+ @SupportsCoexistence
public void clearUserRestriction(@NonNull ComponentName admin,
@UserManager.UserRestrictionKey String key) {
if (mService != null) {
@@ -12312,6 +12314,7 @@
* @see #DELEGATION_PACKAGE_ACCESS
*/
@RequiresPermission(value = MANAGE_DEVICE_POLICY_PACKAGE_STATE, conditional = true)
+ @SupportsCoexistence
public boolean setApplicationHidden(@Nullable ComponentName admin, String packageName,
boolean hidden) {
if (mService != null) {
@@ -12492,6 +12495,7 @@
* @throws SecurityException if {@code admin} is not a device or profile owner.
*/
@RequiresPermission(value = MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT, conditional = true)
+ @SupportsCoexistence
public void setAccountManagementDisabled(@Nullable ComponentName admin, String accountType,
boolean disabled) {
if (mService != null) {
@@ -12575,10 +12579,24 @@
**/
@SystemApi
public void setSecondaryLockscreenEnabled(@NonNull ComponentName admin, boolean enabled) {
+ setSecondaryLockscreenEnabled(admin, enabled, null);
+ }
+
+ /**
+ * Called by the system supervision app to set whether a secondary lockscreen needs to be shown.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with. Null if the
+ * caller is not a device admin.
+ * @param enabled Whether or not the lockscreen needs to be shown.
+ * @param options A {@link PersistableBundle} to supply options to the lock screen.
+ * @hide
+ */
+ public void setSecondaryLockscreenEnabled(@Nullable ComponentName admin, boolean enabled,
+ @Nullable PersistableBundle options) {
throwIfParentInstance("setSecondaryLockscreenEnabled");
if (mService != null) {
try {
- mService.setSecondaryLockscreenEnabled(admin, enabled);
+ mService.setSecondaryLockscreenEnabled(admin, enabled, options);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -14276,6 +14294,7 @@
* @see #retrieveSecurityLogs
*/
@RequiresPermission(value = MANAGE_DEVICE_POLICY_SECURITY_LOGGING, conditional = true)
+ @SupportsCoexistence
public void setSecurityLoggingEnabled(@Nullable ComponentName admin, boolean enabled) {
throwIfParentInstance("setSecurityLoggingEnabled");
try {
@@ -17181,6 +17200,7 @@
* if USB data signaling fails to be enabled/disabled.
*/
@RequiresPermission(value = MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING, conditional = true)
+ @SupportsCoexistence
public void setUsbDataSignalingEnabled(boolean enabled) {
throwIfParentInstance("setUsbDataSignalingEnabled");
if (mService != null) {
@@ -17746,7 +17766,6 @@
*/
@SystemApi
@RequiresPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)
- @FlaggedApi(FLAG_DEVICE_POLICY_SIZE_TRACKING_ENABLED)
public void setMaxPolicyStorageLimit(int storageLimit) {
if (mService != null) {
try {
@@ -17766,7 +17785,6 @@
*/
@SystemApi
@RequiresPermission(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)
- @FlaggedApi(FLAG_DEVICE_POLICY_SIZE_TRACKING_ENABLED)
public int getMaxPolicyStorageLimit() {
if (mService != null) {
try {
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index d4e5c99..a4e2b8f 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -303,7 +303,7 @@
String[] getAccountTypesWithManagementDisabled(String callerPackageName);
String[] getAccountTypesWithManagementDisabledAsUser(int userId, String callerPackageName, in boolean parent);
- void setSecondaryLockscreenEnabled(in ComponentName who, boolean enabled);
+ void setSecondaryLockscreenEnabled(in ComponentName who, boolean enabled, in PersistableBundle options);
boolean isSecondaryLockscreenEnabled(in UserHandle userHandle);
void setPreferentialNetworkServiceConfigs(
diff --git a/core/java/android/app/admin/SecurityLog.java b/core/java/android/app/admin/SecurityLog.java
index 477f2e0..beb93fd 100644
--- a/core/java/android/app/admin/SecurityLog.java
+++ b/core/java/android/app/admin/SecurityLog.java
@@ -16,10 +16,7 @@
package android.app.admin;
-import static android.app.admin.flags.Flags.FLAG_BACKUP_SERVICE_SECURITY_LOG_EVENT_ENABLED;
-
import android.Manifest;
-import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -611,7 +608,6 @@
* <li> [2] backup service state ({@code Integer}, 1 for enabled, 0 for disabled)
* @see DevicePolicyManager#setBackupServiceEnabled(ComponentName, boolean)
*/
- @FlaggedApi(FLAG_BACKUP_SERVICE_SECURITY_LOG_EVENT_ENABLED)
public static final int TAG_BACKUP_SERVICE_TOGGLED =
SecurityLogTags.SECURITY_BACKUP_SERVICE_TOGGLED;
/**
diff --git a/core/java/android/app/admin/flags/flags.aconfig b/core/java/android/app/admin/flags/flags.aconfig
index 9ed5aa6..8e08a95 100644
--- a/core/java/android/app/admin/flags/flags.aconfig
+++ b/core/java/android/app/admin/flags/flags.aconfig
@@ -13,6 +13,7 @@
bug: "289520697"
}
+# Fully rolled out and must not be used.
flag {
name: "device_policy_size_tracking_enabled"
is_exported: true
@@ -22,13 +23,6 @@
}
flag {
- name: "device_policy_size_tracking_internal_enabled"
- namespace: "enterprise"
- description: "Add feature to track the total policy size and have a max threshold - internal changes"
- bug: "281543351"
-}
-
-flag {
name: "onboarding_bugreport_v2_enabled"
is_exported: true
namespace: "enterprise"
@@ -44,13 +38,7 @@
is_fixed_read_only: true
}
-flag {
- name: "dedicated_device_control_enabled"
- namespace: "enterprise"
- description: "Allow the device management role holder to control which platform features are available on dedicated devices."
- bug: "281964214"
-}
-
+# Fully rolled out and must not be used.
flag {
name: "dedicated_device_control_api_enabled"
is_exported: true
@@ -170,6 +158,7 @@
bug: "293441361"
}
+# Fully rolled out and must not be used.
flag {
name: "assist_content_user_restriction_enabled"
is_exported: true
@@ -188,6 +177,7 @@
}
}
+# Fully rolled out and must not be used.
flag {
name: "backup_service_security_log_event_enabled"
is_exported: true
@@ -196,6 +186,7 @@
bug: "304999634"
}
+# Fully rolled out and must not be used.
flag {
name: "esim_management_enabled"
is_exported: true
@@ -213,6 +204,7 @@
bug: "289515470"
}
+# Fully rolled out and must not be used.
flag {
name: "is_mte_policy_enforced"
is_exported: true
diff --git a/core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java b/core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java
index c4e8b41..c4bfae9 100644
--- a/core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java
+++ b/core/java/android/app/appfunctions/AppFunctionRuntimeMetadata.java
@@ -72,14 +72,32 @@
* we need to have per-package app function schemas.
*
* <p>This schema should be set visible to callers from the package owner itself and for callers
- * with {@link android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED} or {@link
- * android.permission.EXECUTE_APP_FUNCTIONS} permissions.
+ * with {@link android.Manifest.permission#EXECUTE_APP_FUNCTIONS} or {@link
+ * android.Manifest.permission#EXECUTE_APP_FUNCTIONS_TRUSTED} permissions.
*
* @param packageName The package name to create a schema for.
*/
@NonNull
public static AppSearchSchema createAppFunctionRuntimeSchema(@NonNull String packageName) {
- return new AppSearchSchema.Builder(getRuntimeSchemaNameForPackage(packageName))
+ return getAppFunctionRuntimeSchemaBuilder(getRuntimeSchemaNameForPackage(packageName))
+ .addParentType(RUNTIME_SCHEMA_TYPE)
+ .build();
+ }
+
+ /**
+ * Creates a parent schema for all app function runtime schemas.
+ *
+ * <p>This schema should be set visible to the owner itself and for callers with {@link
+ * android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED} or {@link
+ * android.permission.EXECUTE_APP_FUNCTIONS} permissions.
+ */
+ public static AppSearchSchema createParentAppFunctionRuntimeSchema() {
+ return getAppFunctionRuntimeSchemaBuilder(RUNTIME_SCHEMA_TYPE).build();
+ }
+
+ private static AppSearchSchema.Builder getAppFunctionRuntimeSchemaBuilder(
+ @NonNull String schemaType) {
+ return new AppSearchSchema.Builder(schemaType)
.addProperty(
new AppSearchSchema.StringPropertyConfig.Builder(PROPERTY_FUNCTION_ID)
.setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
@@ -111,9 +129,7 @@
.setJoinableValueType(
AppSearchSchema.StringPropertyConfig
.JOINABLE_VALUE_TYPE_QUALIFIED_ID)
- .build())
- .addParentType(RUNTIME_SCHEMA_TYPE)
- .build();
+ .build());
}
/** Returns the function id. This might look like "com.example.message#send_message". */
diff --git a/core/java/android/app/jank/OWNERS b/core/java/android/app/jank/OWNERS
new file mode 100644
index 0000000..806de57
--- /dev/null
+++ b/core/java/android/app/jank/OWNERS
@@ -0,0 +1,4 @@
+steventerrell@google.com
+carmenjackson@google.com
+jjaggi@google.com
+pmuetschard@google.com
\ No newline at end of file
diff --git a/core/java/android/companion/virtual/flags/flags.aconfig b/core/java/android/companion/virtual/flags/flags.aconfig
index 297fe8a..748260b 100644
--- a/core/java/android/companion/virtual/flags/flags.aconfig
+++ b/core/java/android/companion/virtual/flags/flags.aconfig
@@ -98,11 +98,11 @@
}
flag {
- name: "camera_multiple_input_streams"
- is_exported: true
- namespace: "virtual_devices"
- description: "Expose multiple surface for the virtual camera owner for different stream resolution"
- bug: "341083465"
+ name: "camera_multiple_input_streams"
+ is_exported: true
+ namespace: "virtual_devices"
+ description: "Expose multiple surface for the virtual camera owner for different stream resolution"
+ bug: "341083465"
}
flag {
@@ -113,8 +113,16 @@
}
flag {
- name: "status_bar_and_insets"
- namespace: "virtual_devices"
- description: "Allow for status bar and insets on virtual devices"
- bug: "350007866"
+ namespace: "virtual_devices"
+ name: "display_power_manager_apis"
+ description: "Make relevant PowerManager APIs display aware by default"
+ bug: "365042486"
+ is_fixed_read_only: true
+}
+
+flag {
+ name: "status_bar_and_insets"
+ namespace: "virtual_devices"
+ description: "Allow for status bar and insets on virtual devices"
+ bug: "350007866"
}
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 495ae60..34bea1a 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -849,6 +849,12 @@
*/
public static final int PRIVATE_FLAG_EXT_CPU_OVERRIDE = 1 << 5;
+ /**
+ * Whether the app has been previously not launched
+ * @hide
+ */
+ public static final int PRIVATE_FLAG_EXT_NOT_LAUNCHED = 1 << 6;
+
/** @hide */
@IntDef(flag = true, prefix = { "PRIVATE_FLAG_EXT_" }, value = {
PRIVATE_FLAG_EXT_PROFILEABLE,
@@ -857,6 +863,7 @@
PRIVATE_FLAG_EXT_ENABLE_ON_BACK_INVOKED_CALLBACK,
PRIVATE_FLAG_EXT_ALLOWLISTED_FOR_HIDDEN_APIS,
PRIVATE_FLAG_EXT_CPU_OVERRIDE,
+ PRIVATE_FLAG_EXT_NOT_LAUNCHED,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ApplicationInfoPrivateFlagsExt {}
@@ -2664,6 +2671,22 @@
}
/**
+ * Returns whether the app in the STOPPED state.
+ * @hide
+ */
+ public boolean isStopped() {
+ return (flags & ApplicationInfo.FLAG_STOPPED) != 0;
+ }
+
+ /**
+ * Returns whether the app was never launched (any process started) before.
+ * @hide
+ */
+ public boolean isNotLaunched() {
+ return (privateFlagsExt & ApplicationInfo.PRIVATE_FLAG_EXT_NOT_LAUNCHED) != 0;
+ }
+
+ /**
* Checks if a changeId is enabled for the current user
* @param changeId The changeId to verify
* @return True of the changeId is enabled
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index 28534ad..9eec7a4 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -426,4 +426,13 @@
metadata {
purpose: PURPOSE_BUGFIX
}
-}
\ No newline at end of file
+}
+
+
+flag {
+ name: "caching_development_improvements"
+ namespace: "multiuser"
+ description: "System API to simplify caching implamentations"
+ bug: "364947162"
+ is_fixed_read_only: true
+}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 98904fe..0a05f70 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5150,13 +5150,19 @@
public static final String SCREEN_OFF_TIMEOUT = "screen_off_timeout";
/**
- * The screen backlight brightness between 0 and 255.
+ * The screen backlight brightness between 1 (minimum) and 255 (maximum).
+ *
+ * Use {@link android.view.WindowManager.LayoutParams#screenBrightness} to set the screen
+ * brightness instead.
*/
@Readable
public static final String SCREEN_BRIGHTNESS = "screen_brightness";
/**
- * Control whether to enable automatic brightness mode.
+ * Controls whether to enable automatic brightness mode. Value can be set to
+ * {@link #SCREEN_BRIGHTNESS_MODE_MANUAL} or {@link #SCREEN_BRIGHTNESS_MODE_AUTOMATIC}.
+ * If {@link #SCREEN_BRIGHTNESS_MODE_AUTOMATIC} is set, the system may change
+ * {@link #SCREEN_BRIGHTNESS} automatically.
*/
@Readable
public static final String SCREEN_BRIGHTNESS_MODE = "screen_brightness_mode";
diff --git a/core/java/android/text/ClientFlags.java b/core/java/android/text/ClientFlags.java
index 5d84d17..bbe9cdb 100644
--- a/core/java/android/text/ClientFlags.java
+++ b/core/java/android/text/ClientFlags.java
@@ -35,20 +35,6 @@
}
/**
- * @see Flags#phraseStrictFallback()
- */
- public static boolean phraseStrictFallback() {
- return TextFlags.isFeatureEnabled(Flags.FLAG_PHRASE_STRICT_FALLBACK);
- }
-
- /**
- * @see Flags#useBoundsForWidth()
- */
- public static boolean useBoundsForWidth() {
- return TextFlags.isFeatureEnabled(Flags.FLAG_USE_BOUNDS_FOR_WIDTH);
- }
-
- /**
* @see Flags#fixLineHeightForLocale()
*/
public static boolean fixLineHeightForLocale() {
diff --git a/core/java/android/text/TextFlags.java b/core/java/android/text/TextFlags.java
index 9e02460..0f1b031 100644
--- a/core/java/android/text/TextFlags.java
+++ b/core/java/android/text/TextFlags.java
@@ -56,8 +56,6 @@
*/
public static final String[] TEXT_ACONFIGS_FLAGS = {
Flags.FLAG_NO_BREAK_NO_HYPHENATION_SPAN,
- Flags.FLAG_PHRASE_STRICT_FALLBACK,
- Flags.FLAG_USE_BOUNDS_FOR_WIDTH,
Flags.FLAG_FIX_LINE_HEIGHT_FOR_LOCALE,
Flags.FLAG_ICU_BIDI_MIGRATION,
Flags.FLAG_FIX_MISALIGNED_CONTEXT_MENU,
@@ -70,8 +68,6 @@
*/
public static final boolean[] TEXT_ACONFIG_DEFAULT_VALUE = {
Flags.noBreakNoHyphenationSpan(),
- Flags.phraseStrictFallback(),
- Flags.useBoundsForWidth(),
Flags.fixLineHeightForLocale(),
Flags.icuBidiMigration(),
Flags.fixMisalignedContextMenu(),
diff --git a/core/java/android/text/flags/flags.aconfig b/core/java/android/text/flags/flags.aconfig
index d33c95e..b2be1a7 100644
--- a/core/java/android/text/flags/flags.aconfig
+++ b/core/java/android/text/flags/flags.aconfig
@@ -2,14 +2,6 @@
container: "system"
flag {
- name: "vendor_custom_locale_fallback"
- namespace: "text"
- description: "A feature flag that adds custom locale fallback to the vendor customization XML. This enables vendors to add their locale specific fonts, e.g. Japanese font."
- is_fixed_read_only: true
- bug: "278768958"
-}
-
-flag {
name: "new_fonts_fallback_xml"
is_exported: true
namespace: "text"
@@ -20,13 +12,6 @@
}
flag {
- name: "fix_double_underline"
- namespace: "text"
- description: "Feature flag for fixing double underline because of the multiple font used in the single line."
- bug: "297336724"
-}
-
-flag {
name: "fix_line_height_for_locale"
is_exported: true
namespace: "text"
@@ -66,13 +51,6 @@
}
flag {
- name: "phrase_strict_fallback"
- namespace: "text"
- description: "Feature flag for automatic fallback from phrase based line break to strict line break."
- bug: "281970875"
-}
-
-flag {
name: "use_bounds_for_width"
is_exported: true
namespace: "text"
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index eb35817..a395c1a 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -1447,6 +1447,11 @@
}
@Override
+ public void onNullBinding(ComponentName name) {
+ context.unbindService(this);
+ }
+
+ @Override
public void onServiceDisconnected(ComponentName componentName) { }
});
diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java
index 2f28a87..118edc2 100644
--- a/core/java/android/widget/RemoteViewsAdapter.java
+++ b/core/java/android/widget/RemoteViewsAdapter.java
@@ -241,6 +241,11 @@
}
@Override
+ public void onNullBinding(ComponentName name) {
+ enqueueDeferredUnbindServiceMessage();
+ }
+
+ @Override
public void handleMessage(Message msg) {
RemoteViewsAdapter adapter = mAdapter.get();
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index a4b28ad..ef941da 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -1659,11 +1659,7 @@
}
if (!hasUseBoundForWidthValue) {
- if (CompatChanges.isChangeEnabled(USE_BOUNDS_FOR_WIDTH)) {
- mUseBoundsForWidth = Flags.useBoundsForWidth();
- } else {
- mUseBoundsForWidth = false;
- }
+ mUseBoundsForWidth = CompatChanges.isChangeEnabled(USE_BOUNDS_FOR_WIDTH);
}
// TODO(b/179693024): Use a ChangeId instead.
diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java
index cdac097..1709ca7 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -404,6 +404,17 @@
}
public static void redirectLogStreams$ravenwood() {
+ if (sOut$ravenwood != null && sErr$ravenwood != null) {
+ return; // Already initialized.
+ }
+
+ // Make sure the Log class is loaded and the JNI methods are hooked up,
+ // before redirecting System.out/err.
+ // Otherwise, because ClassLoadHook tries to write to System.out, this would cause
+ // a circular initialization problem and would cause a UnsatisfiedLinkError
+ // on the JNI methods.
+ Log.isLoggable("X", Log.VERBOSE);
+
if (sOut$ravenwood == null) {
sOut$ravenwood = System.out;
System.setOut(new AndroidPrintStream(Log.INFO, "System.out"));
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 58818f3..4708be8 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -1144,7 +1144,8 @@
mDrawLegacyNavigationBarBackground =
((requestedVisibleTypes | mLastForceConsumingTypes)
& WindowInsets.Type.navigationBars()) != 0
- && (mWindow.getAttributes().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0;
+ && (mWindow.getAttributes().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0
+ && navBarSize > 0;
if (oldDrawLegacy != mDrawLegacyNavigationBarBackground) {
mDrawLegacyNavigationBarBackgroundHandled =
mWindow.onDrawLegacyNavigationBarBackgroundChanged(
diff --git a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
index 1fd933f..e440dc9 100644
--- a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
+++ b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java
@@ -34,7 +34,6 @@
import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MESSAGES;
import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.GROUP_ID;
import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.LEVEL;
-import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.LOCATION;
import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.MESSAGE;
import static android.internal.perfetto.protos.TracePacketOuterClass.TracePacket.INTERNED_DATA;
import static android.internal.perfetto.protos.TracePacketOuterClass.TracePacket.PROTOLOG_MESSAGE;
@@ -72,7 +71,6 @@
import java.io.FileInputStream;
import java.io.FileNotFoundException;
-import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
@@ -103,6 +101,7 @@
private final ProtoLogDataSource mDataSource;
@Nullable
private final ProtoLogViewerConfigReader mViewerConfigReader;
+ @Deprecated
@Nullable
private final ViewerConfigInputStreamProvider mViewerConfigInputStreamProvider;
@NonNull
@@ -148,6 +147,7 @@
cacheUpdater, groups);
}
+ @Deprecated
@VisibleForTesting
public PerfettoProtoLogImpl(
@Nullable ViewerConfigInputStreamProvider viewerConfigInputStreamProvider,
@@ -160,6 +160,18 @@
groups, dataSourceBuilder, configurationService);
}
+ @VisibleForTesting
+ public PerfettoProtoLogImpl(
+ @Nullable String viewerConfigFilePath,
+ @Nullable ProtoLogViewerConfigReader viewerConfigReader,
+ @NonNull Runnable cacheUpdater,
+ @NonNull IProtoLogGroup[] groups,
+ @NonNull ProtoLogDataSourceBuilder dataSourceBuilder,
+ @NonNull ProtoLogConfigurationService configurationService) {
+ this(viewerConfigFilePath, null, viewerConfigReader, cacheUpdater,
+ groups, dataSourceBuilder, configurationService);
+ }
+
private PerfettoProtoLogImpl(
@Nullable String viewerConfigFilePath,
@Nullable ViewerConfigInputStreamProvider viewerConfigInputStreamProvider,
@@ -449,6 +461,7 @@
Log.d(LOG_TAG, "Finished onTracingFlush");
}
+ @Deprecated
private void dumpViewerConfig() {
if (mViewerConfigInputStreamProvider == null) {
// No viewer config available
@@ -457,103 +470,29 @@
Log.d(LOG_TAG, "Dumping viewer config to trace");
- mDataSource.trace(ctx -> {
- try {
- ProtoInputStream pis = mViewerConfigInputStreamProvider.getInputStream();
-
- final ProtoOutputStream os = ctx.newTracePacket();
-
- os.write(TIMESTAMP, SystemClock.elapsedRealtimeNanos());
-
- final long outProtologViewerConfigToken = os.start(PROTOLOG_VIEWER_CONFIG);
- while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
- if (pis.getFieldNumber() == (int) MESSAGES) {
- writeViewerConfigMessage(pis, os);
- }
-
- if (pis.getFieldNumber() == (int) GROUPS) {
- writeViewerConfigGroup(pis, os);
- }
- }
-
- os.end(outProtologViewerConfigToken);
- } catch (IOException e) {
- Log.e(LOG_TAG, "Failed to read ProtoLog viewer config to dump on tracing end", e);
- }
- });
+ Utils.dumpViewerConfig(mDataSource, mViewerConfigInputStreamProvider);
Log.d(LOG_TAG, "Dumped viewer config to trace");
}
- private static void writeViewerConfigGroup(
- ProtoInputStream pis, ProtoOutputStream os) throws IOException {
- final long inGroupToken = pis.start(GROUPS);
- final long outGroupToken = os.start(GROUPS);
-
- while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
- switch (pis.getFieldNumber()) {
- case (int) ID:
- int id = pis.readInt(ID);
- os.write(ID, id);
- break;
- case (int) NAME:
- String name = pis.readString(NAME);
- os.write(NAME, name);
- break;
- case (int) TAG:
- String tag = pis.readString(TAG);
- os.write(TAG, tag);
- break;
- default:
- throw new RuntimeException(
- "Unexpected field id " + pis.getFieldNumber());
- }
- }
-
- pis.end(inGroupToken);
- os.end(outGroupToken);
- }
-
- private static void writeViewerConfigMessage(
- ProtoInputStream pis, ProtoOutputStream os) throws IOException {
- final long inMessageToken = pis.start(MESSAGES);
- final long outMessagesToken = os.start(MESSAGES);
-
- while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
- switch (pis.getFieldNumber()) {
- case (int) MessageData.MESSAGE_ID:
- os.write(MessageData.MESSAGE_ID,
- pis.readLong(MessageData.MESSAGE_ID));
- break;
- case (int) MESSAGE:
- os.write(MESSAGE, pis.readString(MESSAGE));
- break;
- case (int) LEVEL:
- os.write(LEVEL, pis.readInt(LEVEL));
- break;
- case (int) GROUP_ID:
- os.write(GROUP_ID, pis.readInt(GROUP_ID));
- break;
- case (int) LOCATION:
- os.write(LOCATION, pis.readString(LOCATION));
- break;
- default:
- throw new RuntimeException(
- "Unexpected field id " + pis.getFieldNumber());
- }
- }
-
- pis.end(inMessageToken);
- os.end(outMessagesToken);
- }
-
private void logToLogcat(String tag, LogLevel level, Message message,
@Nullable Object[] args) {
String messageString;
if (mViewerConfigReader == null) {
messageString = message.getMessage();
+
+ if (messageString == null) {
+ Log.e(LOG_TAG, "Failed to decode message for logcat. "
+ + "Message not available without ViewerConfig to decode the hash.");
+ }
} else {
messageString = message.getMessage(mViewerConfigReader);
+
+ if (messageString == null) {
+ Log.e(LOG_TAG, "Failed to decode message for logcat. "
+ + "Message hash either not available in viewerConfig file or "
+ + "not loaded into memory from file before decoding.");
+ }
}
if (messageString == null) {
@@ -760,7 +699,7 @@
os.write(MessageData.MESSAGE_ID, messageHash);
os.write(MESSAGE, message);
- os.write(LEVEL, level.ordinal());
+ os.write(LEVEL, level.id);
os.write(GROUP_ID, logGroup.getId());
os.end(messageConfigToken);
@@ -954,8 +893,7 @@
private static class Message {
@Nullable
private final Long mMessageHash;
- @Nullable
- private final Integer mMessageMask;
+ private final int mMessageMask;
@Nullable
private final String mMessageString;
@@ -972,8 +910,7 @@
this.mMessageString = messageString;
}
- @Nullable
- private Integer getMessageMask() {
+ private int getMessageMask() {
return mMessageMask;
}
diff --git a/core/java/com/android/internal/protolog/ProtoLogConfigurationService.java b/core/java/com/android/internal/protolog/ProtoLogConfigurationService.java
index d54a80b..7031d69 100644
--- a/core/java/com/android/internal/protolog/ProtoLogConfigurationService.java
+++ b/core/java/com/android/internal/protolog/ProtoLogConfigurationService.java
@@ -26,8 +26,6 @@
import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.LOCATION;
import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.MESSAGE;
import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.MESSAGE_ID;
-import static android.internal.perfetto.protos.TracePacketOuterClass.TracePacket.PROTOLOG_VIEWER_CONFIG;
-import static android.internal.perfetto.protos.TracePacketOuterClass.TracePacket.TIMESTAMP;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -36,7 +34,6 @@
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
-import android.os.SystemClock;
import android.tracing.perfetto.DataSourceParams;
import android.tracing.perfetto.InitArguments;
import android.tracing.perfetto.Producer;
@@ -125,7 +122,8 @@
this(ProtoLogDataSource::new, tracer);
}
- private ProtoLogConfigurationService(
+ @VisibleForTesting
+ public ProtoLogConfigurationService(
@NonNull ProtoLogDataSourceBuilder dataSourceBuilder,
@NonNull ViewerConfigFileTracer tracer) {
mDataSource = dataSourceBuilder.build(
@@ -374,32 +372,13 @@
private static void dumpTransitionTraceConfig(@NonNull ProtoLogDataSource dataSource,
@NonNull String viewerConfigFilePath) {
- dataSource.trace(ctx -> {
- final ProtoInputStream pis;
+ Utils.dumpViewerConfig(dataSource, () -> {
try {
- pis = new ProtoInputStream(new FileInputStream(viewerConfigFilePath));
+ return new ProtoInputStream(new FileInputStream(viewerConfigFilePath));
} catch (FileNotFoundException e) {
throw new RuntimeException(
"Failed to load viewer config file " + viewerConfigFilePath, e);
}
-
- try {
- final ProtoOutputStream os = ctx.newTracePacket();
-
- os.write(TIMESTAMP, SystemClock.elapsedRealtimeNanos());
-
- final long outProtologViewerConfigToken = os.start(PROTOLOG_VIEWER_CONFIG);
- while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
- switch (pis.getFieldNumber()) {
- case (int) MESSAGES -> writeViewerConfigMessage(pis, os);
- case (int) GROUPS -> writeViewerConfigGroup(pis, os);
- }
- }
-
- os.end(outProtologViewerConfigToken);
- } catch (IOException e) {
- Log.e(LOG_TAG, "Failed to read ProtoLog viewer config to dump on tracing end", e);
- }
});
}
diff --git a/core/java/com/android/internal/protolog/ProtoLogDataSource.java b/core/java/com/android/internal/protolog/ProtoLogDataSource.java
index 1b2f5f7..0afb135 100644
--- a/core/java/com/android/internal/protolog/ProtoLogDataSource.java
+++ b/core/java/com/android/internal/protolog/ProtoLogDataSource.java
@@ -283,10 +283,24 @@
public static class Instance extends DataSourceInstance {
public interface TracingInstanceStartCallback {
+ /**
+ * Execute the tracing instance's onStart callback.
+ * @param instanceIdx The index of the tracing instance we are executing the callback
+ * for.
+ * @param config The protolog configuration for the tracing instance we are executing
+ * the callback for.
+ */
void run(int instanceIdx, @NonNull ProtoLogConfig config);
}
public interface TracingInstanceStopCallback {
+ /**
+ * Execute the tracing instance's onStop callback.
+ * @param instanceIdx The index of the tracing instance we are executing the callback
+ * for.
+ * @param config The protolog configuration for the tracing instance we are executing
+ * the callback for.
+ */
void run(int instanceIdx, @NonNull ProtoLogConfig config);
}
diff --git a/core/java/com/android/internal/protolog/ProtoLogImpl.java b/core/java/com/android/internal/protolog/ProtoLogImpl.java
index da6d8cf..7bdcf2d 100644
--- a/core/java/com/android/internal/protolog/ProtoLogImpl.java
+++ b/core/java/com/android/internal/protolog/ProtoLogImpl.java
@@ -23,6 +23,7 @@
import static com.android.internal.protolog.common.ProtoLogToolInjected.Value.VIEWER_CONFIG_PATH;
import android.annotation.Nullable;
+import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.IProtoLog;
@@ -37,6 +38,8 @@
* A service for the ProtoLog logging system.
*/
public class ProtoLogImpl {
+ private static final String LOG_TAG = "ProtoLogImpl";
+
private static IProtoLog sServiceInstance = null;
@ProtoLogToolInjected(VIEWER_CONFIG_PATH)
@@ -97,6 +100,9 @@
*/
public static synchronized IProtoLog getSingleInstance() {
if (sServiceInstance == null) {
+ Log.i(LOG_TAG, "Setting up " + ProtoLogImpl.class.getSimpleName() + " with "
+ + "viewerConfigPath = " + sViewerConfigPath);
+
final var groups = sLogGroups.values().toArray(new IProtoLogGroup[0]);
if (android.tracing.Flags.perfettoProtologTracing()) {
@@ -105,6 +111,9 @@
// TODO(b/353530422): Remove - temporary fix to unblock b/352290057
// In some tests the viewer config file might not exist in which we don't
// want to provide config path to the user
+ Log.w(LOG_TAG, "Failed to find viewerConfigFile when setting up "
+ + ProtoLogImpl.class.getSimpleName() + ". "
+ + "Setting up without a viewer config instead...");
sServiceInstance = new PerfettoProtoLogImpl(sCacheUpdater, groups);
} else {
sServiceInstance =
diff --git a/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java b/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java
index 6c8996e..0a80e00 100644
--- a/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java
+++ b/core/java/com/android/internal/protolog/ProtoLogViewerConfigReader.java
@@ -43,6 +43,13 @@
return mLogMessageMap.get(messageHash);
}
+ /**
+ * Load the viewer configs for the target groups into memory.
+ * Only viewer configs loaded into memory can be required. So this must be called for all groups
+ * we want to query before we query their viewer config.
+ *
+ * @param groups Groups to load the viewer configs from file into memory.
+ */
public synchronized void loadViewerConfig(@NonNull String[] groups) {
loadViewerConfig(groups, (message) -> {});
}
diff --git a/core/java/com/android/internal/protolog/Utils.java b/core/java/com/android/internal/protolog/Utils.java
new file mode 100644
index 0000000..1e6ba30
--- /dev/null
+++ b/core/java/com/android/internal/protolog/Utils.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.protolog;
+
+import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.GROUPS;
+import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.Group.ID;
+import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.Group.NAME;
+import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.Group.TAG;
+import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MESSAGES;
+import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.GROUP_ID;
+import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.LEVEL;
+import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.LOCATION;
+import static android.internal.perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.MESSAGE;
+import static android.internal.perfetto.protos.TracePacketOuterClass.TracePacket.PROTOLOG_VIEWER_CONFIG;
+import static android.internal.perfetto.protos.TracePacketOuterClass.TracePacket.TIMESTAMP;
+
+import android.annotation.NonNull;
+import android.internal.perfetto.protos.Protolog;
+import android.os.SystemClock;
+import android.util.Log;
+import android.util.proto.ProtoInputStream;
+import android.util.proto.ProtoOutputStream;
+
+import java.io.IOException;
+
+public class Utils {
+ private static final String LOG_TAG = "ProtoLogUtils";
+
+ /**
+ * Dump the viewer config provided by the input stream to the target datasource.
+ * @param dataSource The datasource to dump the ProtoLog viewer config to.
+ * @param viewerConfigInputStreamProvider The InputStream that provided the proto viewer config.
+ */
+ public static void dumpViewerConfig(@NonNull ProtoLogDataSource dataSource,
+ @NonNull ViewerConfigInputStreamProvider viewerConfigInputStreamProvider) {
+ dataSource.trace(ctx -> {
+ try {
+ ProtoInputStream pis = viewerConfigInputStreamProvider.getInputStream();
+
+ final ProtoOutputStream os = ctx.newTracePacket();
+
+ os.write(TIMESTAMP, SystemClock.elapsedRealtimeNanos());
+
+ final long outProtologViewerConfigToken = os.start(PROTOLOG_VIEWER_CONFIG);
+ while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ if (pis.getFieldNumber() == (int) MESSAGES) {
+ writeViewerConfigMessage(pis, os);
+ }
+
+ if (pis.getFieldNumber() == (int) GROUPS) {
+ writeViewerConfigGroup(pis, os);
+ }
+ }
+
+ os.end(outProtologViewerConfigToken);
+ } catch (IOException e) {
+ Log.e(LOG_TAG, "Failed to read ProtoLog viewer config to dump to datasource", e);
+ }
+ });
+ }
+
+ private static void writeViewerConfigGroup(
+ @NonNull ProtoInputStream pis, @NonNull ProtoOutputStream os) throws IOException {
+ final long inGroupToken = pis.start(GROUPS);
+ final long outGroupToken = os.start(GROUPS);
+
+ while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pis.getFieldNumber()) {
+ case (int) ID:
+ int id = pis.readInt(ID);
+ os.write(ID, id);
+ break;
+ case (int) NAME:
+ String name = pis.readString(NAME);
+ os.write(NAME, name);
+ break;
+ case (int) TAG:
+ String tag = pis.readString(TAG);
+ os.write(TAG, tag);
+ break;
+ default:
+ throw new RuntimeException(
+ "Unexpected field id " + pis.getFieldNumber());
+ }
+ }
+
+ pis.end(inGroupToken);
+ os.end(outGroupToken);
+ }
+
+ private static void writeViewerConfigMessage(
+ ProtoInputStream pis, ProtoOutputStream os) throws IOException {
+ final long inMessageToken = pis.start(MESSAGES);
+ final long outMessagesToken = os.start(MESSAGES);
+
+ while (pis.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
+ switch (pis.getFieldNumber()) {
+ case (int) Protolog.ProtoLogViewerConfig.MessageData.MESSAGE_ID:
+ os.write(Protolog.ProtoLogViewerConfig.MessageData.MESSAGE_ID,
+ pis.readLong(Protolog.ProtoLogViewerConfig.MessageData.MESSAGE_ID));
+ break;
+ case (int) MESSAGE:
+ os.write(MESSAGE, pis.readString(MESSAGE));
+ break;
+ case (int) LEVEL:
+ os.write(LEVEL, pis.readInt(LEVEL));
+ break;
+ case (int) GROUP_ID:
+ os.write(GROUP_ID, pis.readInt(GROUP_ID));
+ break;
+ case (int) LOCATION:
+ os.write(LOCATION, pis.readString(LOCATION));
+ break;
+ default:
+ throw new RuntimeException(
+ "Unexpected field id " + pis.getFieldNumber());
+ }
+ }
+
+ pis.end(inMessageToken);
+ os.end(outMessagesToken);
+ }
+
+}
diff --git a/core/java/com/android/internal/protolog/common/LogLevel.java b/core/java/com/android/internal/protolog/common/LogLevel.java
index 16c34e1..b5541ae 100644
--- a/core/java/com/android/internal/protolog/common/LogLevel.java
+++ b/core/java/com/android/internal/protolog/common/LogLevel.java
@@ -17,10 +17,18 @@
package com.android.internal.protolog.common;
public enum LogLevel {
- DEBUG("d"), VERBOSE("v"), INFO("i"), WARN("w"), ERROR("e"), WTF("wtf");
+ DEBUG("d", 1),
+ VERBOSE("v", 2),
+ INFO("i", 3),
+ WARN("w", 4),
+ ERROR("e", 5),
+ WTF("wtf", 6);
public final String shortCode;
- LogLevel(String shortCode) {
+ public final int id;
+
+ LogLevel(String shortCode, int id) {
this.shortCode = shortCode;
+ this.id = id;
}
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index a19b71c..d35c66e 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4033,7 +4033,6 @@
<!-- Allows an application to manage policy related to block package uninstallation.
<p>Protection level: internal|role
<p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
- @FlaggedApi("android.app.admin.flags.dedicated_device_control_api_enabled")
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_BLOCK_UNINSTALL"
android:protectionLevel="internal|role" />
@@ -4041,7 +4040,6 @@
<!-- Allows an application to manage policy related to camera toggle.
<p>Protection level: internal|role
<p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
- @FlaggedApi("android.app.admin.flags.dedicated_device_control_api_enabled")
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_CAMERA_TOGGLE"
android:protectionLevel="internal|role" />
@@ -4049,7 +4047,6 @@
<!-- Allows an application to manage policy related to microphone toggle.
<p>Protection level: internal|role
<p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only.
- @FlaggedApi("android.app.admin.flags.dedicated_device_control_api_enabled")
-->
<permission android:name="android.permission.MANAGE_DEVICE_POLICY_MICROPHONE_TOGGLE"
android:protectionLevel="internal|role" />
diff --git a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java
index 10aed8d..1429272 100644
--- a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java
+++ b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java
@@ -16,8 +16,6 @@
package android.graphics;
-import static com.android.text.flags.Flags.FLAG_VENDOR_CUSTOM_LOCALE_FALLBACK;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -32,7 +30,6 @@
import android.graphics.fonts.SystemFonts;
import android.graphics.text.PositionedGlyphs;
import android.graphics.text.TextRunShaper;
-import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.text.FontConfig;
@@ -931,7 +928,6 @@
return String.format(xml, op, lang, font);
}
- @RequiresFlagsEnabled(FLAG_VENDOR_CUSTOM_LOCALE_FALLBACK)
@Test
public void testBuildSystemFallback__Customization_locale_prepend() {
final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
@@ -947,7 +943,6 @@
assertB3emFontIsUsed(typeface);
}
- @RequiresFlagsEnabled(FLAG_VENDOR_CUSTOM_LOCALE_FALLBACK)
@Test
public void testBuildSystemFallback__Customization_locale_replace() {
final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
@@ -963,7 +958,6 @@
assertB3emFontIsUsed(typeface);
}
- @RequiresFlagsEnabled(FLAG_VENDOR_CUSTOM_LOCALE_FALLBACK)
@Test
public void testBuildSystemFallback__Customization_locale_append() {
final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
@@ -979,7 +973,6 @@
assertA3emFontIsUsed(typeface);
}
- @RequiresFlagsEnabled(FLAG_VENDOR_CUSTOM_LOCALE_FALLBACK)
@Test
public void testBuildSystemFallback__Customization_locale_ScriptMismatch() {
final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
@@ -995,7 +988,6 @@
assertA3emFontIsUsed(typeface);
}
- @RequiresFlagsEnabled(FLAG_VENDOR_CUSTOM_LOCALE_FALLBACK)
@Test
public void testBuildSystemFallback__Customization_locale_SubscriptMatch() {
final ArrayMap<String, Typeface> fontMap = new ArrayMap<>();
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 9a55b80..880f30c 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -580,6 +580,11 @@
<permission name="android.permission.PREPARE_FACTORY_RESET" />
<!-- Permission required for CTS test - FileIntegrityManagerTest -->
<permission name="android.permission.SETUP_FSVERITY" />
+ <!-- Permissions required for CTS test - AppFunctionManagerTest -->
+ <permission name="android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED" />
+ <permission name="android.permission.EXECUTE_APP_FUNCTIONS" />
+ <!-- Permission required for CTS test - CtsNfcTestCases -->
+ <permission name="android.permission.NFC_SET_CONTROLLER_ALWAYS_ON" />
</privapp-permissions>
<privapp-permissions package="com.android.statementservice">
diff --git a/graphics/java/android/graphics/fonts/FontCustomizationParser.java b/graphics/java/android/graphics/fonts/FontCustomizationParser.java
index ba5628c..b7bf055 100644
--- a/graphics/java/android/graphics/fonts/FontCustomizationParser.java
+++ b/graphics/java/android/graphics/fonts/FontCustomizationParser.java
@@ -182,10 +182,8 @@
// For ignoring the customization, consume the new-locale-family element but don't
// register any customizations.
- if (com.android.text.flags.Flags.vendorCustomLocaleFallback()) {
- outCustomization.add(new FontConfig.Customization.LocaleFallback(
- Locale.forLanguageTag(lang), intOp, family));
- }
+ outCustomization.add(new FontConfig.Customization.LocaleFallback(
+ Locale.forLanguageTag(lang), intOp, family));
} else {
throw new IllegalArgumentException("Unknown customizationType=" + customizationType);
}
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt
new file mode 100644
index 0000000..35d459f
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedViewTest.kt
@@ -0,0 +1,324 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.bubbles.bar
+
+import android.app.ActivityManager
+import android.content.Context
+import android.graphics.Insets
+import android.graphics.Rect
+import android.view.LayoutInflater
+import android.view.View
+import android.view.WindowManager
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
+import com.android.internal.protolog.ProtoLog
+import com.android.wm.shell.R
+import com.android.wm.shell.bubbles.Bubble
+import com.android.wm.shell.bubbles.BubbleData
+import com.android.wm.shell.bubbles.BubbleExpandedViewManager
+import com.android.wm.shell.bubbles.BubblePositioner
+import com.android.wm.shell.bubbles.BubbleTaskView
+import com.android.wm.shell.bubbles.BubbleTaskViewFactory
+import com.android.wm.shell.bubbles.DeviceConfig
+import com.android.wm.shell.bubbles.RegionSamplingProvider
+import com.android.wm.shell.common.ShellExecutor
+import com.android.wm.shell.shared.bubbles.BubbleBarLocation
+import com.android.wm.shell.shared.handles.RegionSamplingHelper
+import com.android.wm.shell.taskview.TaskView
+import com.android.wm.shell.taskview.TaskViewTaskController
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+import java.util.Collections
+import java.util.concurrent.Executor
+
+/** Tests for [BubbleBarExpandedViewTest] */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class BubbleBarExpandedViewTest {
+ companion object {
+ const val SCREEN_WIDTH = 2000
+ const val SCREEN_HEIGHT = 1000
+ }
+
+ private val context = ApplicationProvider.getApplicationContext<Context>()
+ private val windowManager = context.getSystemService(WindowManager::class.java)
+
+ private lateinit var mainExecutor: TestExecutor
+ private lateinit var bgExecutor: TestExecutor
+
+ private lateinit var expandedViewManager: BubbleExpandedViewManager
+ private lateinit var positioner: BubblePositioner
+ private lateinit var bubbleTaskView: BubbleTaskView
+
+ private lateinit var bubbleExpandedView: BubbleBarExpandedView
+ private var testableRegionSamplingHelper: TestableRegionSamplingHelper? = null
+ private var regionSamplingProvider: TestRegionSamplingProvider? = null
+
+ @Before
+ fun setUp() {
+ ProtoLog.REQUIRE_PROTOLOGTOOL = false
+ mainExecutor = TestExecutor()
+ bgExecutor = TestExecutor()
+ positioner = BubblePositioner(context, windowManager)
+ positioner.setShowingInBubbleBar(true)
+ val deviceConfig =
+ DeviceConfig(
+ windowBounds = Rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT),
+ isLargeScreen = true,
+ isSmallTablet = false,
+ isLandscape = true,
+ isRtl = false,
+ insets = Insets.of(10, 20, 30, 40)
+ )
+ positioner.update(deviceConfig)
+
+ expandedViewManager = createExpandedViewManager()
+ bubbleTaskView = FakeBubbleTaskViewFactory().create()
+
+ val inflater = LayoutInflater.from(context)
+
+ regionSamplingProvider = TestRegionSamplingProvider()
+
+ bubbleExpandedView = (inflater.inflate(
+ R.layout.bubble_bar_expanded_view, null, false /* attachToRoot */
+ ) as BubbleBarExpandedView)
+ bubbleExpandedView.initialize(
+ expandedViewManager,
+ positioner,
+ false /* isOverflow */,
+ bubbleTaskView,
+ mainExecutor,
+ bgExecutor,
+ regionSamplingProvider
+ )
+
+ getInstrumentation().runOnMainSync(Runnable {
+ bubbleExpandedView.onAttachedToWindow()
+ // Helper should be created once attached to window
+ testableRegionSamplingHelper = regionSamplingProvider!!.helper
+ })
+ }
+
+ @After
+ fun tearDown() {
+ testableRegionSamplingHelper?.stopAndDestroy()
+ }
+
+ @Test
+ fun testCreateSamplingHelper_onAttach() {
+ assertThat(testableRegionSamplingHelper).isNotNull()
+ }
+
+ @Test
+ fun testDestroySamplingHelper_onDetach() {
+ bubbleExpandedView.onDetachedFromWindow()
+ assertThat(testableRegionSamplingHelper!!.isDestroyed).isTrue()
+ }
+
+ @Test
+ fun testStopSampling_onDragStart() {
+ bubbleExpandedView.setContentVisibility(true)
+ assertThat(testableRegionSamplingHelper!!.isStarted).isTrue()
+
+ bubbleExpandedView.setDragging(true)
+ assertThat(testableRegionSamplingHelper!!.isStopped).isTrue()
+ }
+
+ @Test
+ fun testStartSampling_onDragEnd() {
+ bubbleExpandedView.setDragging(true)
+ bubbleExpandedView.setContentVisibility(true)
+ assertThat(testableRegionSamplingHelper!!.isStopped).isTrue()
+
+ bubbleExpandedView.setDragging(false)
+ assertThat(testableRegionSamplingHelper!!.isStarted).isTrue()
+ }
+
+ @Test
+ fun testStartSampling_onContentVisible() {
+ bubbleExpandedView.setContentVisibility(true)
+ assertThat(testableRegionSamplingHelper!!.setWindowVisible).isTrue()
+ assertThat(testableRegionSamplingHelper!!.isStarted).isTrue()
+ }
+
+ @Test
+ fun testStopSampling_onContentInvisible() {
+ bubbleExpandedView.setContentVisibility(false)
+
+ assertThat(testableRegionSamplingHelper!!.setWindowInvisible).isTrue()
+ assertThat(testableRegionSamplingHelper!!.isStopped).isTrue()
+ }
+
+ @Test
+ fun testSampling_startStopAnimating_visible() {
+ bubbleExpandedView.isAnimating = true
+ bubbleExpandedView.setContentVisibility(true)
+ assertThat(testableRegionSamplingHelper!!.isStopped).isTrue()
+
+ bubbleExpandedView.isAnimating = false
+ assertThat(testableRegionSamplingHelper!!.isStarted).isTrue()
+ }
+
+ @Test
+ fun testSampling_startStopAnimating_invisible() {
+ bubbleExpandedView.isAnimating = true
+ bubbleExpandedView.setContentVisibility(false)
+ assertThat(testableRegionSamplingHelper!!.isStopped).isTrue()
+ testableRegionSamplingHelper!!.reset()
+
+ bubbleExpandedView.isAnimating = false
+ assertThat(testableRegionSamplingHelper!!.isStopped).isTrue()
+ }
+
+ private inner class FakeBubbleTaskViewFactory : BubbleTaskViewFactory {
+ override fun create(): BubbleTaskView {
+ val taskViewTaskController = mock<TaskViewTaskController>()
+ val taskView = TaskView(context, taskViewTaskController)
+ val taskInfo = mock<ActivityManager.RunningTaskInfo>()
+ whenever(taskViewTaskController.taskInfo).thenReturn(taskInfo)
+ return BubbleTaskView(taskView, mainExecutor)
+ }
+ }
+
+ private inner class TestRegionSamplingProvider : RegionSamplingProvider {
+
+ lateinit var helper: TestableRegionSamplingHelper
+
+ override fun createHelper(
+ sampledView: View?,
+ callback: RegionSamplingHelper.SamplingCallback?,
+ backgroundExecutor: Executor?,
+ mainExecutor: Executor?
+ ): RegionSamplingHelper {
+ helper = TestableRegionSamplingHelper(sampledView, callback, backgroundExecutor,
+ mainExecutor)
+ return helper
+ }
+ }
+
+ private inner class TestableRegionSamplingHelper(
+ sampledView: View?,
+ samplingCallback: SamplingCallback?,
+ backgroundExecutor: Executor?,
+ mainExecutor: Executor?
+ ) : RegionSamplingHelper(sampledView, samplingCallback, backgroundExecutor, mainExecutor) {
+
+ var isStarted = false
+ var isStopped = false
+ var isDestroyed = false
+ var setWindowVisible = false
+ var setWindowInvisible = false
+
+ override fun start(initialSamplingBounds: Rect) {
+ super.start(initialSamplingBounds)
+ isStarted = true
+ }
+
+ override fun stop() {
+ super.stop()
+ isStopped = true
+ }
+
+ override fun stopAndDestroy() {
+ super.stopAndDestroy()
+ isDestroyed = true
+ }
+
+ override fun setWindowVisible(visible: Boolean) {
+ super.setWindowVisible(visible)
+ if (visible) {
+ setWindowVisible = true
+ } else {
+ setWindowInvisible = true
+ }
+ }
+
+ fun reset() {
+ isStarted = false
+ isStopped = false
+ isDestroyed = false
+ setWindowVisible = false
+ setWindowInvisible = false
+ }
+ }
+
+ private fun createExpandedViewManager(): BubbleExpandedViewManager {
+ return object : BubbleExpandedViewManager {
+ override val overflowBubbles: List<Bubble>
+ get() = Collections.emptyList()
+
+ override fun setOverflowListener(listener: BubbleData.Listener) {
+ }
+
+ override fun collapseStack() {
+ }
+
+ override fun updateWindowFlagsForBackpress(intercept: Boolean) {
+ }
+
+ override fun promoteBubbleFromOverflow(bubble: Bubble) {
+ }
+
+ override fun removeBubble(key: String, reason: Int) {
+ }
+
+ override fun dismissBubble(bubble: Bubble, reason: Int) {
+ }
+
+ override fun setAppBubbleTaskId(key: String, taskId: Int) {
+ }
+
+ override fun isStackExpanded(): Boolean {
+ return true
+ }
+
+ override fun isShowingAsBubbleBar(): Boolean {
+ return true
+ }
+
+ override fun hideCurrentInputMethod() {
+ }
+
+ override fun updateBubbleBarLocation(location: BubbleBarLocation) {
+ }
+ }
+ }
+
+ private class TestExecutor : ShellExecutor {
+
+ private val runnables: MutableList<Runnable> = mutableListOf()
+
+ override fun execute(runnable: Runnable) {
+ runnables.add(runnable)
+ }
+
+ override fun executeDelayed(runnable: Runnable, delayMillis: Long) {
+ execute(runnable)
+ }
+
+ override fun removeCallbacks(runnable: Runnable?) {}
+
+ override fun hasCallback(runnable: Runnable?): Boolean = false
+ }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/handles/RegionSamplingHelper.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/handles/RegionSamplingHelper.java
index b92b8ef..a06cf78 100644
--- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/handles/RegionSamplingHelper.java
+++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/handles/RegionSamplingHelper.java
@@ -329,7 +329,7 @@
/**
* Get the sampled region of interest from the sampled view
* @param sampledView The view that this helper is attached to for convenience
- * @return the region to be sampled in sceen coordinates. Return {@code null} to avoid
+ * @return the region to be sampled in screen coordinates. Return {@code null} to avoid
* sampling in this frame
*/
Rect getSampledRegion(View sampledView);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AppToWebUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AppToWebUtils.kt
new file mode 100644
index 0000000..05ce361
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AppToWebUtils.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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:JvmName("AppToWebUtils")
+
+package com.android.wm.shell.apptoweb
+
+import android.content.Context
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.net.Uri
+
+private val browserIntent = Intent()
+ .setAction(Intent.ACTION_VIEW)
+ .addCategory(Intent.CATEGORY_BROWSABLE)
+ .setData(Uri.parse("http:"))
+
+/**
+ * Returns a boolean indicating whether a given package is a browser app.
+ */
+fun isBrowserApp(context: Context, packageName: String, userId: Int): Boolean {
+ browserIntent.setPackage(packageName)
+ val list = context.packageManager.queryIntentActivitiesAsUser(
+ browserIntent, PackageManager.MATCH_ALL, userId
+ )
+
+ list.forEach {
+ if (it.activityInfo != null && it.handleAllWebDataURI) {
+ return true
+ }
+ }
+ return false
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AssistContentRequester.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AssistContentRequester.kt
new file mode 100644
index 0000000..249185e
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/AssistContentRequester.kt
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.apptoweb
+
+import android.app.ActivityTaskManager
+import android.app.IActivityTaskManager
+import android.app.IAssistDataReceiver
+import android.app.assist.AssistContent
+import android.content.Context
+import android.graphics.Bitmap
+import android.os.Bundle
+import android.os.RemoteException
+import android.util.Slog
+import java.lang.ref.WeakReference
+import java.util.Collections
+import java.util.WeakHashMap
+import java.util.concurrent.Executor
+
+/**
+ * Can be used to request the AssistContent from a provided task id, useful for getting the web uri
+ * if provided from the task.
+ */
+class AssistContentRequester(
+ context: Context,
+ private val callBackExecutor: Executor,
+ private val systemInteractionExecutor: Executor
+) {
+ interface Callback {
+ // Called when the [AssistContent] of the requested task is available.
+ fun onAssistContentAvailable(assistContent: AssistContent?)
+ }
+
+ private val activityTaskManager: IActivityTaskManager = ActivityTaskManager.getService()
+ private val attributionTag: String? = context.attributionTag
+ private val packageName: String = context.applicationContext.packageName
+
+ // If system loses the callback, our internal cache of original callback will also get cleared.
+ private val pendingCallbacks = Collections.synchronizedMap(WeakHashMap<Any, Callback>())
+
+ /**
+ * Request the [AssistContent] from the task with the provided id.
+ *
+ * @param taskId to query for the content.
+ * @param callback to call when the content is available, called on the main thread.
+ */
+ fun requestAssistContent(taskId: Int, callback: Callback) {
+ // ActivityTaskManager interaction here is synchronous, so call off the main thread.
+ systemInteractionExecutor.execute {
+ try {
+ val success = activityTaskManager.requestAssistDataForTask(
+ AssistDataReceiver(callback, this),
+ taskId,
+ packageName,
+ attributionTag,
+ false /* fetchStructure */
+ )
+ if (!success) {
+ executeOnMainExecutor { callback.onAssistContentAvailable(null) }
+ }
+ } catch (e: RemoteException) {
+ Slog.e(TAG, "Requesting assist content failed for task: $taskId", e)
+ }
+ }
+ }
+
+ private fun executeOnMainExecutor(callback: Runnable) {
+ callBackExecutor.execute(callback)
+ }
+
+ private class AssistDataReceiver(
+ callback: Callback,
+ parent: AssistContentRequester
+ ) : IAssistDataReceiver.Stub() {
+ // The AssistDataReceiver binder callback object is passed to a system server, that may
+ // keep hold of it for longer than the lifetime of the AssistContentRequester object,
+ // potentially causing a memory leak. In the callback passed to the system server, only
+ // keep a weak reference to the parent object and lookup its callback if it still exists.
+ private val parentRef: WeakReference<AssistContentRequester>
+ private val callbackKey = Any()
+
+ init {
+ parent.pendingCallbacks[callbackKey] = callback
+ parentRef = WeakReference(parent)
+ }
+
+ override fun onHandleAssistData(data: Bundle?) {
+ val content = data?.getParcelable(ASSIST_KEY_CONTENT, AssistContent::class.java)
+ if (content == null) {
+ Slog.d(TAG, "Received AssistData, but no AssistContent found")
+ return
+ }
+ val requester = parentRef.get()
+ if (requester != null) {
+ val callback = requester.pendingCallbacks[callbackKey]
+ if (callback != null) {
+ requester.executeOnMainExecutor { callback.onAssistContentAvailable(content) }
+ } else {
+ Slog.d(TAG, "Callback received after calling UI was disposed of")
+ }
+ } else {
+ Slog.d(TAG, "Callback received after Requester was collected")
+ }
+ }
+
+ override fun onHandleAssistScreenshot(screenshot: Bitmap) {}
+ }
+
+ companion object {
+ private const val TAG = "AssistContentRequester"
+ private const val ASSIST_KEY_CONTENT = "content"
+ }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/OWNERS
index bfe1306a..6207e5b0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/OWNERS
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/apptoweb/OWNERS
@@ -1,6 +1,8 @@
atsjenk@google.com
jorgegil@google.com
madym@google.com
+mattsziklay@google.com
+mdehaini@google.com
pbdr@google.com
tkachenkoi@google.com
vaniadesmonda@google.com
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 0c95934..169361a 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
@@ -609,7 +609,8 @@
callback.onBubbleViewsReady(bubble);
}
},
- mMainExecutor);
+ mMainExecutor,
+ mBgExecutor);
if (mInflateSynchronously) {
mInflationTaskLegacy.onPostExecute(mInflationTaskLegacy.doInBackground());
} else {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
index f32974e..68c4657 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleOverflow.kt
@@ -80,7 +80,10 @@
expandedViewManager,
positioner,
/* isOverflow= */ true,
- /* bubbleTaskView= */ null
+ /* bubbleTaskView= */ null,
+ /* mainExecutor= */ null,
+ /* backgroundExecutor= */ null,
+ /* regionSamplingProvider= */ null
)
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java
index 5f8f0fd..0c0fd7b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTaskViewHelper.java
@@ -60,6 +60,9 @@
/** Called when back is pressed on the task root. */
void onBackPressed();
+
+ /** Called when task removal has started. */
+ void onTaskRemovalStarted();
}
private final Context mContext;
@@ -190,6 +193,7 @@
((ViewGroup) mParentView).removeView(mTaskView);
mTaskView = null;
}
+ mListener.onTaskRemovalStarted();
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
index 13855f7..3982a23 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTask.java
@@ -38,6 +38,7 @@
import android.util.Log;
import android.util.PathParser;
import android.view.LayoutInflater;
+import android.view.View;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.ColorUtils;
@@ -47,6 +48,7 @@
import com.android.wm.shell.R;
import com.android.wm.shell.bubbles.bar.BubbleBarExpandedView;
import com.android.wm.shell.bubbles.bar.BubbleBarLayerView;
+import com.android.wm.shell.shared.handles.RegionSamplingHelper;
import java.lang.ref.WeakReference;
import java.util.Objects;
@@ -222,7 +224,16 @@
ProtoLog.v(WM_SHELL_BUBBLES, "Task initializing bubble bar expanded view key=%s",
mBubble.getKey());
viewInfo.bubbleBarExpandedView.initialize(mExpandedViewManager.get(),
- mPositioner.get(), false /* isOverflow */, viewInfo.taskView);
+ mPositioner.get(), false /* isOverflow */, viewInfo.taskView,
+ mMainExecutor, mBgExecutor, new RegionSamplingProvider() {
+ @Override
+ public RegionSamplingHelper createHelper(View sampledView,
+ RegionSamplingHelper.SamplingCallback callback,
+ Executor backgroundExecutor, Executor mainExecutor) {
+ return RegionSamplingProvider.super.createHelper(sampledView,
+ callback, backgroundExecutor, mainExecutor);
+ }
+ });
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskLegacy.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskLegacy.java
index 5cfebf8..1b7bb0d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskLegacy.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleViewInfoTaskLegacy.java
@@ -38,6 +38,7 @@
import android.util.Log;
import android.util.PathParser;
import android.view.LayoutInflater;
+import android.view.View;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.graphics.ColorUtils;
@@ -46,6 +47,7 @@
import com.android.wm.shell.R;
import com.android.wm.shell.bubbles.bar.BubbleBarExpandedView;
import com.android.wm.shell.bubbles.bar.BubbleBarLayerView;
+import com.android.wm.shell.shared.handles.RegionSamplingHelper;
import java.lang.ref.WeakReference;
import java.util.Objects;
@@ -85,6 +87,7 @@
private boolean mSkipInflation;
private Callback mCallback;
private Executor mMainExecutor;
+ private Executor mBackgroundExecutor;
/**
* Creates a task to load information for the provided {@link Bubble}. Once all info
@@ -100,7 +103,8 @@
BubbleIconFactory factory,
boolean skipInflation,
Callback c,
- Executor mainExecutor) {
+ Executor mainExecutor,
+ Executor backgroundExecutor) {
mBubble = b;
mContext = new WeakReference<>(context);
mExpandedViewManager = new WeakReference<>(expandedViewManager);
@@ -112,6 +116,7 @@
mSkipInflation = skipInflation;
mCallback = c;
mMainExecutor = mainExecutor;
+ mBackgroundExecutor = backgroundExecutor;
}
@Override
@@ -123,7 +128,7 @@
if (mLayerView.get() != null) {
return BubbleViewInfo.populateForBubbleBar(mContext.get(), mExpandedViewManager.get(),
mTaskViewFactory.get(), mPositioner.get(), mLayerView.get(), mIconFactory,
- mBubble, mSkipInflation);
+ mBubble, mSkipInflation, mMainExecutor, mBackgroundExecutor);
} else {
return BubbleViewInfo.populate(mContext.get(), mExpandedViewManager.get(),
mTaskViewFactory.get(), mPositioner.get(), mStackView.get(), mIconFactory,
@@ -188,7 +193,9 @@
BubbleBarLayerView layerView,
BubbleIconFactory iconFactory,
Bubble b,
- boolean skipInflation) {
+ boolean skipInflation,
+ Executor mainExecutor,
+ Executor backgroundExecutor) {
BubbleViewInfo info = new BubbleViewInfo();
if (!skipInflation && !b.isInflated()) {
@@ -197,7 +204,16 @@
info.bubbleBarExpandedView = (BubbleBarExpandedView) inflater.inflate(
R.layout.bubble_bar_expanded_view, layerView, false /* attachToRoot */);
info.bubbleBarExpandedView.initialize(
- expandedViewManager, positioner, false /* isOverflow */, bubbleTaskView);
+ expandedViewManager, positioner, false /* isOverflow */, bubbleTaskView,
+ mainExecutor, backgroundExecutor, new RegionSamplingProvider() {
+ @Override
+ public RegionSamplingHelper createHelper(View sampledView,
+ RegionSamplingHelper.SamplingCallback callback,
+ Executor backgroundExecutor, Executor mainExecutor) {
+ return RegionSamplingProvider.super.createHelper(sampledView,
+ callback, backgroundExecutor, mainExecutor);
+ }
+ });
}
if (!populateCommonInfo(info, c, b, iconFactory)) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/RegionSamplingProvider.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/RegionSamplingProvider.java
new file mode 100644
index 0000000..30f5c8fd
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/RegionSamplingProvider.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.bubbles;
+
+import android.view.View;
+
+import com.android.wm.shell.shared.handles.RegionSamplingHelper;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Wrapper to provide a {@link com.android.wm.shell.shared.handles.RegionSamplingHelper} to allow
+ * testing it.
+ */
+public interface RegionSamplingProvider {
+
+ /** Creates and returns the region sampling helper */
+ default RegionSamplingHelper createHelper(View sampledView,
+ RegionSamplingHelper.SamplingCallback callback,
+ Executor backgroundExecutor,
+ Executor mainExecutor) {
+ return new RegionSamplingHelper(sampledView,
+ callback, backgroundExecutor, mainExecutor);
+ }
+
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
index 565fde0..74c3748 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
@@ -253,6 +253,7 @@
return;
}
setDragPivot(bbev);
+ bbev.setDragging(true);
// Corner radius gets scaled, apply the reverse scale to ensure we have the desired radius
final float cornerRadius = bbev.getDraggedCornerRadius() / EXPANDED_VIEW_DRAG_SCALE;
@@ -329,6 +330,7 @@
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
bbev.resetPivot();
+ bbev.setDragging(false);
}
});
startNewDragAnimation(animatorSet);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
index f90b2aa..ec235a5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java
@@ -19,10 +19,7 @@
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import android.annotation.Nullable;
-import android.app.ActivityManager;
import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Color;
import android.graphics.Insets;
import android.graphics.Outline;
import android.graphics.Rect;
@@ -37,6 +34,7 @@
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
import com.android.wm.shell.R;
import com.android.wm.shell.bubbles.Bubble;
@@ -46,9 +44,12 @@
import com.android.wm.shell.bubbles.BubbleTaskView;
import com.android.wm.shell.bubbles.BubbleTaskViewHelper;
import com.android.wm.shell.bubbles.Bubbles;
+import com.android.wm.shell.bubbles.RegionSamplingProvider;
import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
+import com.android.wm.shell.shared.handles.RegionSamplingHelper;
import com.android.wm.shell.taskview.TaskView;
+import java.util.concurrent.Executor;
import java.util.function.Supplier;
/** Expanded view of a bubble when it's part of the bubble bar. */
@@ -92,16 +93,35 @@
private boolean mIsOverflow;
private BubbleTaskViewHelper mBubbleTaskViewHelper;
private BubbleBarMenuViewController mMenuViewController;
- private @Nullable Supplier<Rect> mLayerBoundsSupplier;
- private @Nullable Listener mListener;
+ @Nullable
+ private Supplier<Rect> mLayerBoundsSupplier;
+ @Nullable
+ private Listener mListener;
private BubbleBarHandleView mHandleView;
- private @Nullable TaskView mTaskView;
- private @Nullable BubbleOverflowContainerView mOverflowView;
+ @Nullable
+ private TaskView mTaskView;
+ @Nullable
+ private BubbleOverflowContainerView mOverflowView;
+ /**
+ * The handle shown in the caption area is tinted based on the background color of the area.
+ * This can vary so we sample the caption region and update the handle color based on that.
+ * If we're showing the overflow, the helper and executors will be null.
+ */
+ @Nullable
+ private RegionSamplingHelper mRegionSamplingHelper;
+ @Nullable
+ private RegionSamplingProvider mRegionSamplingProvider;
+ @Nullable
+ private Executor mMainExecutor;
+ @Nullable
+ private Executor mBackgroundExecutor;
+ private final Rect mSampleRect = new Rect();
+ private final int[] mLoc = new int[2];
+
+ /** Height of the caption inset at the top of the TaskView */
private int mCaptionHeight;
-
- private int mBackgroundColor;
/** Corner radius used when view is resting */
private float mRestingCornerRadius = 0f;
/** Corner radius applied while dragging */
@@ -116,6 +136,7 @@
*/
private boolean mIsContentVisible = false;
private boolean mIsAnimating;
+ private boolean mIsDragging;
public BubbleBarExpandedView(Context context) {
this(context, null);
@@ -154,21 +175,20 @@
setOnTouchListener((v, event) -> true);
}
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- // Hide manage menu when view disappears
- mMenuViewController.hideMenu(false /* animated */);
- }
-
/** Initializes the view, must be called before doing anything else. */
public void initialize(BubbleExpandedViewManager expandedViewManager,
BubblePositioner positioner,
boolean isOverflow,
- @Nullable BubbleTaskView bubbleTaskView) {
+ @Nullable BubbleTaskView bubbleTaskView,
+ @Nullable Executor mainExecutor,
+ @Nullable Executor backgroundExecutor,
+ @Nullable RegionSamplingProvider regionSamplingProvider) {
mManager = expandedViewManager;
mPositioner = positioner;
mIsOverflow = isOverflow;
+ mMainExecutor = mainExecutor;
+ mBackgroundExecutor = backgroundExecutor;
+ mRegionSamplingProvider = regionSamplingProvider;
if (mIsOverflow) {
mOverflowView = (BubbleOverflowContainerView) LayoutInflater.from(getContext()).inflate(
@@ -191,6 +211,7 @@
mTaskView.setEnableSurfaceClipping(true);
mTaskView.setCornerRadius(mCurrentCornerRadius);
mTaskView.setVisibility(VISIBLE);
+ mTaskView.setCaptionInsets(Insets.of(0, mCaptionHeight, 0, 0));
// Handle view needs to draw on top of task view.
bringChildToFront(mHandleView);
@@ -245,32 +266,40 @@
return mHandleView;
}
- // TODO (b/275087636): call this when theme/config changes
/** Updates the view based on the current theme. */
public void applyThemeAttrs() {
+ mCaptionHeight = getResources().getDimensionPixelSize(
+ R.dimen.bubble_bar_expanded_view_caption_height);
mRestingCornerRadius = getResources().getDimensionPixelSize(
- R.dimen.bubble_bar_expanded_view_corner_radius
- );
+ R.dimen.bubble_bar_expanded_view_corner_radius);
mDraggedCornerRadius = getResources().getDimensionPixelSize(
- R.dimen.bubble_bar_expanded_view_corner_radius_dragged
- );
+ R.dimen.bubble_bar_expanded_view_corner_radius_dragged);
mCurrentCornerRadius = mRestingCornerRadius;
- final TypedArray ta = mContext.obtainStyledAttributes(new int[]{
- android.R.attr.colorBackgroundFloating});
- mBackgroundColor = ta.getColor(0, Color.WHITE);
- ta.recycle();
- mCaptionHeight = getResources().getDimensionPixelSize(
- R.dimen.bubble_bar_expanded_view_caption_height);
-
if (mTaskView != null) {
mTaskView.setCornerRadius(mCurrentCornerRadius);
- updateHandleColor(true /* animated */);
+ mTaskView.setCaptionInsets(Insets.of(0, mCaptionHeight, 0, 0));
}
}
@Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ // Hide manage menu when view disappears
+ mMenuViewController.hideMenu(false /* animated */);
+ if (mRegionSamplingHelper != null) {
+ mRegionSamplingHelper.stopAndDestroy();
+ }
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ recreateRegionSamplingHelper();
+ }
+
+ @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (mTaskView != null) {
@@ -284,16 +313,13 @@
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if (mTaskView != null) {
- mTaskView.layout(l, t, r,
- t + mTaskView.getMeasuredHeight());
- mTaskView.setCaptionInsets(Insets.of(0, mCaptionHeight, 0, 0));
+ mTaskView.layout(l, t, r, t + mTaskView.getMeasuredHeight());
}
}
@Override
public void onTaskCreated() {
setContentVisibility(true);
- updateHandleColor(false /* animated */);
if (mListener != null) {
mListener.onTaskCreated();
}
@@ -305,11 +331,70 @@
}
@Override
+ public void onTaskRemovalStarted() {
+ if (mRegionSamplingHelper != null) {
+ mRegionSamplingHelper.stopAndDestroy();
+ }
+ }
+
+ @Override
public void onBackPressed() {
if (mListener == null) return;
mListener.onBackPressed();
}
+ /**
+ * Set whether this view is currently being dragged.
+ *
+ * When dragging, the handle is hidden and content shouldn't be sampled. When dragging has
+ * ended we should start again.
+ */
+ public void setDragging(boolean isDragging) {
+ if (isDragging != mIsDragging) {
+ mIsDragging = isDragging;
+ updateSamplingState();
+ }
+ }
+
+ /** Returns whether region sampling should be enabled, i.e. if task view content is visible. */
+ private boolean shouldSampleRegion() {
+ return mTaskView != null
+ && mTaskView.getTaskInfo() != null
+ && !mIsDragging
+ && !mIsAnimating
+ && mIsContentVisible;
+ }
+
+ /**
+ * Handles starting or stopping the region sampling helper based on
+ * {@link #shouldSampleRegion()}.
+ */
+ private void updateSamplingState() {
+ if (mRegionSamplingHelper == null) return;
+ boolean shouldSample = shouldSampleRegion();
+ if (shouldSample) {
+ mRegionSamplingHelper.start(getCaptionSampleRect());
+ } else {
+ mRegionSamplingHelper.stop();
+ }
+ }
+
+ /** Returns the current area of the caption bar, in screen coordinates. */
+ Rect getCaptionSampleRect() {
+ if (mTaskView == null) return null;
+ mTaskView.getLocationOnScreen(mLoc);
+ mSampleRect.set(mLoc[0], mLoc[1],
+ mLoc[0] + mTaskView.getWidth(),
+ mLoc[1] + mCaptionHeight);
+ return mSampleRect;
+ }
+
+ @VisibleForTesting
+ @Nullable
+ public RegionSamplingHelper getRegionSamplingHelper() {
+ return mRegionSamplingHelper;
+ }
+
/** Cleans up the expanded view, should be called when the bubble is no longer active. */
public void cleanUpExpandedState() {
mMenuViewController.hideMenu(false /* animated */);
@@ -394,27 +479,14 @@
if (!mIsAnimating) {
mTaskView.setAlpha(visible ? 1f : 0f);
+ if (mRegionSamplingHelper != null) {
+ mRegionSamplingHelper.setWindowVisible(visible);
+ }
+ updateSamplingState();
}
}
/**
- * Updates the handle color based on the task view status bar or background color; if those
- * are transparent it defaults to the background color pulled from system theme attributes.
- */
- private void updateHandleColor(boolean animated) {
- if (mTaskView == null || mTaskView.getTaskInfo() == null) return;
- int color = mBackgroundColor;
- ActivityManager.TaskDescription taskDescription = mTaskView.getTaskInfo().taskDescription;
- if (taskDescription.getStatusBarColor() != Color.TRANSPARENT) {
- color = taskDescription.getStatusBarColor();
- } else if (taskDescription.getBackgroundColor() != Color.TRANSPARENT) {
- color = taskDescription.getBackgroundColor();
- }
- final boolean isRegionDark = Color.luminance(color) <= 0.5;
- mHandleView.updateHandleColor(isRegionDark, animated);
- }
-
- /**
* Sets the alpha of both this view and the task view.
*/
public void setTaskViewAlpha(float alpha) {
@@ -442,6 +514,11 @@
*/
public void setAnimating(boolean animating) {
mIsAnimating = animating;
+ if (mIsAnimating) {
+ // Stop sampling while animating -- when animating is done setContentVisibility will
+ // re-trigger sampling if we're visible.
+ updateSamplingState();
+ }
// If we're done animating, apply the correct visibility.
if (!animating) {
setContentVisibility(mIsContentVisible);
@@ -481,6 +558,37 @@
}
}
+ private void recreateRegionSamplingHelper() {
+ if (mRegionSamplingHelper != null) {
+ mRegionSamplingHelper.stopAndDestroy();
+ }
+ if (mMainExecutor == null || mBackgroundExecutor == null
+ || mRegionSamplingProvider == null) {
+ // Null when it's the overflow / don't need sampling then.
+ return;
+ }
+ mRegionSamplingHelper = mRegionSamplingProvider.createHelper(this,
+ new RegionSamplingHelper.SamplingCallback() {
+ @Override
+ public void onRegionDarknessChanged(boolean isRegionDark) {
+ if (mHandleView != null) {
+ mHandleView.updateHandleColor(isRegionDark,
+ true /* animated */);
+ }
+ }
+
+ @Override
+ public Rect getSampledRegion(View sampledView) {
+ return getCaptionSampleRect();
+ }
+
+ @Override
+ public boolean isSamplingEnabled() {
+ return shouldSampleRegion();
+ }
+ }, mMainExecutor, mBackgroundExecutor);
+ }
+
private class HandleViewAccessibilityDelegate extends AccessibilityDelegate {
@Override
public void onInitializeAccessibilityNodeInfo(@NonNull View host,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleView.java
index c91567d..e781c07 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarHandleView.java
@@ -42,7 +42,9 @@
private final @ColorInt int mHandleLightColor;
private final @ColorInt int mHandleDarkColor;
- private @Nullable ObjectAnimator mColorChangeAnim;
+ private @ColorInt int mCurrentColor;
+ @Nullable
+ private ObjectAnimator mColorChangeAnim;
public BubbleBarHandleView(Context context) {
this(context, null /* attrs */);
@@ -88,13 +90,17 @@
*
* @param isRegionDark Whether the background behind the handle is dark, and thus the handle
* should be light (and vice versa).
- * @param animated Whether to animate the change, or apply it immediately.
+ * @param animated Whether to animate the change, or apply it immediately.
*/
public void updateHandleColor(boolean isRegionDark, boolean animated) {
int newColor = isRegionDark ? mHandleLightColor : mHandleDarkColor;
+ if (newColor == mCurrentColor) {
+ return;
+ }
if (mColorChangeAnim != null) {
mColorChangeAnim.cancel();
}
+ mCurrentColor = newColor;
if (animated) {
mColorChangeAnim = ObjectAnimator.ofArgb(this, "backgroundColor", newColor);
mColorChangeAnim.addListener(new AnimatorListenerAdapter() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 02ecfd9..7054c17c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -38,6 +38,7 @@
import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.activityembedding.ActivityEmbeddingController;
import com.android.wm.shell.apptoweb.AppToWebGenericLinksParser;
+import com.android.wm.shell.apptoweb.AssistContentRequester;
import com.android.wm.shell.bubbles.BubbleController;
import com.android.wm.shell.bubbles.BubbleData;
import com.android.wm.shell.bubbles.BubbleDataRepository;
@@ -240,6 +241,7 @@
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
InteractionJankMonitor interactionJankMonitor,
AppToWebGenericLinksParser genericLinksParser,
+ AssistContentRequester assistContentRequester,
MultiInstanceHelper multiInstanceHelper,
Optional<DesktopTasksLimiter> desktopTasksLimiter,
Optional<DesktopActivityOrientationChangeHandler> desktopActivityOrientationHandler) {
@@ -263,6 +265,7 @@
rootTaskDisplayAreaOrganizer,
interactionJankMonitor,
genericLinksParser,
+ assistContentRequester,
multiInstanceHelper,
desktopTasksLimiter,
desktopActivityOrientationHandler);
@@ -291,6 +294,15 @@
return new AppToWebGenericLinksParser(context, mainExecutor);
}
+ @Provides
+ static AssistContentRequester provideAssistContentRequester(
+ Context context,
+ @ShellMainThread ShellExecutor shellExecutor,
+ @ShellBackgroundThread ShellExecutor bgExecutor
+ ) {
+ return new AssistContentRequester(context, shellExecutor, bgExecutor);
+ }
+
//
// Freeform
//
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java
deleted file mode 100644
index 1dad413..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/MainStage.java
+++ /dev/null
@@ -1,85 +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.splitscreen;
-
-import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN;
-
-import android.content.Context;
-import android.view.SurfaceSession;
-import android.window.WindowContainerToken;
-import android.window.WindowContainerTransaction;
-
-import com.android.internal.protolog.ProtoLog;
-import com.android.launcher3.icons.IconProvider;
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.common.SyncTransactionQueue;
-import com.android.wm.shell.windowdecor.WindowDecorViewModel;
-
-import java.util.Optional;
-
-/**
- * Main stage for split-screen mode. When split-screen is active all standard activity types launch
- * on the main stage, except for task that are explicitly pinned to the {@link StageTaskListener}.
- * @see StageCoordinator
- */
-class MainStage extends StageTaskListener {
- private boolean mIsActive = false;
-
- MainStage(Context context, ShellTaskOrganizer taskOrganizer, int displayId,
- StageListenerCallbacks callbacks, SyncTransactionQueue syncQueue,
- SurfaceSession surfaceSession, IconProvider iconProvider,
- Optional<WindowDecorViewModel> windowDecorViewModel) {
- super(context, taskOrganizer, displayId, callbacks, syncQueue, surfaceSession,
- iconProvider, windowDecorViewModel);
- }
-
- boolean isActive() {
- return mIsActive;
- }
-
- void activate(WindowContainerTransaction wct, boolean includingTopTask) {
- if (mIsActive) return;
- ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "activate: main stage includingTopTask=%b",
- includingTopTask);
-
- if (includingTopTask) {
- reparentTopTask(wct);
- }
-
- mIsActive = true;
- }
-
- void deactivate(WindowContainerTransaction wct) {
- deactivate(wct, false /* toTop */);
- }
-
- void deactivate(WindowContainerTransaction wct, boolean toTop) {
- if (!mIsActive) return;
- ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "deactivate: main stage toTop=%b rootTaskInfo=%s",
- toTop, mRootTaskInfo);
- mIsActive = false;
-
- if (mRootTaskInfo == null) return;
- final WindowContainerToken rootToken = mRootTaskInfo.token;
- wct.reparentTasks(
- rootToken,
- null /* newParent */,
- null /* windowingModes */,
- null /* activityTypes */,
- toTop);
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
index 526c1d4..b36b1f8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreen.java
@@ -44,7 +44,7 @@
int STAGE_TYPE_UNDEFINED = -1;
/**
* The main stage type.
- * @see MainStage
+ * @see StageTaskListener
*/
int STAGE_TYPE_MAIN = 0;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index f3959cc..f3113dc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -34,6 +34,7 @@
import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER;
+import static com.android.wm.shell.Flags.enableFlexibleSplit;
import static com.android.wm.shell.common.split.SplitLayout.PARALLAX_ALIGN_CENTER;
import static com.android.wm.shell.common.split.SplitScreenUtils.reverseSplitPosition;
import static com.android.wm.shell.common.split.SplitScreenUtils.splitFailureMessage;
@@ -154,14 +155,12 @@
import java.util.concurrent.Executor;
/**
- * Coordinates the staging (visibility, sizing, ...) of the split-screen {@link MainStage} and
- * other stages.
+ * Coordinates the staging (visibility, sizing, ...) of the split-screen stages.
* Some high-level rules:
* - The {@link StageCoordinator} is only considered active if the other stages contain at
* least one child task.
- * - The {@link MainStage} should only have children if the coordinator is active.
- * - The {@link SplitLayout} divider is only visible if both the {@link MainStage}
- * and other stages are visible.
+ * - The {@link SplitLayout} divider is only visible if multiple {@link StageTaskListener}s are
+ * visible
* - Both stages are put under a single-top root task.
* This rules are mostly implemented in {@link #onStageVisibilityChanged(StageListenerImpl)} and
* {@link #onStageHasChildrenChanged(StageListenerImpl).}
@@ -174,7 +173,7 @@
private final SurfaceSession mSurfaceSession = new SurfaceSession();
- private final MainStage mMainStage;
+ private final StageTaskListener mMainStage;
private final StageListenerImpl mMainStageListener = new StageListenerImpl();
private final StageTaskListener mSideStage;
private final StageListenerImpl mSideStageListener = new StageListenerImpl();
@@ -329,7 +328,7 @@
taskOrganizer.createRootTask(displayId, WINDOWING_MODE_FULLSCREEN, this /* listener */);
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "Creating main/side root task");
- mMainStage = new MainStage(
+ mMainStage = new StageTaskListener(
mContext,
mTaskOrganizer,
mDisplayId,
@@ -367,8 +366,9 @@
@VisibleForTesting
StageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
- ShellTaskOrganizer taskOrganizer, MainStage mainStage, StageTaskListener sideStage,
- DisplayController displayController, DisplayImeController displayImeController,
+ ShellTaskOrganizer taskOrganizer, StageTaskListener mainStage,
+ StageTaskListener sideStage, DisplayController displayController,
+ DisplayImeController displayImeController,
DisplayInsetsController displayInsetsController, SplitLayout splitLayout,
Transitions transitions, TransactionPool transactionPool, ShellExecutor mainExecutor,
Handler mainHandler, Optional<RecentTasksController> recentTasks,
@@ -420,6 +420,10 @@
return mSideStageListener.mVisible && mMainStageListener.mVisible;
}
+ private void activateSplit(WindowContainerTransaction wct, boolean includingTopTask) {
+ mMainStage.activate(wct, includingTopTask);
+ }
+
public boolean isSplitActive() {
return mMainStage.isActive();
}
@@ -505,10 +509,10 @@
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "removeFromSideStage: task=%d", taskId);
final WindowContainerTransaction wct = new WindowContainerTransaction();
- /**
- * {@link MainStage} will be deactivated in {@link #onStageHasChildrenChanged} if the
- * other stages no longer have children.
- */
+
+ // MainStage will be deactivated in onStageHasChildrenChanged() if the other stages
+ // no longer have children.
+
final boolean result = mSideStage.removeTask(taskId,
isSplitActive() ? mMainStage.mRootTaskInfo.token : null,
wct);
@@ -805,7 +809,7 @@
if (!isSplitActive()) {
// Build a request WCT that will launch both apps such that task 0 is on the main stage
// while task 1 is on the side stage.
- mMainStage.activate(wct, false /* reparent */);
+ activateSplit(wct, false /* reparentToTop */);
}
mSplitLayout.setDivideRatio(snapPosition);
updateWindowBounds(mSplitLayout, wct);
@@ -872,7 +876,7 @@
if (!isSplitActive()) {
// Build a request WCT that will launch both apps such that task 0 is on the main stage
// while task 1 is on the side stage.
- mMainStage.activate(wct, false /* reparent */);
+ activateSplit(wct, false /* reparentToTop */);
}
setSideStagePosition(splitPosition, wct);
@@ -1439,7 +1443,7 @@
setSideStagePosition(startPosition, wct);
mSideStage.addTask(taskInfo, wct);
}
- mMainStage.activate(wct, true /* includingTopTask */);
+ activateSplit(wct, true /* reparentToTop */);
prepareSplitLayout(wct, resizeAnim);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
index 4593553..29b5114 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageTaskListener.java
@@ -72,6 +72,10 @@
public class StageTaskListener implements ShellTaskOrganizer.TaskListener {
private static final String TAG = StageTaskListener.class.getSimpleName();
+ // No current way to enforce this but if enableFlexibleSplit() is enabled, then only 1 of the
+ // stages should have this be set/being used
+ private boolean mIsActive;
+
/** Callback interface for listening to changes in a split-screen stage. */
public interface StageListenerCallbacks {
void onRootTaskAppeared();
@@ -475,6 +479,44 @@
});
}
+ // ---------
+ // Previously only used in MainStage
+ boolean isActive() {
+ return mIsActive;
+ }
+
+ void activate(WindowContainerTransaction wct, boolean includingTopTask) {
+ if (mIsActive) return;
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "activate: includingTopTask=%b",
+ includingTopTask);
+
+ if (includingTopTask) {
+ reparentTopTask(wct);
+ }
+
+ mIsActive = true;
+ }
+
+ void deactivate(WindowContainerTransaction wct) {
+ deactivate(wct, false /* toTop */);
+ }
+
+ void deactivate(WindowContainerTransaction wct, boolean toTop) {
+ if (!mIsActive) return;
+ ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "deactivate: toTop=%b rootTaskInfo=%s",
+ toTop, mRootTaskInfo);
+ mIsActive = false;
+
+ if (mRootTaskInfo == null) return;
+ final WindowContainerToken rootToken = mRootTaskInfo.token;
+ wct.reparentTasks(
+ rootToken,
+ null /* newParent */,
+ null /* windowingModes */,
+ null /* activityTypes */,
+ toTop);
+ }
+
// --------
// Previously only used in SideStage
boolean removeAllTasks(WindowContainerTransaction wct, boolean toTop) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
index c88c1e2..7919068 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java
@@ -90,6 +90,7 @@
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.apptoweb.AppToWebGenericLinksParser;
+import com.android.wm.shell.apptoweb.AssistContentRequester;
import com.android.wm.shell.common.DisplayChangeController;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayInsetsController;
@@ -182,6 +183,7 @@
private final Region mExclusionRegion = Region.obtain();
private boolean mInImmersiveMode;
private final String mSysUIPackageName;
+ private final AssistContentRequester mAssistContentRequester;
private final DisplayChangeController.OnDisplayChangingListener mOnDisplayChangingListener;
private final ISystemGestureExclusionListener mGestureExclusionListener =
@@ -217,6 +219,7 @@
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
InteractionJankMonitor interactionJankMonitor,
AppToWebGenericLinksParser genericLinksParser,
+ AssistContentRequester assistContentRequester,
MultiInstanceHelper multiInstanceHelper,
Optional<DesktopTasksLimiter> desktopTasksLimiter,
Optional<DesktopActivityOrientationChangeHandler> activityOrientationChangeHandler
@@ -238,6 +241,7 @@
transitions,
desktopTasksController,
genericLinksParser,
+ assistContentRequester,
multiInstanceHelper,
new DesktopModeWindowDecoration.Factory(),
new InputMonitorFactory(),
@@ -267,6 +271,7 @@
Transitions transitions,
Optional<DesktopTasksController> desktopTasksController,
AppToWebGenericLinksParser genericLinksParser,
+ AssistContentRequester assistContentRequester,
MultiInstanceHelper multiInstanceHelper,
DesktopModeWindowDecoration.Factory desktopModeWindowDecorFactory,
InputMonitorFactory inputMonitorFactory,
@@ -304,6 +309,7 @@
mInteractionJankMonitor = interactionJankMonitor;
mDesktopTasksLimiter = desktopTasksLimiter;
mActivityOrientationChangeHandler = activityOrientationChangeHandler;
+ mAssistContentRequester = assistContentRequester;
mOnDisplayChangingListener = (displayId, fromRotation, toRotation, displayAreaInfo, t) -> {
DesktopModeWindowDecoration decoration;
RunningTaskInfo taskInfo;
@@ -626,7 +632,7 @@
} else if (id == R.id.caption_handle || id == R.id.open_menu_button) {
if (!decoration.isHandleMenuActive()) {
moveTaskToFront(decoration.mTaskInfo);
- decoration.createHandleMenu(mSplitScreenController);
+ decoration.createHandleMenu();
}
} else if (id == R.id.maximize_window) {
// TODO(b/346441962): move click detection logic into the decor's
@@ -1270,6 +1276,7 @@
mSyncQueue,
mRootTaskDisplayAreaOrganizer,
mGenericLinksParser,
+ mAssistContentRequester,
mMultiInstanceHelper);
mWindowDecorByTaskId.put(taskInfo.taskId, windowDecoration);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
index aa43c8d..142be91 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecoration.java
@@ -38,6 +38,7 @@
import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.WindowConfiguration.WindowingMode;
+import android.app.assist.AssistContent;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ActivityInfo;
@@ -75,6 +76,8 @@
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.apptoweb.AppToWebGenericLinksParser;
+import com.android.wm.shell.apptoweb.AppToWebUtils;
+import com.android.wm.shell.apptoweb.AssistContentRequester;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.MultiInstanceHelper;
@@ -150,6 +153,7 @@
private CharSequence mAppName;
private CapturedLink mCapturedLink;
private Uri mGenericLink;
+ private Uri mWebUri;
private Consumer<Uri> mOpenInBrowserClickListener;
private ExclusionRegionListener mExclusionRegionListener;
@@ -158,6 +162,7 @@
private final MaximizeMenuFactory mMaximizeMenuFactory;
private final HandleMenuFactory mHandleMenuFactory;
private final AppToWebGenericLinksParser mGenericLinksParser;
+ private final AssistContentRequester mAssistContentRequester;
// Hover state for the maximize menu and button. The menu will remain open as long as either of
// these is true. See {@link #onMaximizeHoverStateChanged()}.
@@ -184,16 +189,16 @@
SyncTransactionQueue syncQueue,
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
AppToWebGenericLinksParser genericLinksParser,
+ AssistContentRequester assistContentRequester,
MultiInstanceHelper multiInstanceHelper) {
this (context, userContext, displayController, splitScreenController, taskOrganizer,
taskInfo, taskSurface, handler, bgExecutor, choreographer, syncQueue,
- rootTaskDisplayAreaOrganizer, genericLinksParser, SurfaceControl.Builder::new,
- SurfaceControl.Transaction::new, WindowContainerTransaction::new,
- SurfaceControl::new, new WindowManagerWrapper(
+ rootTaskDisplayAreaOrganizer, genericLinksParser, assistContentRequester,
+ SurfaceControl.Builder::new, SurfaceControl.Transaction::new,
+ WindowContainerTransaction::new, SurfaceControl::new, new WindowManagerWrapper(
context.getSystemService(WindowManager.class)),
- new SurfaceControlViewHostFactory() {},
- DefaultMaximizeMenuFactory.INSTANCE, DefaultHandleMenuFactory.INSTANCE,
- multiInstanceHelper);
+ new SurfaceControlViewHostFactory() {}, DefaultMaximizeMenuFactory.INSTANCE,
+ DefaultHandleMenuFactory.INSTANCE, multiInstanceHelper);
}
DesktopModeWindowDecoration(
@@ -210,6 +215,7 @@
SyncTransactionQueue syncQueue,
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
AppToWebGenericLinksParser genericLinksParser,
+ AssistContentRequester assistContentRequester,
Supplier<SurfaceControl.Builder> surfaceControlBuilderSupplier,
Supplier<SurfaceControl.Transaction> surfaceControlTransactionSupplier,
Supplier<WindowContainerTransaction> windowContainerTransactionSupplier,
@@ -230,6 +236,7 @@
mSyncQueue = syncQueue;
mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer;
mGenericLinksParser = genericLinksParser;
+ mAssistContentRequester = assistContentRequester;
mMaximizeMenuFactory = maximizeMenuFactory;
mHandleMenuFactory = handleMenuFactory;
mMultiInstanceHelper = multiInstanceHelper;
@@ -478,10 +485,18 @@
@Nullable
private Uri getBrowserLink() {
+ // Do not show browser link in browser applications
+ final ComponentName baseActivity = mTaskInfo.baseActivity;
+ if (baseActivity != null && AppToWebUtils.isBrowserApp(mContext,
+ baseActivity.getPackageName(), mUserContext.getUserId())) {
+ return null;
+ }
// If the captured link is available and has not expired, return the captured link.
// Otherwise, return the generic link which is set to null if a generic link is unavailable.
if (mCapturedLink != null && !mCapturedLink.mExpired) {
return mCapturedLink.mUri;
+ } else if (mWebUri != null) {
+ return mWebUri;
}
return mGenericLink;
}
@@ -987,18 +1002,32 @@
}
/**
- * Create and display handle menu window.
+ * Updates app info and creates and displays handle menu window.
*/
- void createHandleMenu(SplitScreenController splitScreenController) {
+ void createHandleMenu() {
+ // Requests assist content. When content is received, calls {@link #onAssistContentReceived}
+ // which sets app info and creates the handle menu.
+ mAssistContentRequester.requestAssistContent(
+ mTaskInfo.taskId, this::onAssistContentReceived);
+ }
+
+ /**
+ * Called when assist content is received. updates the saved links and creates the handle menu.
+ */
+ @VisibleForTesting
+ void onAssistContentReceived(@Nullable AssistContent assistContent) {
+ mWebUri = assistContent == null ? null : assistContent.getWebUri();
loadAppInfoIfNeeded();
updateGenericLink();
+
+ // Create and display handle menu
mHandleMenu = mHandleMenuFactory.create(
this,
mWindowManagerWrapper,
mRelayoutParams.mLayoutResId,
mAppIconBitmap,
mAppName,
- splitScreenController,
+ mSplitScreenController,
DesktopModeStatus.canEnterDesktopMode(mContext),
Flags.enableDesktopWindowingMultiInstanceFeatures()
&& mMultiInstanceHelper
@@ -1012,6 +1041,7 @@
mHandleMenu.show(
/* onToDesktopClickListener= */ () -> {
mOnToDesktopClickListener.accept(APP_HANDLE_MENU_BUTTON);
+ mOnToDesktopClickListener.accept(APP_HANDLE_MENU_BUTTON);
return Unit.INSTANCE;
},
/* onToFullscreenClickListener= */ mOnToFullscreenClickListener,
@@ -1333,6 +1363,7 @@
SyncTransactionQueue syncQueue,
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
AppToWebGenericLinksParser genericLinksParser,
+ AssistContentRequester assistContentRequester,
MultiInstanceHelper multiInstanceHelper) {
return new DesktopModeWindowDecoration(
context,
@@ -1348,6 +1379,7 @@
syncQueue,
rootTaskDisplayAreaOrganizer,
genericLinksParser,
+ assistContentRequester,
multiInstanceHelper);
}
}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt
index ec1d4f7..7640cb1 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/DesktopModeFlickerScenarios.kt
@@ -195,6 +195,25 @@
.associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
)
+ val CORNER_RESIZE_TO_MAXIMUM_SIZE =
+ FlickerConfigEntry(
+ scenarioId = ScenarioId("CORNER_RESIZE_TO_MAXIMUM_SIZE"),
+ extractor =
+ TaggedScenarioExtractorBuilder()
+ .setTargetTag(CujType.CUJ_DESKTOP_MODE_RESIZE_WINDOW)
+ .setTransitionMatcher(
+ TaggedCujTransitionMatcher(associatedTransitionRequired = false)
+ )
+ .build(),
+ assertions =
+ AssertionTemplates.DESKTOP_MODE_APP_VISIBILITY_ASSERTIONS +
+ listOf(
+ AppLayerIncreasesInSize(DESKTOP_MODE_APP),
+ AppWindowHasMaxDisplayHeight(DESKTOP_MODE_APP),
+ AppWindowHasMaxDisplayWidth(DESKTOP_MODE_APP)
+ ).associateBy({ it }, { AssertionInvocationGroup.BLOCKING }),
+ )
+
val SNAP_RESIZE_LEFT_WITH_BUTTON =
FlickerConfigEntry(
scenarioId = ScenarioId("SNAP_RESIZE_LEFT_WITH_BUTTON"),
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppToMaximumWindowSizeLandscape.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppToMaximumWindowSizeLandscape.kt
new file mode 100644
index 0000000..0b98ba2
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppToMaximumWindowSizeLandscape.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker
+
+import android.tools.Rotation
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.CORNER_RESIZE_TO_MAXIMUM_SIZE
+import com.android.wm.shell.scenarios.ResizeAppWithCornerResize
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Resize app window using corner resize to the greatest possible height and width in
+ * landscape mode.
+ *
+ * Assert that the maximum window size constraint is maintained.
+ */
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class ResizeAppToMaximumWindowSizeLandscape : ResizeAppWithCornerResize(
+ rotation = Rotation.ROTATION_90
+) {
+ @ExpectedScenarios(["CORNER_RESIZE_TO_MAXIMUM_SIZE"])
+ @Test
+ override fun resizeAppWithCornerResizeToMaximumSize() =
+ super.resizeAppWithCornerResizeToMaximumSize()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(CORNER_RESIZE_TO_MAXIMUM_SIZE)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppToMaximumWindowSizePortrait.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppToMaximumWindowSizePortrait.kt
new file mode 100644
index 0000000..b1c04d3
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/flicker-service/src/com/android/wm/shell/flicker/ResizeAppToMaximumWindowSizePortrait.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker
+
+import android.tools.flicker.FlickerConfig
+import android.tools.flicker.annotation.ExpectedScenarios
+import android.tools.flicker.annotation.FlickerConfigProvider
+import android.tools.flicker.config.FlickerConfig
+import android.tools.flicker.config.FlickerServiceConfig
+import android.tools.flicker.junit.FlickerServiceJUnit4ClassRunner
+import com.android.wm.shell.flicker.DesktopModeFlickerScenarios.Companion.CORNER_RESIZE_TO_MAXIMUM_SIZE
+import com.android.wm.shell.scenarios.ResizeAppWithCornerResize
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Resize app window using corner resize to the greatest possible height and width in
+ * portrait mode.
+ *
+ * Assert that the maximum window size constraint is maintained.
+ */
+@RunWith(FlickerServiceJUnit4ClassRunner::class)
+class ResizeAppToMaximumWindowSizePortrait : ResizeAppWithCornerResize() {
+ @ExpectedScenarios(["CORNER_RESIZE_TO_MAXIMUM_SIZE"])
+ @Test
+ override fun resizeAppWithCornerResizeToMaximumSize() =
+ super.resizeAppWithCornerResizeToMaximumSize()
+
+ companion object {
+ @JvmStatic
+ @FlickerConfigProvider
+ fun flickerConfigProvider(): FlickerConfig =
+ FlickerConfig().use(FlickerServiceConfig.DEFAULT).use(CORNER_RESIZE_TO_MAXIMUM_SIZE)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/CloseAllAppsWithAppHeaderExitTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/CloseAllAppsWithAppHeaderExitTest.kt
new file mode 100644
index 0000000..a4dc52b
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/CloseAllAppsWithAppHeaderExitTest.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.functional
+
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.CloseAllAppsWithAppHeaderExit
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/* Functional test for [CloseAllAppsWithAppHeaderExit]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class CloseAllAppsWithAppHeaderExitTest() : CloseAllAppsWithAppHeaderExit()
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/EnterDesktopWithDragTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/EnterDesktopWithDragTest.kt
new file mode 100644
index 0000000..3d95f97
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/EnterDesktopWithDragTest.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.functional
+
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.EnterDesktopWithDrag
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/* Functional test for [EnterDesktopWithDrag]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class EnterDesktopWithDragTest : EnterDesktopWithDrag()
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ExitDesktopWithDragToTopDragZoneTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ExitDesktopWithDragToTopDragZoneTest.kt
new file mode 100644
index 0000000..140c5ec
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ExitDesktopWithDragToTopDragZoneTest.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.functional
+
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.ExitDesktopWithDragToTopDragZone
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/* Functional test for [ExitDesktopWithDragToTopDragZone]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class ExitDesktopWithDragToTopDragZoneTest : ExitDesktopWithDragToTopDragZone()
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/MaximizeAppWindowTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/MaximizeAppWindowTest.kt
new file mode 100644
index 0000000..3d3dcd0
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/MaximizeAppWindowTest.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.functional
+
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.MaximizeAppWindow
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/* Functional test for [MaximizeAppWindow]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class MaximizeAppWindowTest : MaximizeAppWindow()
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/OpenAppsInDesktopModeTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/OpenAppsInDesktopModeTest.kt
new file mode 100644
index 0000000..263e89f6
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/OpenAppsInDesktopModeTest.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.functional
+
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.OpenAppsInDesktopMode
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/* Functional test for [OpenAppsInDesktopMode]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class OpenAppsInDesktopModeTest : OpenAppsInDesktopMode()
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ResizeAppCornerMultiWindowAndPipTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ResizeAppCornerMultiWindowAndPipTest.kt
new file mode 100644
index 0000000..13f4775
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ResizeAppCornerMultiWindowAndPipTest.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.functional
+
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.ResizeAppCornerMultiWindowAndPip
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/* Functional test for [ResizeAppCornerMultiWindowAndPip]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class ResizeAppCornerMultiWindowAndPipTest : ResizeAppCornerMultiWindowAndPip()
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ResizeAppCornerMultiWindowTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ResizeAppCornerMultiWindowTest.kt
new file mode 100644
index 0000000..bc9bb41
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ResizeAppCornerMultiWindowTest.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.functional
+
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.ResizeAppCornerMultiWindow
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/* Functional test for [ResizeAppCornerMultiWindow]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class ResizeAppCornerMultiWindowTest : ResizeAppCornerMultiWindow()
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ResizeAppWithCornerResizeTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ResizeAppWithCornerResizeTest.kt
new file mode 100644
index 0000000..46168eb
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ResizeAppWithCornerResizeTest.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.functional
+
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.ResizeAppWithCornerResize
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/* Functional test for [ResizeAppWithCornerResize]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class ResizeAppWithCornerResizeTest : ResizeAppWithCornerResize()
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ResizeAppWithEdgeResizeTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ResizeAppWithEdgeResizeTest.kt
new file mode 100644
index 0000000..ee24200
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/ResizeAppWithEdgeResizeTest.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.functional
+
+import android.platform.test.annotations.Postsubmit
+import com.android.server.wm.flicker.helpers.MotionEventHelper
+import com.android.wm.shell.scenarios.ResizeAppWithEdgeResize
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/* Functional test for [ResizeAppWithEdgeResize]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class ResizeAppWithEdgeResizeTest :
+ ResizeAppWithEdgeResize(MotionEventHelper.InputMethod.TOUCHPAD)
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/SnapResizeAppWindowWithButtonTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/SnapResizeAppWindowWithButtonTest.kt
new file mode 100644
index 0000000..38e85c7
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/SnapResizeAppWindowWithButtonTest.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.functional
+
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.SnapResizeAppWindowWithButton
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/* Functional test for [SnapResizeAppWindowWithButton]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class SnapResizeAppWindowWithButtonTest : SnapResizeAppWindowWithButton()
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/SnapResizeAppWindowWithDragTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/SnapResizeAppWindowWithDragTest.kt
new file mode 100644
index 0000000..082a3fb
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/SnapResizeAppWindowWithDragTest.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.functional
+
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.SnapResizeAppWindowWithDrag
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/* Functional test for [SnapResizeAppWindowWithDrag]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class SnapResizeAppWindowWithDragTest : SnapResizeAppWindowWithDrag()
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/SwitchToOverviewFromDesktopTest.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/SwitchToOverviewFromDesktopTest.kt
new file mode 100644
index 0000000..fdd0d81
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/functional/SwitchToOverviewFromDesktopTest.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.functional
+
+import android.platform.test.annotations.Postsubmit
+import com.android.wm.shell.scenarios.SwitchToOverviewFromDesktop
+import org.junit.runner.RunWith
+import org.junit.runners.BlockJUnit4ClassRunner
+
+/* Functional test for [SwitchToOverviewFromDesktop]. */
+@RunWith(BlockJUnit4ClassRunner::class)
+@Postsubmit
+class SwitchToOverviewFromDesktopTest : SwitchToOverviewFromDesktop()
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/CloseAllAppsWithAppHeaderExit.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/CloseAllAppsWithAppHeaderExit.kt
index e9056f3..351a700 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/CloseAllAppsWithAppHeaderExit.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/CloseAllAppsWithAppHeaderExit.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.scenarios
-import android.platform.test.annotations.Postsubmit
import android.app.Instrumentation
import android.tools.NavBar
import android.tools.Rotation
@@ -33,15 +32,12 @@
import org.junit.After
import org.junit.Assume
import org.junit.Before
+import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.BlockJUnit4ClassRunner
-@RunWith(BlockJUnit4ClassRunner::class)
-@Postsubmit
-open class CloseAllAppsWithAppHeaderExit
-@JvmOverloads
+@Ignore("Base Test Class")
+abstract class CloseAllAppsWithAppHeaderExit
constructor(val rotation: Rotation = Rotation.ROTATION_0) {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DragAppWindowMultiWindow.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DragAppWindowMultiWindow.kt
index ca1dc1a..3f9927f 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DragAppWindowMultiWindow.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/DragAppWindowMultiWindow.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.scenarios
-import android.platform.test.annotations.Postsubmit
import com.android.server.wm.flicker.helpers.DesktopModeAppHelper
import com.android.server.wm.flicker.helpers.ImeAppHelper
import com.android.server.wm.flicker.helpers.MailAppHelper
@@ -26,13 +25,11 @@
import org.junit.After
import org.junit.Assume
import org.junit.Before
+import org.junit.Ignore
import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.BlockJUnit4ClassRunner
-@RunWith(BlockJUnit4ClassRunner::class)
-@Postsubmit
-open class DragAppWindowMultiWindow : DragAppWindowScenarioTestBase()
+@Ignore("Test Base Class")
+abstract class DragAppWindowMultiWindow : DragAppWindowScenarioTestBase()
{
private val imeAppHelper = ImeAppHelper(instrumentation)
private val testApp = DesktopModeAppHelper(SimpleAppHelper(instrumentation))
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithDrag.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithDrag.kt
index f4d6414..967bd29 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithDrag.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/EnterDesktopWithDrag.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.scenarios
-import android.platform.test.annotations.Postsubmit
import android.tools.NavBar
import android.tools.Rotation
import android.tools.flicker.rules.ChangeDisplayOrientationRule
@@ -25,19 +24,16 @@
import org.junit.After
import org.junit.Assume
import org.junit.Before
+import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.BlockJUnit4ClassRunner
-@RunWith(BlockJUnit4ClassRunner::class)
-@Postsubmit
-open class EnterDesktopWithDrag
-@JvmOverloads
+@Ignore("Test Base Class")
+abstract class EnterDesktopWithDrag
constructor(
val rotation: Rotation = Rotation.ROTATION_0,
isResizeable: Boolean = true,
- isLandscapeApp: Boolean = true
+ isLandscapeApp: Boolean = true,
) : DesktopScenarioCustomAppTestBase(isResizeable, isLandscapeApp) {
@Rule @JvmField val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation)
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ExitDesktopWithDragToTopDragZone.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ExitDesktopWithDragToTopDragZone.kt
index b616e53..824c448 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ExitDesktopWithDragToTopDragZone.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ExitDesktopWithDragToTopDragZone.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.scenarios
-import android.platform.test.annotations.Postsubmit
import android.tools.NavBar
import android.tools.Rotation
import com.android.window.flags.Flags
@@ -24,19 +23,16 @@
import org.junit.After
import org.junit.Assume
import org.junit.Before
+import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.BlockJUnit4ClassRunner
-@RunWith(BlockJUnit4ClassRunner::class)
-@Postsubmit
-open class ExitDesktopWithDragToTopDragZone
-@JvmOverloads
+@Ignore("Test Base Class")
+abstract class ExitDesktopWithDragToTopDragZone
constructor(
val rotation: Rotation = Rotation.ROTATION_0,
isResizeable: Boolean = true,
- isLandscapeApp: Boolean = true
+ isLandscapeApp: Boolean = true,
) : DesktopScenarioCustomAppTestBase(isResizeable, isLandscapeApp) {
@Rule @JvmField val testSetupRule = Utils.testSetupRule(NavBar.MODE_GESTURAL, rotation)
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/OpenAppsInDesktopMode.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/OpenAppsInDesktopMode.kt
index a5c794b..aad266f 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/OpenAppsInDesktopMode.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/OpenAppsInDesktopMode.kt
@@ -17,7 +17,6 @@
package com.android.wm.shell.scenarios
import android.app.Instrumentation
-import android.platform.test.annotations.Postsubmit
import android.tools.flicker.rules.ChangeDisplayOrientationRule
import android.tools.NavBar
import android.tools.Rotation
@@ -36,14 +35,12 @@
import org.junit.After
import org.junit.Assume
import org.junit.Before
+import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.BlockJUnit4ClassRunner
-@RunWith(BlockJUnit4ClassRunner::class)
-@Postsubmit
-open class OpenAppsInDesktopMode(val rotation: Rotation = Rotation.ROTATION_0) {
+@Ignore("Test Base Class")
+abstract class OpenAppsInDesktopMode(val rotation: Rotation = Rotation.ROTATION_0) {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
private val tapl = LauncherInstrumentation()
@@ -83,4 +80,4 @@
secondApp.exit(wmHelper)
firstApp.exit(wmHelper)
}
-}
\ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppCornerMultiWindow.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppCornerMultiWindow.kt
index b6bca7a..bfee318 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppCornerMultiWindow.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppCornerMultiWindow.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.scenarios
-import android.platform.test.annotations.Postsubmit
import android.app.Instrumentation
import android.tools.NavBar
import android.tools.Rotation
@@ -34,15 +33,12 @@
import org.junit.After
import org.junit.Assume
import org.junit.Before
+import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.BlockJUnit4ClassRunner
-@RunWith(BlockJUnit4ClassRunner::class)
-@Postsubmit
-open class ResizeAppCornerMultiWindow
-@JvmOverloads
+@Ignore("Test Base Class")
+abstract class ResizeAppCornerMultiWindow
constructor(val rotation: Rotation = Rotation.ROTATION_0,
val horizontalChange: Int = 50,
val verticalChange: Int = -50) {
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppCornerMultiWindowAndPip.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppCornerMultiWindowAndPip.kt
index 285ea13..5b1b64e 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppCornerMultiWindowAndPip.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppCornerMultiWindowAndPip.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.scenarios
-import android.platform.test.annotations.Postsubmit
import android.app.Instrumentation
import android.tools.NavBar
import android.tools.Rotation
@@ -35,15 +34,12 @@
import org.junit.After
import org.junit.Assume
import org.junit.Before
+import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.BlockJUnit4ClassRunner
-@RunWith(BlockJUnit4ClassRunner::class)
-@Postsubmit
-open class ResizeAppCornerMultiWindowAndPip
-@JvmOverloads
+@Ignore("Test Base Class")
+abstract class ResizeAppCornerMultiWindowAndPip
constructor(val rotation: Rotation = Rotation.ROTATION_0,
val horizontalChange: Int = 50,
val verticalChange: Int = -50) {
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithCornerResize.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithCornerResize.kt
index 42940a9..bd25639 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithCornerResize.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithCornerResize.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.scenarios
-import android.platform.test.annotations.Postsubmit
import android.app.Instrumentation
import android.tools.NavBar
import android.tools.Rotation
@@ -32,16 +31,12 @@
import org.junit.After
import org.junit.Assume
import org.junit.Before
+import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.BlockJUnit4ClassRunner
-@RunWith(BlockJUnit4ClassRunner::class)
-@Postsubmit
-open class ResizeAppWithCornerResize
-@JvmOverloads
-constructor(
+@Ignore("Test Base Class")
+abstract class ResizeAppWithCornerResize(
val rotation: Rotation = Rotation.ROTATION_0,
val horizontalChange: Int = 200,
val verticalChange: Int = -200,
@@ -83,6 +78,25 @@
)
}
+ @Test
+ open fun resizeAppWithCornerResizeToMaximumSize() {
+ val maxResizeChange = 3000
+ testApp.cornerResize(
+ wmHelper,
+ device,
+ DesktopModeAppHelper.Corners.RIGHT_TOP,
+ maxResizeChange,
+ -maxResizeChange
+ )
+ testApp.cornerResize(
+ wmHelper,
+ device,
+ DesktopModeAppHelper.Corners.LEFT_BOTTOM,
+ -maxResizeChange,
+ maxResizeChange
+ )
+ }
+
@After
fun teardown() {
testApp.exit(wmHelper)
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithEdgeResize.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithEdgeResize.kt
index d094967..6780238 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithEdgeResize.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/ResizeAppWithEdgeResize.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.scenarios
-import android.platform.test.annotations.Postsubmit
import android.app.Instrumentation
import android.tools.NavBar
import android.tools.Rotation
@@ -32,15 +31,12 @@
import org.junit.After
import org.junit.Assume
import org.junit.Before
+import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.BlockJUnit4ClassRunner
-@RunWith(BlockJUnit4ClassRunner::class)
-@Postsubmit
-open class ResizeAppWithEdgeResize
-@JvmOverloads
+@Ignore("Test Base Class")
+abstract class ResizeAppWithEdgeResize
constructor(
val inputMethod: MotionEventHelper.InputMethod,
val rotation: Rotation = Rotation.ROTATION_90
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithButton.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithButton.kt
index 33242db..2b40497 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithButton.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithButton.kt
@@ -17,7 +17,6 @@
package com.android.wm.shell.scenarios
import android.app.Instrumentation
-import android.platform.test.annotations.Postsubmit
import android.tools.NavBar
import android.tools.Rotation
import android.tools.traces.parsers.WindowManagerStateHelper
@@ -32,15 +31,12 @@
import org.junit.After
import org.junit.Assume
import org.junit.Before
+import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.BlockJUnit4ClassRunner
-@RunWith(BlockJUnit4ClassRunner::class)
-@Postsubmit
-open class SnapResizeAppWindowWithButton
-@JvmOverloads
+@Ignore("Test Base Class")
+abstract class SnapResizeAppWindowWithButton
constructor(private val toLeft: Boolean = true, isResizable: Boolean = true) {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithDrag.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithDrag.kt
index 14eb779..b4bd7e1 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithDrag.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SnapResizeAppWindowWithDrag.kt
@@ -17,7 +17,6 @@
package com.android.wm.shell.scenarios
import android.app.Instrumentation
-import android.platform.test.annotations.Postsubmit
import android.tools.NavBar
import android.tools.Rotation
import android.tools.traces.parsers.WindowManagerStateHelper
@@ -32,15 +31,12 @@
import org.junit.After
import org.junit.Assume
import org.junit.Before
+import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.BlockJUnit4ClassRunner
-@RunWith(BlockJUnit4ClassRunner::class)
-@Postsubmit
-open class SnapResizeAppWindowWithDrag
-@JvmOverloads
+@Ignore("Test Base Class")
+abstract class SnapResizeAppWindowWithDrag
constructor(private val toLeft: Boolean = true, isResizable: Boolean = true) {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
@@ -72,4 +68,4 @@
fun teardown() {
testApp.exit(wmHelper)
}
-}
\ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SwitchToOverviewFromDesktop.kt b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SwitchToOverviewFromDesktop.kt
index 53e36e23..dad2eb6 100644
--- a/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SwitchToOverviewFromDesktop.kt
+++ b/libs/WindowManager/Shell/tests/e2e/desktopmode/scenarios/src/com/android/wm/shell/scenarios/SwitchToOverviewFromDesktop.kt
@@ -16,7 +16,6 @@
package com.android.wm.shell.scenarios
-import android.platform.test.annotations.Postsubmit
import android.app.Instrumentation
import android.tools.NavBar
import android.tools.Rotation
@@ -31,20 +30,17 @@
import org.junit.After
import org.junit.Assume
import org.junit.Before
+import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.runners.BlockJUnit4ClassRunner
/**
* Base test for opening recent apps overview from desktop mode.
*
* Navigation mode can be passed as a constructor parameter, by default it is set to gesture navigation.
*/
-@RunWith(BlockJUnit4ClassRunner::class)
-@Postsubmit
-open class SwitchToOverviewFromDesktop
-@JvmOverloads
+@Ignore("Base Test Class")
+abstract class SwitchToOverviewFromDesktop
constructor(val navigationMode: NavBar = NavBar.MODE_GESTURAL) {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java
deleted file mode 100644
index b1befc4..0000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/MainStageTests.java
+++ /dev/null
@@ -1,78 +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.splitscreen;
-
-import static android.view.Display.DEFAULT_DISPLAY;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.app.ActivityManager;
-import android.view.SurfaceControl;
-import android.view.SurfaceSession;
-import android.window.WindowContainerTransaction;
-
-import androidx.test.annotation.UiThreadTest;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.launcher3.icons.IconProvider;
-import com.android.wm.shell.ShellTaskOrganizer;
-import com.android.wm.shell.ShellTestCase;
-import com.android.wm.shell.TestRunningTaskInfoBuilder;
-import com.android.wm.shell.common.SyncTransactionQueue;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.Optional;
-
-/** Tests for {@link MainStage} */
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class MainStageTests extends ShellTestCase {
- @Mock private ShellTaskOrganizer mTaskOrganizer;
- @Mock private StageTaskListener.StageListenerCallbacks mCallbacks;
- @Mock private SyncTransactionQueue mSyncQueue;
- @Mock private ActivityManager.RunningTaskInfo mRootTaskInfo;
- @Mock private SurfaceControl mRootLeash;
- @Mock private IconProvider mIconProvider;
- private WindowContainerTransaction mWct = new WindowContainerTransaction();
- private SurfaceSession mSurfaceSession = new SurfaceSession();
- private MainStage mMainStage;
-
- @Before
- @UiThreadTest
- public void setup() {
- MockitoAnnotations.initMocks(this);
- mRootTaskInfo = new TestRunningTaskInfoBuilder().build();
- mMainStage = new MainStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mCallbacks,
- mSyncQueue, mSurfaceSession, mIconProvider, Optional.empty());
- mMainStage.onTaskAppeared(mRootTaskInfo, mRootLeash);
- }
-
- @Test
- public void testActiveDeactivate() {
- mMainStage.activate(mWct, true /* reparent */);
- assertThat(mMainStage.isActive()).isTrue();
-
- mMainStage.deactivate(mWct);
- assertThat(mMainStage.isActive()).isFalse();
- }
-}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
index 4de2278..751275b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTestUtils.java
@@ -74,10 +74,10 @@
final SurfaceControl mRootLeash;
TestStageCoordinator(Context context, int displayId, SyncTransactionQueue syncQueue,
- ShellTaskOrganizer taskOrganizer, MainStage mainStage, StageTaskListener sideStage,
- DisplayController displayController, DisplayImeController imeController,
- DisplayInsetsController insetsController, SplitLayout splitLayout,
- Transitions transitions, TransactionPool transactionPool,
+ ShellTaskOrganizer taskOrganizer, StageTaskListener mainStage,
+ StageTaskListener sideStage, DisplayController displayController,
+ DisplayImeController imeController, DisplayInsetsController insetsController,
+ SplitLayout splitLayout, Transitions transitions, TransactionPool transactionPool,
ShellExecutor mainExecutor, Handler mainHandler,
Optional<RecentTasksController> recentTasks,
LaunchAdjacentController launchAdjacentController,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index e167433..af288c8 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -116,7 +116,7 @@
@Mock private SplitScreen.SplitInvocationListener mInvocationListener;
private final TestShellExecutor mTestShellExecutor = new TestShellExecutor();
private SplitLayout mSplitLayout;
- private MainStage mMainStage;
+ private StageTaskListener mMainStage;
private StageTaskListener mSideStage;
private StageCoordinator mStageCoordinator;
private SplitScreenTransitions mSplitScreenTransitions;
@@ -133,7 +133,7 @@
doReturn(mockExecutor).when(mTransitions).getAnimExecutor();
doReturn(mock(SurfaceControl.Transaction.class)).when(mTransactionPool).acquire();
mSplitLayout = SplitTestUtils.createMockSplitLayout();
- mMainStage = spy(new MainStage(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock(
+ mMainStage = spy(new StageTaskListener(mContext, mTaskOrganizer, DEFAULT_DISPLAY, mock(
StageTaskListener.StageListenerCallbacks.class), mSyncQueue, mSurfaceSession,
mIconProvider, Optional.of(mWindowDecorViewModel)));
mMainStage.onTaskAppeared(new TestRunningTaskInfoBuilder().build(), createMockSurface());
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
index c9e1414..ce343b8 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageCoordinatorTests.java
@@ -97,7 +97,7 @@
@Mock
private SyncTransactionQueue mSyncQueue;
@Mock
- private MainStage mMainStage;
+ private StageTaskListener mMainStage;
@Mock
private StageTaskListener mSideStage;
@Mock
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
index acd612e..8b5cb97 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/StageTaskListenerTests.java
@@ -199,4 +199,13 @@
assertThat(mStageTaskListener.removeTask(task.taskId, null, mWct)).isTrue();
verify(mWct).reparent(eq(task.token), isNull(), eq(false));
}
+
+ @Test
+ public void testActiveDeactivate() {
+ mStageTaskListener.activate(mWct, true /* reparent */);
+ assertThat(mStageTaskListener.isActive()).isTrue();
+
+ mStageTaskListener.deactivate(mWct);
+ assertThat(mStageTaskListener.isActive()).isFalse();
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
index be0549b..3dd8a2b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt
@@ -73,6 +73,7 @@
import com.android.wm.shell.TestRunningTaskInfoBuilder
import com.android.wm.shell.TestShellExecutor
import com.android.wm.shell.apptoweb.AppToWebGenericLinksParser
+import com.android.wm.shell.apptoweb.AssistContentRequester
import com.android.wm.shell.common.DisplayChangeController
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.DisplayInsetsController
@@ -165,6 +166,7 @@
@Mock private lateinit var mockInteractionJankMonitor: InteractionJankMonitor
@Mock private lateinit var mockGenericLinksParser: AppToWebGenericLinksParser
@Mock private lateinit var mockUserHandle: UserHandle
+ @Mock private lateinit var mockAssistContentRequester: AssistContentRequester
@Mock private lateinit var mockToast: Toast
private val bgExecutor = TestShellExecutor()
@Mock private lateinit var mockMultiInstanceHelper: MultiInstanceHelper
@@ -218,6 +220,7 @@
mockTransitions,
Optional.of(mockDesktopTasksController),
mockGenericLinksParser,
+ mockAssistContentRequester,
mockMultiInstanceHelper,
mockDesktopModeWindowDecorFactory,
mockInputMonitorFactory,
@@ -1131,7 +1134,7 @@
whenever(
mockDesktopModeWindowDecorFactory.create(
any(), any(), any(), any(), any(), eq(task), any(), any(), any(), any(), any(),
- any(), any(), any())
+ any(), any(), any(), any())
).thenReturn(decoration)
decoration.mTaskInfo = task
whenever(decoration.isFocused).thenReturn(task.isFocused)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
index 258c860..b9e542a0 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorationTests.java
@@ -46,6 +46,7 @@
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
+import android.app.assist.AssistContent;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.ActivityInfo;
@@ -88,6 +89,7 @@
import com.android.wm.shell.TestRunningTaskInfoBuilder;
import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.apptoweb.AppToWebGenericLinksParser;
+import com.android.wm.shell.apptoweb.AssistContentRequester;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.MultiInstanceHelper;
import com.android.wm.shell.common.ShellExecutor;
@@ -133,6 +135,7 @@
private static final Uri TEST_URI1 = Uri.parse("https://www.google.com/");
private static final Uri TEST_URI2 = Uri.parse("https://docs.google.com/");
+ private static final Uri TEST_URI3 = Uri.parse("https://slides.google.com/");
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
@@ -175,6 +178,8 @@
@Mock
private WindowManager mMockWindowManager;
@Mock
+ private AssistContentRequester mMockAssistContentRequester;
+ @Mock
private HandleMenu mMockHandleMenu;
@Mock
private HandleMenuFactory mMockHandleMenuFactory;
@@ -189,7 +194,8 @@
private SurfaceControl.Transaction mMockTransaction;
private StaticMockitoSession mMockitoSession;
private TestableContext mTestableContext;
- private ShellExecutor mBgExecutor = new TestShellExecutor();
+ private final ShellExecutor mBgExecutor = new TestShellExecutor();
+ private final AssistContent mAssistContent = new AssistContent();
/** Set up run before test class. */
@BeforeClass
@@ -673,10 +679,11 @@
public void capturedLink_handleMenuBrowserLinkSetToCapturedLinkIfValid() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
final DesktopModeWindowDecoration decor = createWindowDecoration(
- taskInfo, TEST_URI1 /* captured link */, TEST_URI2 /* generic link */);
+ taskInfo, TEST_URI1 /* captured link */, TEST_URI2 /* web uri */,
+ TEST_URI3 /* generic link */);
// Verify handle menu's browser link set as captured link
- decor.createHandleMenu(mMockSplitScreenController);
+ createHandleMenu(decor);
verifyHandleMenuCreated(TEST_URI1);
}
@@ -685,7 +692,8 @@
public void capturedLink_postsOnCapturedLinkExpiredRunnable() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
final DesktopModeWindowDecoration decor = createWindowDecoration(
- taskInfo, TEST_URI1 /* captured link */, null /* generic link */);
+ taskInfo, TEST_URI1 /* captured link */, null /* web uri */,
+ null /* generic link */);
final ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class);
// Run runnable to set captured link to expired
@@ -694,7 +702,7 @@
// Verify captured link is no longer valid by verifying link is not set as handle menu
// browser link.
- decor.createHandleMenu(mMockSplitScreenController);
+ createHandleMenu(decor);
verifyHandleMenuCreated(null /* uri */);
}
@@ -703,7 +711,8 @@
public void capturedLink_capturedLinkNotResetToSameLink() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
final DesktopModeWindowDecoration decor = createWindowDecoration(
- taskInfo, TEST_URI1 /* captured link */, null /* generic link */);
+ taskInfo, TEST_URI1 /* captured link */, null /* web uri */,
+ null /* generic link */);
final ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class);
// Run runnable to set captured link to expired
@@ -714,7 +723,7 @@
decor.relayout(taskInfo);
// Verify handle menu's browser link not set to captured link since link is expired
- decor.createHandleMenu(mMockSplitScreenController);
+ createHandleMenu(decor);
verifyHandleMenuCreated(null /* uri */);
}
@@ -723,11 +732,12 @@
public void capturedLink_capturedLinkStillUsedIfExpiredAfterHandleMenuCreation() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
final DesktopModeWindowDecoration decor = createWindowDecoration(
- taskInfo, TEST_URI1 /* captured link */, null /* generic link */);
+ taskInfo, TEST_URI1 /* captured link */, null /* web uri */,
+ null /* generic link */);
final ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class);
// Create handle menu before link expires
- decor.createHandleMenu(mMockSplitScreenController);
+ createHandleMenu(decor);
// Run runnable to set captured link to expired
verify(mMockHandler).postDelayed(runnableArgument.capture(), anyLong());
@@ -735,7 +745,7 @@
// Verify handle menu's browser link is set to captured link since menu was opened before
// captured link expired
- decor.createHandleMenu(mMockSplitScreenController);
+ createHandleMenu(decor);
verifyHandleMenuCreated(TEST_URI1);
}
@@ -744,12 +754,13 @@
public void capturedLink_capturedLinkExpiresAfterClick() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
final DesktopModeWindowDecoration decor = createWindowDecoration(
- taskInfo, TEST_URI1 /* captured link */, null /* generic link */);
+ taskInfo, TEST_URI1 /* captured link */, null /* web uri */,
+ null /* generic link */);
final ArgumentCaptor<Function1<Uri, Unit>> openInBrowserCaptor =
ArgumentCaptor.forClass(Function1.class);
// Simulate menu opening and clicking open in browser button
- decor.createHandleMenu(mMockSplitScreenController);
+ createHandleMenu(decor);
verify(mMockHandleMenu).show(
any(),
any(),
@@ -763,7 +774,7 @@
// Verify handle menu's browser link not set to captured link since link not valid after
// open in browser clicked
- decor.createHandleMenu(mMockSplitScreenController);
+ createHandleMenu(decor);
verifyHandleMenuCreated(null /* uri */);
}
@@ -772,10 +783,11 @@
public void capturedLink_openInBrowserListenerCalledOnClick() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
final DesktopModeWindowDecoration decor = createWindowDecoration(
- taskInfo, TEST_URI1 /* captured link */, null /* generic link */);
+ taskInfo, TEST_URI1 /* captured link */, null /* web uri */,
+ null /* generic link */);
final ArgumentCaptor<Function1<Uri, Unit>> openInBrowserCaptor =
ArgumentCaptor.forClass(Function1.class);
- decor.createHandleMenu(mMockSplitScreenController);
+ createHandleMenu(decor);
verify(mMockHandleMenu).show(
any(),
any(),
@@ -793,24 +805,38 @@
@Test
@EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB)
- public void genericLink_genericLinkUsedWhenCapturedLinkUnavailable() {
+ public void webUriLink_webUriLinkUsedWhenCapturedLinkUnavailable() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
final DesktopModeWindowDecoration decor = createWindowDecoration(
- taskInfo, null /* captured link */, TEST_URI2 /* generic link */);
-
- // Verify handle menu's browser link set as generic link no captured link is available
- decor.createHandleMenu(mMockSplitScreenController);
+ taskInfo, null /* captured link */, TEST_URI2 /* web uri */,
+ TEST_URI3 /* generic link */);
+ // Verify handle menu's browser link set as web uri link when captured link is unavailable
+ createHandleMenu(decor);
verifyHandleMenuCreated(TEST_URI2);
}
@Test
+ @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_APP_TO_WEB)
+ public void genericLink_genericLinkUsedWhenCapturedLinkAndWebUriUnavailable() {
+ final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(true /* visible */);
+ final DesktopModeWindowDecoration decor = createWindowDecoration(
+ taskInfo, null /* captured link */, null /* web uri */,
+ TEST_URI3 /* generic link */);
+
+ // Verify handle menu's browser link set as generic link when captured link and web uri link
+ // are unavailable
+ createHandleMenu(decor);
+ verifyHandleMenuCreated(TEST_URI3);
+ }
+
+ @Test
public void handleMenu_onCloseMenuClick_closesMenu() {
final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true);
final DesktopModeWindowDecoration decoration = createWindowDecoration(taskInfo,
true /* relayout */);
final ArgumentCaptor<Function0<Unit>> closeClickListener =
ArgumentCaptor.forClass(Function0.class);
- decoration.createHandleMenu(mMockSplitScreenController);
+ createHandleMenu(decoration);
verify(mMockHandleMenu).show(
any(),
any(),
@@ -860,9 +886,10 @@
private DesktopModeWindowDecoration createWindowDecoration(
ActivityManager.RunningTaskInfo taskInfo, @Nullable Uri capturedLink,
- @Nullable Uri genericLink) {
+ @Nullable Uri webUri, @Nullable Uri genericLink) {
taskInfo.capturedLink = capturedLink;
taskInfo.capturedLinkTimestamp = System.currentTimeMillis();
+ mAssistContent.setWebUri(webUri);
final String genericLinkString = genericLink == null ? null : genericLink.toString();
doReturn(genericLinkString).when(mMockGenericLinksParser).getGenericLink(any());
// Relayout to set captured link
@@ -894,11 +921,10 @@
mContext, mMockDisplayController, mMockSplitScreenController,
mMockShellTaskOrganizer, taskInfo, mMockSurfaceControl, mMockHandler, mBgExecutor,
mMockChoreographer, mMockSyncQueue, mMockRootTaskDisplayAreaOrganizer,
- mMockGenericLinksParser, SurfaceControl.Builder::new, mMockTransactionSupplier,
- WindowContainerTransaction::new, SurfaceControl::new,
- new WindowManagerWrapper(mMockWindowManager),
- mMockSurfaceControlViewHostFactory, maximizeMenuFactory, mMockHandleMenuFactory,
- mMockMultiInstanceHelper);
+ mMockGenericLinksParser, mMockAssistContentRequester, SurfaceControl.Builder::new,
+ mMockTransactionSupplier, WindowContainerTransaction::new, SurfaceControl::new,
+ new WindowManagerWrapper(mMockWindowManager), mMockSurfaceControlViewHostFactory,
+ maximizeMenuFactory, mMockHandleMenuFactory, mMockMultiInstanceHelper);
windowDecor.setCaptionListeners(mMockTouchEventListener, mMockTouchEventListener,
mMockTouchEventListener, mMockTouchEventListener);
windowDecor.setExclusionRegionListener(mMockExclusionRegionListener);
@@ -926,6 +952,13 @@
}
+ private void createHandleMenu(@NonNull DesktopModeWindowDecoration decor) {
+ decor.createHandleMenu();
+ // Call DesktopModeWindowDecoration#onAssistContentReceived because decor waits to receive
+ // {@link AssistContent} before creating the menu
+ decor.onAssistContentReceived(mAssistContent);
+ }
+
private static boolean hasNoInputChannelFeature(RelayoutParams params) {
return (params.mInputFeatures & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL)
!= 0;
diff --git a/libs/hostgraphics/Android.bp b/libs/hostgraphics/Android.bp
index 09232b6..a58493a 100644
--- a/libs/hostgraphics/Android.bp
+++ b/libs/hostgraphics/Android.bp
@@ -7,6 +7,17 @@
default_applicable_licenses: ["frameworks_base_license"],
}
+cc_library_headers {
+ name: "libhostgraphics_headers",
+ host_supported: true,
+ export_include_dirs: ["include"],
+ target: {
+ windows: {
+ enabled: true,
+ },
+ },
+}
+
cc_library_host_static {
name: "libhostgraphics",
@@ -30,12 +41,13 @@
],
header_libs: [
+ "libhostgraphics_headers",
"libnativebase_headers",
"libnativedisplay_headers",
"libnativewindow_headers",
],
- export_include_dirs: ["."],
+ export_include_dirs: ["include"],
target: {
windows: {
diff --git a/libs/hostgraphics/gui/BufferItem.h b/libs/hostgraphics/include/gui/BufferItem.h
similarity index 100%
rename from libs/hostgraphics/gui/BufferItem.h
rename to libs/hostgraphics/include/gui/BufferItem.h
diff --git a/libs/hostgraphics/gui/BufferItemConsumer.h b/libs/hostgraphics/include/gui/BufferItemConsumer.h
similarity index 100%
rename from libs/hostgraphics/gui/BufferItemConsumer.h
rename to libs/hostgraphics/include/gui/BufferItemConsumer.h
diff --git a/libs/hostgraphics/gui/BufferQueue.h b/libs/hostgraphics/include/gui/BufferQueue.h
similarity index 100%
rename from libs/hostgraphics/gui/BufferQueue.h
rename to libs/hostgraphics/include/gui/BufferQueue.h
diff --git a/libs/hostgraphics/gui/ConsumerBase.h b/libs/hostgraphics/include/gui/ConsumerBase.h
similarity index 100%
rename from libs/hostgraphics/gui/ConsumerBase.h
rename to libs/hostgraphics/include/gui/ConsumerBase.h
diff --git a/libs/hostgraphics/gui/IGraphicBufferConsumer.h b/libs/hostgraphics/include/gui/IGraphicBufferConsumer.h
similarity index 100%
rename from libs/hostgraphics/gui/IGraphicBufferConsumer.h
rename to libs/hostgraphics/include/gui/IGraphicBufferConsumer.h
diff --git a/libs/hostgraphics/gui/IGraphicBufferProducer.h b/libs/hostgraphics/include/gui/IGraphicBufferProducer.h
similarity index 100%
rename from libs/hostgraphics/gui/IGraphicBufferProducer.h
rename to libs/hostgraphics/include/gui/IGraphicBufferProducer.h
diff --git a/libs/hostgraphics/gui/Surface.h b/libs/hostgraphics/include/gui/Surface.h
similarity index 100%
rename from libs/hostgraphics/gui/Surface.h
rename to libs/hostgraphics/include/gui/Surface.h
diff --git a/libs/hostgraphics/ui/Fence.h b/libs/hostgraphics/include/ui/Fence.h
similarity index 100%
rename from libs/hostgraphics/ui/Fence.h
rename to libs/hostgraphics/include/ui/Fence.h
diff --git a/libs/hostgraphics/ui/GraphicBuffer.h b/libs/hostgraphics/include/ui/GraphicBuffer.h
similarity index 100%
rename from libs/hostgraphics/ui/GraphicBuffer.h
rename to libs/hostgraphics/include/ui/GraphicBuffer.h
diff --git a/libs/hwui/FeatureFlags.h b/libs/hwui/FeatureFlags.h
index c1c30f5..c0cedf1 100644
--- a/libs/hwui/FeatureFlags.h
+++ b/libs/hwui/FeatureFlags.h
@@ -25,14 +25,6 @@
namespace text_feature {
-inline bool fix_double_underline() {
-#ifdef __ANDROID__
- return com_android_text_flags_fix_double_underline();
-#else
- return true;
-#endif // __ANDROID__
-}
-
inline bool deprecate_ui_fonts() {
#ifdef __ANDROID__
return com_android_text_flags_deprecate_ui_fonts();
diff --git a/libs/hwui/HardwareBitmapUploader.cpp b/libs/hwui/HardwareBitmapUploader.cpp
index a2748b0..236c3736 100644
--- a/libs/hwui/HardwareBitmapUploader.cpp
+++ b/libs/hwui/HardwareBitmapUploader.cpp
@@ -318,6 +318,11 @@
return has101012Support;
}
+bool HardwareBitmapUploader::has10101010Support() {
+ static bool has1010110Support = checkSupport(AHARDWAREBUFFER_FORMAT_R10G10B10A10_UNORM);
+ return has1010110Support;
+}
+
bool HardwareBitmapUploader::hasAlpha8Support() {
static bool hasAlpha8Support = checkSupport(AHARDWAREBUFFER_FORMAT_R8_UNORM);
return hasAlpha8Support;
@@ -376,6 +381,19 @@
}
formatInfo.format = GL_RGBA;
break;
+ case kRGBA_10x6_SkColorType:
+ formatInfo.isSupported = HardwareBitmapUploader::has10101010Support();
+ if (formatInfo.isSupported) {
+ formatInfo.type = 0; // Not supported in GL
+ formatInfo.bufferFormat = AHARDWAREBUFFER_FORMAT_R10G10B10A10_UNORM;
+ formatInfo.vkFormat = VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16;
+ } else {
+ formatInfo.type = GL_UNSIGNED_BYTE;
+ formatInfo.bufferFormat = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM;
+ formatInfo.vkFormat = VK_FORMAT_R8G8B8A8_UNORM;
+ }
+ formatInfo.format = 0; // Not supported in GL
+ break;
case kAlpha_8_SkColorType:
formatInfo.isSupported = HardwareBitmapUploader::hasAlpha8Support();
if (formatInfo.isSupported) {
diff --git a/libs/hwui/HardwareBitmapUploader.h b/libs/hwui/HardwareBitmapUploader.h
index 00ee996..76cb80b 100644
--- a/libs/hwui/HardwareBitmapUploader.h
+++ b/libs/hwui/HardwareBitmapUploader.h
@@ -33,12 +33,14 @@
#ifdef __ANDROID__
static bool hasFP16Support();
static bool has1010102Support();
+ static bool has10101010Support();
static bool hasAlpha8Support();
#else
static bool hasFP16Support() {
return true;
}
static bool has1010102Support() { return true; }
+ static bool has10101010Support() { return true; }
static bool hasAlpha8Support() { return true; }
#endif
};
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 72e83af..9e825fb 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -841,9 +841,6 @@
sk_sp<SkTextBlob> textBlob(builder.make());
applyLooper(&paintCopy, [&](const SkPaint& p) { mCanvas->drawTextBlob(textBlob, 0, 0, p); });
- if (!text_feature::fix_double_underline()) {
- drawTextDecorations(x, y, totalAdvance, paintCopy);
- }
}
void SkiaCanvas::drawLayoutOnPath(const minikin::Layout& layout, float hOffset, float vOffset,
diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp
index 80b6c03..5af4af2 100644
--- a/libs/hwui/hwui/Canvas.cpp
+++ b/libs/hwui/hwui/Canvas.cpp
@@ -110,28 +110,26 @@
DrawTextFunctor f(layout, this, paint, x, y, layout.getAdvance());
MinikinUtils::forFontRun(layout, &paint, f);
- if (text_feature::fix_double_underline()) {
- Paint copied(paint);
- PaintFilter* filter = getPaintFilter();
- if (filter != nullptr) {
- filter->filterFullPaint(&copied);
+ Paint copied(paint);
+ PaintFilter* filter = getPaintFilter();
+ if (filter != nullptr) {
+ filter->filterFullPaint(&copied);
+ }
+ const bool isUnderline = copied.isUnderline();
+ const bool isStrikeThru = copied.isStrikeThru();
+ if (isUnderline || isStrikeThru) {
+ const SkScalar left = x;
+ const SkScalar right = x + layout.getAdvance();
+ if (isUnderline) {
+ const SkScalar top = y + f.getUnderlinePosition();
+ drawStroke(left, right, top, f.getUnderlineThickness(), copied, this);
}
- const bool isUnderline = copied.isUnderline();
- const bool isStrikeThru = copied.isStrikeThru();
- if (isUnderline || isStrikeThru) {
- const SkScalar left = x;
- const SkScalar right = x + layout.getAdvance();
- if (isUnderline) {
- const SkScalar top = y + f.getUnderlinePosition();
- drawStroke(left, right, top, f.getUnderlineThickness(), copied, this);
- }
- if (isStrikeThru) {
- float textSize = paint.getSkFont().getSize();
- const float position = textSize * Paint::kStdStrikeThru_Top;
- const SkScalar thickness = textSize * Paint::kStdStrikeThru_Thickness;
- const SkScalar top = y + position;
- drawStroke(left, right, top, thickness, copied, this);
- }
+ if (isStrikeThru) {
+ float textSize = paint.getSkFont().getSize();
+ const float position = textSize * Paint::kStdStrikeThru_Top;
+ const SkScalar thickness = textSize * Paint::kStdStrikeThru_Thickness;
+ const SkScalar top = y + position;
+ drawStroke(left, right, top, thickness, copied, this);
}
}
}
diff --git a/libs/hwui/hwui/DrawTextFunctor.h b/libs/hwui/hwui/DrawTextFunctor.h
index 0efb2c8..d7bf201 100644
--- a/libs/hwui/hwui/DrawTextFunctor.h
+++ b/libs/hwui/hwui/DrawTextFunctor.h
@@ -142,32 +142,30 @@
canvas->drawGlyphs(glyphFunc, glyphCount, paint, x, y, totalAdvance);
}
- if (text_feature::fix_double_underline()) {
- // Extract underline position and thickness.
- if (paint.isUnderline()) {
- SkFontMetrics metrics;
- paint.getSkFont().getMetrics(&metrics);
- const float textSize = paint.getSkFont().getSize();
- SkScalar position;
- if (!metrics.hasUnderlinePosition(&position)) {
- position = textSize * Paint::kStdUnderline_Top;
- }
- SkScalar thickness;
- if (!metrics.hasUnderlineThickness(&thickness)) {
- thickness = textSize * Paint::kStdUnderline_Thickness;
- }
-
- // If multiple fonts are used, use the most bottom position and most thick stroke
- // width as the underline position. This follows the CSS standard:
- // https://www.w3.org/TR/css-text-decor-3/#text-underline-position-property
- // <quote>
- // The exact position and thickness of line decorations is UA-defined in this level.
- // However, for underlines and overlines the UA must use a single thickness and
- // position on each line for the decorations deriving from a single decorating box.
- // </quote>
- underlinePosition = std::max(underlinePosition, position);
- underlineThickness = std::max(underlineThickness, thickness);
+ // Extract underline position and thickness.
+ if (paint.isUnderline()) {
+ SkFontMetrics metrics;
+ paint.getSkFont().getMetrics(&metrics);
+ const float textSize = paint.getSkFont().getSize();
+ SkScalar position;
+ if (!metrics.hasUnderlinePosition(&position)) {
+ position = textSize * Paint::kStdUnderline_Top;
}
+ SkScalar thickness;
+ if (!metrics.hasUnderlineThickness(&thickness)) {
+ thickness = textSize * Paint::kStdUnderline_Thickness;
+ }
+
+ // If multiple fonts are used, use the most bottom position and most thick stroke
+ // width as the underline position. This follows the CSS standard:
+ // https://www.w3.org/TR/css-text-decor-3/#text-underline-position-property
+ // <quote>
+ // The exact position and thickness of line decorations is UA-defined in this level.
+ // However, for underlines and overlines the UA must use a single thickness and
+ // position on each line for the decorations deriving from a single decorating box.
+ // </quote>
+ underlinePosition = std::max(underlinePosition, position);
+ underlineThickness = std::max(underlineThickness, thickness);
}
}
diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index bce84ae..e302393 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -318,6 +318,15 @@
tailPNext = &deviceFaultFeatures->pNext;
}
+ if (grExtensions.hasExtension(VK_EXT_RGBA10X6_FORMATS_EXTENSION_NAME, 1)) {
+ VkPhysicalDeviceRGBA10X6FormatsFeaturesEXT* formatFeatures =
+ new VkPhysicalDeviceRGBA10X6FormatsFeaturesEXT;
+ formatFeatures->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RGBA10X6_FORMATS_FEATURES_EXT;
+ formatFeatures->pNext = nullptr;
+ *tailPNext = formatFeatures;
+ tailPNext = &formatFeatures->pNext;
+ }
+
// query to get the physical device features
mGetPhysicalDeviceFeatures2(mPhysicalDevice, &features);
// this looks like it would slow things down,
diff --git a/libs/hwui/tests/unit/UnderlineTest.cpp b/libs/hwui/tests/unit/UnderlineTest.cpp
index c70a304..ecb06d8 100644
--- a/libs/hwui/tests/unit/UnderlineTest.cpp
+++ b/libs/hwui/tests/unit/UnderlineTest.cpp
@@ -109,9 +109,7 @@
return f;
}
-TEST_WITH_FLAGS(UnderlineTest, Roboto,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::text::flags,
- fix_double_underline))) {
+TEST(UnderlineTest, Roboto) {
float textSize = 100;
Paint paint;
paint.getSkFont().setSize(textSize);
@@ -123,9 +121,7 @@
EXPECT_EQ(ROBOTO_THICKNESS_EM * textSize, functor.getUnderlineThickness());
}
-TEST_WITH_FLAGS(UnderlineTest, NotoCJK,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::text::flags,
- fix_double_underline))) {
+TEST(UnderlineTest, NotoCJK) {
float textSize = 100;
Paint paint;
paint.getSkFont().setSize(textSize);
@@ -137,9 +133,7 @@
EXPECT_EQ(NOTO_CJK_THICKNESS_EM * textSize, functor.getUnderlineThickness());
}
-TEST_WITH_FLAGS(UnderlineTest, Mixture,
- REQUIRES_FLAGS_ENABLED(ACONFIG_FLAG(com::android::text::flags,
- fix_double_underline))) {
+TEST(UnderlineTest, Mixture) {
float textSize = 100;
Paint paint;
paint.getSkFont().setSize(textSize);
diff --git a/libs/hwui/utils/Color.cpp b/libs/hwui/utils/Color.cpp
index 6a560b3..9673c5f 100644
--- a/libs/hwui/utils/Color.cpp
+++ b/libs/hwui/utils/Color.cpp
@@ -49,6 +49,10 @@
colorType = kRGBA_1010102_SkColorType;
alphaType = kPremul_SkAlphaType;
break;
+ case AHARDWAREBUFFER_FORMAT_R10G10B10A10_UNORM:
+ colorType = kRGBA_10x6_SkColorType;
+ alphaType = kPremul_SkAlphaType;
+ break;
case AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT:
colorType = kRGBA_F16_SkColorType;
alphaType = kPremul_SkAlphaType;
@@ -86,6 +90,8 @@
return AHARDWAREBUFFER_FORMAT_R8G8B8X8_UNORM;
case kRGBA_1010102_SkColorType:
return AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM;
+ case kRGBA_10x6_SkColorType:
+ return AHARDWAREBUFFER_FORMAT_R10G10B10A10_UNORM;
case kARGB_4444_SkColorType:
// Hardcoding the value from android::PixelFormat
static constexpr uint64_t kRGBA4444 = 7;
@@ -108,6 +114,8 @@
return kRGB_565_SkColorType;
case AHARDWAREBUFFER_FORMAT_R10G10B10A2_UNORM:
return kRGBA_1010102_SkColorType;
+ case AHARDWAREBUFFER_FORMAT_R10G10B10A10_UNORM:
+ return kRGBA_10x6_SkColorType;
case AHARDWAREBUFFER_FORMAT_R16G16B16A16_FLOAT:
return kRGBA_F16_SkColorType;
case AHARDWAREBUFFER_FORMAT_R8_UNORM:
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 25fae76..4981cb3 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -6984,6 +6984,27 @@
}
/**
+ * Test method for enabling/disabling the volume controller long press timeout for checking
+ * whether two consecutive volume adjustments should be treated as a volume long press.
+ *
+ * <p>Used only for testing
+ *
+ * @param enable true for enabling, otherwise will be disabled (test mode)
+ *
+ * @hide
+ **/
+ @TestApi
+ @SuppressLint("UnflaggedApi") // @TestApi without associated feature.
+ @RequiresPermission(Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED)
+ public void setVolumeControllerLongPressTimeoutEnabled(boolean enable) {
+ try {
+ getService().setVolumeControllerLongPressTimeoutEnabled(enable);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Only useful for volume controllers.
* @hide
*/
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index a96562d..9af6b28 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -303,6 +303,9 @@
void notifyVolumeControllerVisible(in IVolumeController controller, boolean visible);
+ @EnforcePermission("MODIFY_AUDIO_SETTINGS_PRIVILEGED")
+ oneway void setVolumeControllerLongPressTimeoutEnabled(boolean enable);
+
boolean isStreamAffectedByRingerMode(int streamType);
boolean isStreamAffectedByMute(int streamType);
diff --git a/packages/SettingsLib/DataStore/Android.bp b/packages/SettingsLib/DataStore/Android.bp
index 86c8f0da..6840e10 100644
--- a/packages/SettingsLib/DataStore/Android.bp
+++ b/packages/SettingsLib/DataStore/Android.bp
@@ -17,6 +17,7 @@
"androidx.annotation_annotation",
"androidx.collection_collection-ktx",
"androidx.core_core-ktx",
+ "error_prone_annotations",
"guava",
],
kotlincflags: ["-Xjvm-default=all"],
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyValueStore.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyValueStore.kt
new file mode 100644
index 0000000..3d41337
--- /dev/null
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyValueStore.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.datastore
+
+import android.content.SharedPreferences
+
+/** Interface of key-value store. */
+interface KeyValueStore : KeyedObservable<String> {
+
+ /** Returns if the storage contains persistent value of given key. */
+ fun contains(key: String): Boolean
+
+ /** Gets default value of given key. */
+ fun <T : Any> getDefaultValue(key: String, valueType: Class<T>): T? =
+ when (valueType) {
+ Boolean::class.javaObjectType -> false
+ Float::class.javaObjectType -> 0f
+ Int::class.javaObjectType -> 0
+ Long::class.javaObjectType -> 0
+ else -> null
+ }
+ as T?
+
+ /** Gets value of given key. */
+ fun <T : Any> getValue(key: String, valueType: Class<T>): T?
+
+ /**
+ * Sets value for given key.
+ *
+ * @param key key
+ * @param valueType value type
+ * @param value value to set, null means remove
+ */
+ fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?)
+}
+
+/** [SharedPreferences] based [KeyValueStore]. */
+interface SharedPreferencesKeyValueStore : KeyValueStore {
+
+ /** [SharedPreferences] of the key-value store. */
+ val sharedPreferences: SharedPreferences
+
+ override fun contains(key: String): Boolean = sharedPreferences.contains(key)
+
+ override fun <T : Any> getValue(key: String, valueType: Class<T>): T? =
+ when (valueType) {
+ Boolean::class.javaObjectType -> sharedPreferences.getBoolean(key, false)
+ Float::class.javaObjectType -> sharedPreferences.getFloat(key, 0f)
+ Int::class.javaObjectType -> sharedPreferences.getInt(key, 0)
+ Long::class.javaObjectType -> sharedPreferences.getLong(key, 0)
+ String::class.javaObjectType -> sharedPreferences.getString(key, null)
+ Set::class.javaObjectType -> sharedPreferences.getStringSet(key, null)
+ else -> null
+ }
+ as T?
+
+ override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) {
+ if (value == null) {
+ sharedPreferences.edit().remove(key).apply()
+ return
+ }
+ val edit = sharedPreferences.edit()
+ when (valueType) {
+ Boolean::class.javaObjectType -> edit.putBoolean(key, value as Boolean)
+ Float::class.javaObjectType -> edit.putFloat(key, value as Float)
+ Int::class.javaObjectType -> edit.putInt(key, value as Int)
+ Long::class.javaObjectType -> edit.putLong(key, value as Long)
+ String::class.javaObjectType -> edit.putString(key, value as String)
+ Set::class.javaObjectType -> edit.putStringSet(key, value as Set<String>)
+ else -> {}
+ }
+ edit.apply()
+ }
+}
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt
index 4ce1d37..ec90317 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/KeyedObserver.kt
@@ -19,6 +19,7 @@
import androidx.annotation.AnyThread
import androidx.annotation.GuardedBy
import androidx.collection.MutableScatterMap
+import com.google.errorprone.annotations.CanIgnoreReturnValue
import java.util.WeakHashMap
import java.util.concurrent.Executor
@@ -62,8 +63,9 @@
*
* @param observer observer to be notified
* @param executor executor to run the callback
+ * @return if the observer is newly added
*/
- fun addObserver(observer: KeyedObserver<K?>, executor: Executor)
+ @CanIgnoreReturnValue fun addObserver(observer: KeyedObserver<K?>, executor: Executor): Boolean
/**
* Adds an observer on given key.
@@ -73,14 +75,24 @@
* @param key key to observe
* @param observer observer to be notified
* @param executor executor to run the callback
+ * @return if the observer is newly added
*/
- fun addObserver(key: K, observer: KeyedObserver<K>, executor: Executor)
+ @CanIgnoreReturnValue
+ fun addObserver(key: K, observer: KeyedObserver<K>, executor: Executor): Boolean
- /** Removes observer. */
- fun removeObserver(observer: KeyedObserver<K?>)
+ /**
+ * Removes observer.
+ *
+ * @return if the observer is found and removed
+ */
+ @CanIgnoreReturnValue fun removeObserver(observer: KeyedObserver<K?>): Boolean
- /** Removes observer on given key. */
- fun removeObserver(key: K, observer: KeyedObserver<K>)
+ /**
+ * Removes observer on given key.
+ *
+ * @return if the observer is found and removed
+ */
+ @CanIgnoreReturnValue fun removeObserver(key: K, observer: KeyedObserver<K>): Boolean
/**
* Notifies all observers that a change occurs.
@@ -111,14 +123,17 @@
@GuardedBy("itself")
private val keyedObservers = MutableScatterMap<K, WeakHashMap<KeyedObserver<K>, Executor>>()
- override fun addObserver(observer: KeyedObserver<K?>, executor: Executor) {
+ @CanIgnoreReturnValue
+ override fun addObserver(observer: KeyedObserver<K?>, executor: Executor): Boolean {
val oldExecutor = synchronized(observers) { observers.put(observer, executor) }
if (oldExecutor != null && oldExecutor != executor) {
throw IllegalStateException("Add $observer twice, old=$oldExecutor, new=$executor")
}
+ return oldExecutor == null
}
- override fun addObserver(key: K, observer: KeyedObserver<K>, executor: Executor) {
+ @CanIgnoreReturnValue
+ override fun addObserver(key: K, observer: KeyedObserver<K>, executor: Executor): Boolean {
val oldExecutor =
synchronized(keyedObservers) {
keyedObservers.getOrPut(key) { WeakHashMap() }.put(observer, executor)
@@ -126,20 +141,23 @@
if (oldExecutor != null && oldExecutor != executor) {
throw IllegalStateException("Add $observer twice, old=$oldExecutor, new=$executor")
}
+ return oldExecutor == null
}
- override fun removeObserver(observer: KeyedObserver<K?>) {
- synchronized(observers) { observers.remove(observer) }
- }
+ @CanIgnoreReturnValue
+ override fun removeObserver(observer: KeyedObserver<K?>) =
+ synchronized(observers) { observers.remove(observer) } != null
- override fun removeObserver(key: K, observer: KeyedObserver<K>) {
+ @CanIgnoreReturnValue
+ override fun removeObserver(key: K, observer: KeyedObserver<K>) =
synchronized(keyedObservers) {
- val observers = keyedObservers[key]
- if (observers?.remove(observer) != null && observers.isEmpty()) {
+ val observers = keyedObservers[key] ?: return false
+ val removed = observers.remove(observer) != null
+ if (removed && observers.isEmpty()) {
keyedObservers.remove(key)
}
+ removed
}
- }
override fun notifyChange(reason: Int) {
// make a copy to avoid potential ConcurrentModificationException
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsGlobalStore.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsGlobalStore.kt
new file mode 100644
index 0000000..4aef0fc
--- /dev/null
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsGlobalStore.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.datastore
+
+import android.content.ContentResolver
+import android.content.Context
+import android.provider.Settings.Global
+import android.provider.Settings.SettingNotFoundException
+
+/**
+ * [KeyValueStore] for [Global] settings.
+ *
+ * By default, a boolean type `true` value is stored as `1` and `false` value is stored as `0`.
+ */
+class SettingsGlobalStore private constructor(contentResolver: ContentResolver) :
+ SettingsStore(contentResolver) {
+
+ override val tag: String
+ get() = "SettingsGlobalStore"
+
+ override fun contains(key: String): Boolean = Global.getString(contentResolver, key) != null
+
+ override fun <T : Any> getValue(key: String, valueType: Class<T>): T? =
+ try {
+ when (valueType) {
+ Boolean::class.javaObjectType -> Global.getInt(contentResolver, key) != 0
+ Float::class.javaObjectType -> Global.getFloat(contentResolver, key)
+ Int::class.javaObjectType -> Global.getInt(contentResolver, key)
+ Long::class.javaObjectType -> Global.getLong(contentResolver, key)
+ String::class.javaObjectType -> Global.getString(contentResolver, key)
+ else -> throw UnsupportedOperationException("Get $key $valueType")
+ }
+ as T?
+ } catch (e: SettingNotFoundException) {
+ null
+ }
+
+ override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) {
+ if (value == null) {
+ Global.putString(contentResolver, key, null)
+ return
+ }
+ when (valueType) {
+ Boolean::class.javaObjectType ->
+ Global.putInt(contentResolver, key, if (value == true) 1 else 0)
+ Float::class.javaObjectType -> Global.putFloat(contentResolver, key, value as Float)
+ Int::class.javaObjectType -> Global.putInt(contentResolver, key, value as Int)
+ Long::class.javaObjectType -> Global.putLong(contentResolver, key, value as Long)
+ String::class.javaObjectType -> Global.putString(contentResolver, key, value as String)
+ else -> throw UnsupportedOperationException("Set $key $valueType")
+ }
+ }
+
+ companion object {
+ @Volatile private var instance: SettingsGlobalStore? = null
+
+ @JvmStatic
+ fun get(context: Context): SettingsGlobalStore =
+ instance
+ ?: synchronized(this) {
+ instance
+ ?: SettingsGlobalStore(context.applicationContext.contentResolver).also {
+ instance = it
+ }
+ }
+ }
+}
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsSecureStore.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsSecureStore.kt
new file mode 100644
index 0000000..9f41ecb
--- /dev/null
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsSecureStore.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.datastore
+
+import android.content.ContentResolver
+import android.content.Context
+import android.provider.Settings.Secure
+import android.provider.Settings.SettingNotFoundException
+
+/**
+ * [KeyValueStore] for [Secure] settings.
+ *
+ * By default, a boolean type `true` value is stored as `1` and `false` value is stored as `0`.
+ */
+class SettingsSecureStore private constructor(contentResolver: ContentResolver) :
+ SettingsStore(contentResolver) {
+
+ override val tag: String
+ get() = "SettingsSecureStore"
+
+ override fun contains(key: String): Boolean = Secure.getString(contentResolver, key) != null
+
+ override fun <T : Any> getValue(key: String, valueType: Class<T>): T? =
+ try {
+ when (valueType) {
+ Boolean::class.javaObjectType -> Secure.getInt(contentResolver, key) != 0
+ Float::class.javaObjectType -> Secure.getFloat(contentResolver, key)
+ Int::class.javaObjectType -> Secure.getInt(contentResolver, key)
+ Long::class.javaObjectType -> Secure.getLong(contentResolver, key)
+ String::class.javaObjectType -> Secure.getString(contentResolver, key)
+ else -> throw UnsupportedOperationException("Get $key $valueType")
+ }
+ as T?
+ } catch (e: SettingNotFoundException) {
+ null
+ }
+
+ override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) {
+ if (value == null) {
+ Secure.putString(contentResolver, key, null)
+ return
+ }
+ when (valueType) {
+ Boolean::class.javaObjectType ->
+ Secure.putInt(contentResolver, key, if (value == true) 1 else 0)
+ Float::class.javaObjectType -> Secure.putFloat(contentResolver, key, value as Float)
+ Int::class.javaObjectType -> Secure.putInt(contentResolver, key, value as Int)
+ Long::class.javaObjectType -> Secure.putLong(contentResolver, key, value as Long)
+ String::class.javaObjectType -> Secure.putString(contentResolver, key, value as String)
+ else -> throw UnsupportedOperationException("Set $key $valueType")
+ }
+ }
+
+ companion object {
+ @Volatile private var instance: SettingsSecureStore? = null
+
+ @JvmStatic
+ fun get(context: Context): SettingsSecureStore =
+ instance
+ ?: synchronized(this) {
+ instance
+ ?: SettingsSecureStore(context.applicationContext.contentResolver).also {
+ instance = it
+ }
+ }
+ }
+}
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsStore.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsStore.kt
new file mode 100644
index 0000000..5981688
--- /dev/null
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsStore.kt
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.datastore
+
+import android.content.ContentResolver
+import android.database.ContentObserver
+import android.net.Uri
+import android.os.Handler
+import android.os.Looper
+import android.provider.Settings
+import android.util.Log
+import java.util.concurrent.Executor
+import java.util.concurrent.atomic.AtomicInteger
+
+/** Base class of the Settings provider data stores. */
+open abstract class SettingsStore(protected val contentResolver: ContentResolver) :
+ KeyedDataObservable<String>(), KeyValueStore {
+
+ /**
+ * Counter of observers.
+ *
+ * The value is accurate only when [addObserver] and [removeObserver] are called correctly. When
+ * an observer is not removed (and its weak reference is garbage collected), the content
+ * observer is not unregistered but this is not a big deal.
+ */
+ private val counter = AtomicInteger()
+
+ private val contentObserver =
+ object : ContentObserver(Handler(Looper.getMainLooper())) {
+ override fun onChange(selfChange: Boolean) {
+ super.onChange(selfChange, null)
+ }
+
+ override fun onChange(selfChange: Boolean, uri: Uri?) {
+ val key = uri?.lastPathSegment ?: return
+ notifyChange(key, DataChangeReason.UPDATE)
+ }
+ }
+
+ override fun addObserver(observer: KeyedObserver<String?>, executor: Executor) =
+ if (super.addObserver(observer, executor)) {
+ onObserverAdded()
+ true
+ } else {
+ false
+ }
+
+ override fun addObserver(key: String, observer: KeyedObserver<String>, executor: Executor) =
+ if (super.addObserver(key, observer, executor)) {
+ onObserverAdded()
+ true
+ } else {
+ false
+ }
+
+ private fun onObserverAdded() {
+ if (counter.getAndIncrement() != 0) return
+ Log.i(tag, "registerContentObserver")
+ contentResolver.registerContentObserver(
+ Settings.Global.getUriFor(""),
+ true,
+ contentObserver,
+ )
+ }
+
+ override fun removeObserver(observer: KeyedObserver<String?>) =
+ if (super.removeObserver(observer)) {
+ onObserverRemoved()
+ true
+ } else {
+ false
+ }
+
+ override fun removeObserver(key: String, observer: KeyedObserver<String>) =
+ if (super.removeObserver(key, observer)) {
+ onObserverRemoved()
+ true
+ } else {
+ false
+ }
+
+ private fun onObserverRemoved() {
+ if (counter.decrementAndGet() != 0) return
+ Log.i(tag, "unregisterContentObserver")
+ contentResolver.unregisterContentObserver(contentObserver)
+ }
+
+ /** Tag for logging. */
+ abstract val tag: String
+}
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsSystemStore.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsSystemStore.kt
new file mode 100644
index 0000000..6cca7ed
--- /dev/null
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SettingsSystemStore.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.datastore
+
+import android.content.ContentResolver
+import android.content.Context
+import android.provider.Settings.SettingNotFoundException
+import android.provider.Settings.System
+
+/**
+ * [KeyValueStore] for [System] settings.
+ *
+ * By default, a boolean type `true` value is stored as `1` and `false` value is stored as `0`.
+ */
+class SettingsSystemStore private constructor(contentResolver: ContentResolver) :
+ SettingsStore(contentResolver) {
+
+ override val tag: String
+ get() = "SettingsSystemStore"
+
+ override fun contains(key: String): Boolean = System.getString(contentResolver, key) != null
+
+ override fun <T : Any> getValue(key: String, valueType: Class<T>): T? =
+ try {
+ when (valueType) {
+ Boolean::class.javaObjectType -> System.getInt(contentResolver, key) != 0
+ Float::class.javaObjectType -> System.getFloat(contentResolver, key)
+ Int::class.javaObjectType -> System.getInt(contentResolver, key)
+ Long::class.javaObjectType -> System.getLong(contentResolver, key)
+ String::class.javaObjectType -> System.getString(contentResolver, key)
+ else -> throw UnsupportedOperationException("Get $key $valueType")
+ }
+ as T?
+ } catch (e: SettingNotFoundException) {
+ null
+ }
+
+ override fun <T : Any> setValue(key: String, valueType: Class<T>, value: T?) {
+ if (value == null) {
+ System.putString(contentResolver, key, null)
+ return
+ }
+ when (valueType) {
+ Boolean::class.javaObjectType ->
+ System.putInt(contentResolver, key, if (value == true) 1 else 0)
+ Float::class.javaObjectType -> System.putFloat(contentResolver, key, value as Float)
+ Int::class.javaObjectType -> System.putInt(contentResolver, key, value as Int)
+ Long::class.javaObjectType -> System.putLong(contentResolver, key, value as Long)
+ String::class.javaObjectType -> System.putString(contentResolver, key, value as String)
+ else -> throw UnsupportedOperationException("Set $key $valueType")
+ }
+ }
+
+ companion object {
+ @Volatile private var instance: SettingsSystemStore? = null
+
+ @JvmStatic
+ fun get(context: Context): SettingsSystemStore =
+ instance
+ ?: synchronized(this) {
+ instance
+ ?: SettingsSystemStore(context.applicationContext.contentResolver).also {
+ instance = it
+ }
+ }
+ }
+}
diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SharedPreferencesStorage.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SharedPreferencesStorage.kt
index 0ca91cd..ea17a56 100644
--- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SharedPreferencesStorage.kt
+++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SharedPreferencesStorage.kt
@@ -40,8 +40,9 @@
* Note that existing entries in the SharedPreferences will NOT be deleted before restore.
*
* @param context Context to get SharedPreferences
- * @param name Name of the SharedPreferences
- * @param mode Operating mode, see [Context.getSharedPreferences]
+ * @param name Name of the backup restore storage
+ * @param sharedPreferences SharedPreferences object
+ * @param filePath shared preferences file path relative to data dir
* @param verbose Verbose logging on key/value pairs during backup/restore. Enable for dev only!
* @param filter Filter of key/value pairs for backup and restore.
*/
@@ -50,12 +51,14 @@
constructor(
context: Context,
override val name: String,
- @get:VisibleForTesting internal val sharedPreferences: SharedPreferences,
+ override val sharedPreferences: SharedPreferences,
+ filePath: String = getSharedPreferencesFilePath(context, name),
private val codec: BackupCodec? = null,
private val verbose: Boolean = defaultVerbose(),
private val filter: (String, Any?) -> Boolean = { _, _ -> true },
) :
- BackupRestoreFileStorage(context, context.getSharedPreferencesFilePath(name)),
+ BackupRestoreFileStorage(context, filePath),
+ SharedPreferencesKeyValueStore,
KeyedObservable<String> by KeyedDataObservable() {
@JvmOverloads
@@ -66,7 +69,15 @@
codec: BackupCodec? = null,
verbose: Boolean = defaultVerbose(),
filter: (String, Any?) -> Boolean = { _, _ -> true },
- ) : this(context, name, context.getSharedPreferences(name, mode), codec, verbose, filter)
+ ) : this(
+ context,
+ name,
+ context.getSharedPreferences(name, mode),
+ getSharedPreferencesFilePath(context, name),
+ codec,
+ verbose,
+ filter,
+ )
/** Name of the intermediate SharedPreferences. */
@VisibleForTesting
@@ -80,7 +91,15 @@
return context.getSharedPreferences(intermediateName, Context.MODE_MULTI_PROCESS)
}
- private val sharedPreferencesListener = createSharedPreferenceListener()
+ private val sharedPreferencesListener =
+ SharedPreferences.OnSharedPreferenceChangeListener { _, key ->
+ if (key != null) {
+ notifyChange(key, DataChangeReason.UPDATE)
+ } else {
+ // On Android >= R, SharedPreferences.Editor.clear() will trigger this case
+ notifyChange(DataChangeReason.DELETE)
+ }
+ }
init {
// listener is weakly referenced, so unregister is optional
@@ -183,7 +202,8 @@
else -> {
Log.e(
LOG_TAG,
- "[$name] $operation $key=$value, unknown type: ${value?.javaClass}")
+ "[$name] $operation $key=$value, unknown type: ${value?.javaClass}",
+ )
}
}
}
@@ -191,14 +211,31 @@
}
companion object {
- private fun Context.getSharedPreferencesFilePath(name: String): String {
- val file = getSharedPreferencesFile(name)
- return file.relativeTo(dataDirCompat).toString()
+ /** Returns the storage object of default [SharedPreferences]. */
+ @JvmStatic
+ fun getDefault(context: Context, name: String): SharedPreferencesStorage {
+ val prefName = getDefaultSharedPreferencesName(context)
+ return SharedPreferencesStorage(
+ context,
+ name,
+ context.getSharedPreferences(prefName, Context.MODE_PRIVATE),
+ getSharedPreferencesFilePath(context, prefName),
+ )
+ }
+
+ /** Returns the name of default [SharedPreferences]. */
+ @JvmStatic
+ fun getDefaultSharedPreferencesName(context: Context) = context.packageName + "_preferences"
+
+ /** Returns the shared preferences file path relative to data dir. */
+ @JvmStatic
+ fun getSharedPreferencesFilePath(context: Context, name: String): String {
+ val file = context.getSharedPreferencesFile(name)
+ return file.relativeTo(context.dataDirCompat).toString()
}
/** Returns the absolute path of shared preferences file. */
- @JvmStatic
- fun Context.getSharedPreferencesFile(name: String): File {
+ private fun Context.getSharedPreferencesFile(name: String): File {
// ContextImpl.getSharedPreferencesPath is private
return File(getSharedPreferencesDir(), "$name.xml")
}
diff --git a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/KeyedObserverTest.kt b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/KeyedObserverTest.kt
index 0fdecb0..c99d4b3 100644
--- a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/KeyedObserverTest.kt
+++ b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/KeyedObserverTest.kt
@@ -45,14 +45,14 @@
@Test
fun addObserver_sameExecutor() {
- keyedObservable.addObserver(observer1, executor1)
- keyedObservable.addObserver(observer1, executor1)
+ assertThat(keyedObservable.addObserver(observer1, executor1)).isTrue()
+ assertThat(keyedObservable.addObserver(observer1, executor1)).isFalse()
}
@Test
fun addObserver_keyedObserver_sameExecutor() {
- keyedObservable.addObserver(key1, keyedObserver1, executor1)
- keyedObservable.addObserver(key1, keyedObserver1, executor1)
+ assertThat(keyedObservable.addObserver(key1, keyedObserver1, executor1)).isTrue()
+ assertThat(keyedObservable.addObserver(key1, keyedObserver1, executor1)).isFalse()
}
@Test
@@ -109,15 +109,15 @@
@Test
fun addObserver_notifyObservers_removeObserver() {
- keyedObservable.addObserver(observer1, executor1)
- keyedObservable.addObserver(observer2, executor2)
+ assertThat(keyedObservable.addObserver(observer1, executor1)).isTrue()
+ assertThat(keyedObservable.addObserver(observer2, executor2)).isTrue()
keyedObservable.notifyChange(DataChangeReason.UPDATE)
verify(observer1).onKeyChanged(null, DataChangeReason.UPDATE)
verify(observer2).onKeyChanged(null, DataChangeReason.UPDATE)
reset(observer1, observer2)
- keyedObservable.removeObserver(observer2)
+ assertThat(keyedObservable.removeObserver(observer2)).isTrue()
keyedObservable.notifyChange(DataChangeReason.DELETE)
verify(observer1).onKeyChanged(null, DataChangeReason.DELETE)
@@ -126,15 +126,15 @@
@Test
fun addObserver_keyedObserver_notifyObservers_removeObserver() {
- keyedObservable.addObserver(key1, keyedObserver1, executor1)
- keyedObservable.addObserver(key2, keyedObserver2, executor2)
+ assertThat(keyedObservable.addObserver(key1, keyedObserver1, executor1)).isTrue()
+ assertThat(keyedObservable.addObserver(key2, keyedObserver2, executor2)).isTrue()
keyedObservable.notifyChange(key1, DataChangeReason.UPDATE)
verify(keyedObserver1).onKeyChanged(key1, DataChangeReason.UPDATE)
verify(keyedObserver2, never()).onKeyChanged(key2, DataChangeReason.UPDATE)
reset(keyedObserver1, keyedObserver2)
- keyedObservable.removeObserver(key1, keyedObserver1)
+ assertThat(keyedObservable.removeObserver(key1, keyedObserver1)).isTrue()
keyedObservable.notifyChange(key1, DataChangeReason.DELETE)
verify(keyedObserver1, never()).onKeyChanged(key1, DataChangeReason.DELETE)
@@ -143,9 +143,9 @@
@Test
fun notifyChange_addMoreTypeObservers_checkOnKeyChanged() {
- keyedObservable.addObserver(observer1, executor1)
- keyedObservable.addObserver(key1, keyedObserver1, executor1)
- keyedObservable.addObserver(key2, keyedObserver2, executor1)
+ assertThat(keyedObservable.addObserver(observer1, executor1)).isTrue()
+ assertThat(keyedObservable.addObserver(key1, keyedObserver1, executor1)).isTrue()
+ assertThat(keyedObservable.addObserver(key2, keyedObserver2, executor1)).isTrue()
keyedObservable.notifyChange(DataChangeReason.UPDATE)
verify(observer1).onKeyChanged(null, DataChangeReason.UPDATE)
@@ -171,25 +171,25 @@
fun notifyChange_addObserverWithinCallback() {
// ConcurrentModificationException is raised if it is not implemented correctly
val observer: KeyedObserver<Any?> = KeyedObserver { _, _ ->
- keyedObservable.addObserver(observer1, executor1)
+ assertThat(keyedObservable.addObserver(observer1, executor1)).isTrue()
}
- keyedObservable.addObserver(observer, executor1)
+ assertThat(keyedObservable.addObserver(observer, executor1)).isTrue()
keyedObservable.notifyChange(DataChangeReason.UPDATE)
- keyedObservable.removeObserver(observer)
+ assertThat(keyedObservable.removeObserver(observer)).isTrue()
}
@Test
fun notifyChange_KeyedObserver_addObserverWithinCallback() {
// ConcurrentModificationException is raised if it is not implemented correctly
val keyObserver: KeyedObserver<Any?> = KeyedObserver { _, _ ->
- keyedObservable.addObserver(key1, keyedObserver1, executor1)
+ assertThat(keyedObservable.addObserver(key1, keyedObserver1, executor1)).isTrue()
}
- keyedObservable.addObserver(key1, keyObserver, executor1)
+ assertThat(keyedObservable.addObserver(key1, keyObserver, executor1)).isTrue()
keyedObservable.notifyChange(key1, DataChangeReason.UPDATE)
- keyedObservable.removeObserver(key1, keyObserver)
+ assertThat(keyedObservable.removeObserver(key1, keyObserver)).isTrue()
}
}
diff --git a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
index 4387b6f..a0599bb 100644
--- a/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
+++ b/packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/IllustrationPreference.java
@@ -35,6 +35,7 @@
import android.widget.FrameLayout;
import android.widget.ImageView;
+import androidx.annotation.Nullable;
import androidx.annotation.RawRes;
import androidx.annotation.StringRes;
import androidx.preference.Preference;
@@ -243,6 +244,14 @@
}
/**
+ * Gets the content description set by {@link #setContentDescription}.
+ */
+ @Nullable
+ public CharSequence getContentDescription() {
+ return mContentDescription;
+ }
+
+ /**
* Gets the lottie illustration resource id.
*/
public int getLottieAnimationResId() {
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt
index 90cee16..1f3e2425 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/framework/theme/SettingsDimension.kt
@@ -25,6 +25,13 @@
val paddingLarge = 16.dp
val paddingExtraLarge = 24.dp
+ val spinnerHorizontalPadding = paddingExtraLarge
+ val spinnerVerticalPadding = paddingLarge
+
+ val actionIconWidth = 32.dp
+ val actionIconHeight = 40.dp
+ val actionIconPadding = 4.dp
+
val itemIconSize = 24.dp
val itemIconContainerSize = 72.dp
val itemPaddingStart = paddingExtraLarge
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/Actions.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/Actions.kt
index 5f320f7..9bbc16d 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/Actions.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/Actions.kt
@@ -17,15 +17,24 @@
package com.android.settingslib.spa.widget.scaffold
import androidx.appcompat.R
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.outlined.ArrowBack
import androidx.compose.material.icons.outlined.Clear
import androidx.compose.material.icons.outlined.FindInPage
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
+import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.stringResource
import com.android.settingslib.spa.framework.compose.LocalNavController
+import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.framework.theme.SettingsShape
+import com.android.settingslib.spa.framework.theme.isSpaExpressiveEnabled
/** Action that navigates back to last page. */
@Composable
@@ -50,6 +59,11 @@
Icon(
imageVector = Icons.AutoMirrored.Outlined.ArrowBack,
contentDescription = contentDescription,
+ modifier = if (isSpaExpressiveEnabled) Modifier
+ .size(SettingsDimension.actionIconWidth, SettingsDimension.actionIconHeight)
+ .clip(SettingsShape.CornerExtraLarge)
+ .background(MaterialTheme.colorScheme.onSurfaceVariant)
+ .padding(SettingsDimension.actionIconPadding) else Modifier
)
}
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsScaffold.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsScaffold.kt
index 4cf741e..7d8ee79 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsScaffold.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/scaffold/SettingsScaffold.kt
@@ -39,6 +39,7 @@
import com.android.settingslib.spa.framework.compose.horizontalValues
import com.android.settingslib.spa.framework.compose.verticalValues
import com.android.settingslib.spa.framework.theme.SettingsTheme
+import com.android.settingslib.spa.framework.theme.isSpaExpressiveEnabled
import com.android.settingslib.spa.framework.theme.settingsBackground
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
@@ -55,6 +56,10 @@
) {
ActivityTitle(title)
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
+ if (isSpaExpressiveEnabled) {
+ LaunchedEffect(scrollBehavior.state.heightOffsetLimit) { scrollBehavior.collapse() }
+ }
+
Scaffold(
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
topBar = { SettingsTopAppBar(title, scrollBehavior, actions) },
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Spinner.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Spinner.kt
index c48a147..6b2db90 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Spinner.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Spinner.kt
@@ -46,6 +46,7 @@
import androidx.compose.ui.unit.dp
import com.android.settingslib.spa.framework.theme.SettingsDimension
import com.android.settingslib.spa.framework.theme.SettingsTheme
+import com.android.settingslib.spa.framework.theme.isSpaExpressiveEnabled
data class SpinnerOption(
val id: Int,
@@ -70,7 +71,10 @@
)
.selectableGroup(),
) {
- val contentPadding = PaddingValues(horizontal = SettingsDimension.itemPaddingEnd)
+ val contentPadding = if (isSpaExpressiveEnabled) PaddingValues(
+ horizontal = SettingsDimension.spinnerHorizontalPadding,
+ vertical = SettingsDimension.spinnerVerticalPadding
+ ) else PaddingValues(horizontal = SettingsDimension.itemPaddingEnd)
Button(
modifier = Modifier.semantics { role = Role.DropdownList },
onClick = { expanded = true },
@@ -129,7 +133,11 @@
text = option?.text ?: "",
modifier = modifier
.padding(end = SettingsDimension.itemPaddingEnd)
- .padding(vertical = SettingsDimension.itemPaddingAround),
+ .then(
+ if (!isSpaExpressiveEnabled)
+ Modifier.padding(vertical = SettingsDimension.itemPaddingAround)
+ else Modifier
+ ),
color = color,
style = MaterialTheme.typography.labelLarge,
)
diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/FakeZenModeRepository.kt b/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/FakeZenModeRepository.kt
index 043219a..c686708 100644
--- a/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/FakeZenModeRepository.kt
+++ b/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/FakeZenModeRepository.kt
@@ -61,6 +61,10 @@
mutableModesFlow.value += zenModes
}
+ fun addMode(mode: ZenMode) {
+ mutableModesFlow.value += mode
+ }
+
fun addMode(id: String, @AutomaticZenRule.Type type: Int = AutomaticZenRule.TYPE_UNKNOWN,
active: Boolean = false) {
mutableModesFlow.value += newMode(id, type, active)
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/IllustrationPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/IllustrationPreferenceTest.java
index ca53fc2..3f3e1b2 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/IllustrationPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/IllustrationPreferenceTest.java
@@ -291,4 +291,12 @@
assertThat(mPreference.isApplyDynamicColor()).isTrue();
}
+
+ @Test
+ public void setContentDescription_getContentDescription_isEqual() {
+ final String contentDesc = "content desc";
+ mPreference.setContentDescription(contentDesc);
+
+ assertThat(mPreference.getContentDescription().toString()).isEqualTo(contentDesc);
+ }
}
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 0b5187c..f3c5a18 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -941,6 +941,13 @@
<!-- Permission required for CTS test - FileIntegrityManagerTest -->
<uses-permission android:name="android.permission.SETUP_FSVERITY" />
+ <!-- Permissions required for CTS test - AppFunctionManagerTest -->
+ <uses-permission android:name="android.permission.EXECUTE_APP_FUNCTIONS_TRUSTED" />
+ <uses-permission android:name="android.permission.EXECUTE_APP_FUNCTIONS" />
+
+ <!-- Permission required for CTS test - CtsNfcTestCases -->
+ <uses-permission android:name="android.permission.NFC_SET_CONTROLLER_ALWAYS_ON" />
+
<application
android:label="@string/app_label"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_assistant_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_assistant.xml
similarity index 89%
rename from packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_assistant_24dp.xml
rename to packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_assistant.xml
index 9c3417f..6408a12 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_assistant_24dp.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_assistant.xml
@@ -1,6 +1,6 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
+ android:width="108dp"
+ android:height="108dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
android:tint="@color/colorControlNormal">
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_brightness_down_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_brightness_down.xml
similarity index 92%
rename from packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_brightness_down_24dp.xml
rename to packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_brightness_down.xml
index a64a0d1..f13239c 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_brightness_down_24dp.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_brightness_down.xml
@@ -1,6 +1,6 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
+ android:width="108dp"
+ android:height="108dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
android:tint="@color/colorControlNormal">
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_brightness_up_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_brightness_up.xml
similarity index 95%
rename from packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_brightness_up_24dp.xml
rename to packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_brightness_up.xml
index 40423c7..a5d15f9 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_brightness_up_24dp.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_brightness_up.xml
@@ -15,8 +15,8 @@
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
+ android:width="108dp"
+ android:height="108dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
android:tint="@color/colorControlNormal">
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_lock_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_lock.xml
similarity index 90%
rename from packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_lock_24dp.xml
rename to packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_lock.xml
index a0f7b5d..2127632 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_lock_24dp.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_lock.xml
@@ -1,6 +1,6 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
+ android:width="108dp"
+ android:height="108dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
android:tint="@color/colorControlNormal">
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_notifications_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_notifications.xml
similarity index 90%
rename from packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_notifications_24dp.xml
rename to packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_notifications.xml
index 8757f22..62c8d1d 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_notifications_24dp.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_notifications.xml
@@ -1,6 +1,6 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
+ android:width="108dp"
+ android:height="108dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
android:tint="@color/colorControlNormal">
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_power_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_power.xml
similarity index 90%
rename from packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_power_24dp.xml
rename to packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_power.xml
index 049013a..ed11b44 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_power_24dp.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_power.xml
@@ -1,6 +1,6 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
+ android:width="108dp"
+ android:height="108dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
android:tint="@color/colorControlNormal">
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_quick_settings_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_quick_settings.xml
similarity index 97%
rename from packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_quick_settings_24dp.xml
rename to packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_quick_settings.xml
index 4f25e7d..2da63a6 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_quick_settings_24dp.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_quick_settings.xml
@@ -15,8 +15,8 @@
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
+ android:width="108dp"
+ android:height="108dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
android:tint="@color/colorControlNormal">
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_recent_apps_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_recent_apps.xml
similarity index 94%
rename from packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_recent_apps_24dp.xml
rename to packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_recent_apps.xml
index 38234c0..9763b8e 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_recent_apps_24dp.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_recent_apps.xml
@@ -15,8 +15,8 @@
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
+ android:width="108dp"
+ android:height="108dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/colorControlNormal">
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_screenshot_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_screenshot.xml
similarity index 95%
rename from packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_screenshot_24dp.xml
rename to packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_screenshot.xml
index 6d7f49c..2bfbd5b 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_screenshot_24dp.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_screenshot.xml
@@ -15,8 +15,8 @@
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
+ android:width="108dp"
+ android:height="108dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
android:tint="@color/colorControlNormal">
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_settings_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_settings.xml
similarity index 95%
rename from packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_settings_24dp.xml
rename to packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_settings.xml
index 5ed6f19..4ca9bfc 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_settings_24dp.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_settings.xml
@@ -15,8 +15,8 @@
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
+ android:width="108dp"
+ android:height="108dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
android:tint="@color/colorControlNormal">
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_volume_down_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_volume_down.xml
similarity index 94%
rename from packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_volume_down_24dp.xml
rename to packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_volume_down.xml
index 16653e8..f924e5e 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_volume_down_24dp.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_volume_down.xml
@@ -15,8 +15,8 @@
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
+ android:width="108dp"
+ android:height="108dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
android:tint="@color/colorControlNormal">
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_volume_up_24dp.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_volume_up.xml
similarity index 95%
rename from packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_volume_up_24dp.xml
rename to packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_volume_up.xml
index e572c6a..41fe351 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_volume_up_24dp.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/ic_logo_a11y_volume_up.xml
@@ -15,8 +15,8 @@
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
+ android:width="108dp"
+ android:height="108dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
android:tint="@color/colorControlNormal">
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/menuitem_background_ripple.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/menuitem_background_ripple.xml
new file mode 100644
index 0000000..6cab464
--- /dev/null
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/drawable/menuitem_background_ripple.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2024 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+ android:color="@color/ripple_material_color" />
\ No newline at end of file
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/layout/grid_item.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/grid_item.xml
index 3c73eca..a1130e6 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/layout/grid_item.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/grid_item.xml
@@ -12,7 +12,8 @@
android:layout_height="@dimen/image_button_height"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
- android:scaleType="fitCenter"/>
+ android:scaleType="fitCenter"
+ android:background="@drawable/menuitem_background_ripple" />
<TextView
android:id="@+id/shortcutLabel"
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/model/A11yMenuShortcut.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/model/A11yMenuShortcut.java
index c698d18..11ce41e 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/model/A11yMenuShortcut.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/model/A11yMenuShortcut.java
@@ -53,73 +53,73 @@
/** Map stores all shortcut resource IDs that is in matching order of defined shortcut. */
private static final Map<ShortcutId, int[]> sShortcutResource = Map.ofEntries(
Map.entry(ShortcutId.ID_ASSISTANT_VALUE, new int[] {
- R.drawable.ic_logo_a11y_assistant_24dp,
+ R.drawable.ic_logo_a11y_assistant,
R.color.assistant_color,
R.string.assistant_utterance,
R.string.assistant_label,
}),
Map.entry(ShortcutId.ID_A11YSETTING_VALUE, new int[] {
- R.drawable.ic_logo_a11y_settings_24dp,
+ R.drawable.ic_logo_a11y_settings,
R.color.a11y_settings_color,
R.string.a11y_settings_label,
R.string.a11y_settings_label,
}),
Map.entry(ShortcutId.ID_POWER_VALUE, new int[] {
- R.drawable.ic_logo_a11y_power_24dp,
+ R.drawable.ic_logo_a11y_power,
R.color.power_color,
R.string.power_utterance,
R.string.power_label,
}),
Map.entry(ShortcutId.ID_RECENT_VALUE, new int[] {
- R.drawable.ic_logo_a11y_recent_apps_24dp,
+ R.drawable.ic_logo_a11y_recent_apps,
R.color.recent_apps_color,
R.string.recent_apps_label,
R.string.recent_apps_label,
}),
Map.entry(ShortcutId.ID_LOCKSCREEN_VALUE, new int[] {
- R.drawable.ic_logo_a11y_lock_24dp,
+ R.drawable.ic_logo_a11y_lock,
R.color.lockscreen_color,
R.string.lockscreen_label,
R.string.lockscreen_label,
}),
Map.entry(ShortcutId.ID_QUICKSETTING_VALUE, new int[] {
- R.drawable.ic_logo_a11y_quick_settings_24dp,
+ R.drawable.ic_logo_a11y_quick_settings,
R.color.quick_settings_color,
R.string.quick_settings_label,
R.string.quick_settings_label,
}),
Map.entry(ShortcutId.ID_NOTIFICATION_VALUE, new int[] {
- R.drawable.ic_logo_a11y_notifications_24dp,
+ R.drawable.ic_logo_a11y_notifications,
R.color.notifications_color,
R.string.notifications_label,
R.string.notifications_label,
}),
Map.entry(ShortcutId.ID_SCREENSHOT_VALUE, new int[] {
- R.drawable.ic_logo_a11y_screenshot_24dp,
+ R.drawable.ic_logo_a11y_screenshot,
R.color.screenshot_color,
R.string.screenshot_utterance,
R.string.screenshot_label,
}),
Map.entry(ShortcutId.ID_BRIGHTNESS_UP_VALUE, new int[] {
- R.drawable.ic_logo_a11y_brightness_up_24dp,
+ R.drawable.ic_logo_a11y_brightness_up,
R.color.brightness_color,
R.string.brightness_up_label,
R.string.brightness_up_label,
}),
Map.entry(ShortcutId.ID_BRIGHTNESS_DOWN_VALUE, new int[] {
- R.drawable.ic_logo_a11y_brightness_down_24dp,
+ R.drawable.ic_logo_a11y_brightness_down,
R.color.brightness_color,
R.string.brightness_down_label,
R.string.brightness_down_label,
}),
Map.entry(ShortcutId.ID_VOLUME_UP_VALUE, new int[] {
- R.drawable.ic_logo_a11y_volume_up_24dp,
+ R.drawable.ic_logo_a11y_volume_up,
R.color.volume_color,
R.string.volume_up_label,
R.string.volume_up_label,
}),
Map.entry(ShortcutId.ID_VOLUME_DOWN_VALUE, new int[] {
- R.drawable.ic_logo_a11y_volume_down_24dp,
+ R.drawable.ic_logo_a11y_volume_down,
R.color.volume_color,
R.string.volume_down_label,
R.string.volume_down_label,
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/utils/ShortcutDrawableUtils.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/utils/ShortcutDrawableUtils.java
deleted file mode 100644
index 28ba4b5..0000000
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/utils/ShortcutDrawableUtils.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.accessibility.accessibilitymenu.utils;
-
-import android.content.Context;
-import android.content.res.ColorStateList;
-import android.graphics.Bitmap;
-import android.graphics.Bitmap.Config;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.Paint.Style;
-import android.graphics.drawable.AdaptiveIconDrawable;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.LayerDrawable;
-import android.graphics.drawable.RippleDrawable;
-
-import com.android.systemui.accessibility.accessibilitymenu.R;
-
-/** Creates background drawable for a11y menu shortcut. */
-public class ShortcutDrawableUtils {
-
- /**
- * To make the circular background of shortcut icons have higher resolution. The higher value of
- * LENGTH is, the higher resolution of the circular background are.
- */
- private static final int LENGTH = 480;
-
- private static final int RADIUS = LENGTH / 2;
- private static final int COORDINATE = LENGTH / 2;
- private static final int RIPPLE_COLOR_ID = R.color.ripple_material_color;
-
- private final Context mContext;
- private final ColorStateList mRippleColorStateList;
-
- // Placeholder of drawable to prevent NullPointerException
- private final ColorDrawable mTransparentDrawable = new ColorDrawable(Color.TRANSPARENT);
-
- public ShortcutDrawableUtils(Context context) {
- this.mContext = context;
-
- int rippleColor = context.getColor(RIPPLE_COLOR_ID);
- mRippleColorStateList = ColorStateList.valueOf(rippleColor);
- }
-
- /**
- * Creates a circular drawable in specific color for shortcut.
- *
- * @param colorResId color resource ID
- * @return drawable circular drawable
- */
- public Drawable createCircularDrawable(int colorResId) {
- Bitmap output = Bitmap.createBitmap(LENGTH, LENGTH, Config.ARGB_8888);
- Canvas canvas = new Canvas(output);
- int color = mContext.getColor(colorResId);
- Paint paint = new Paint();
- paint.setColor(color);
- paint.setStrokeCap(Paint.Cap.ROUND);
- paint.setStyle(Style.FILL);
- canvas.drawCircle(COORDINATE, COORDINATE, RADIUS, paint);
-
- BitmapDrawable drawable = new BitmapDrawable(mContext.getResources(), output);
- return drawable;
- }
-
- /**
- * Creates an adaptive icon drawable in specific color for shortcut.
- *
- * @param colorResId color resource ID
- * @return drawable for adaptive icon
- */
- public Drawable createAdaptiveIconDrawable(int colorResId) {
- Drawable circleLayer = createCircularDrawable(colorResId);
- RippleDrawable rippleLayer = new RippleDrawable(mRippleColorStateList, null, null);
-
- AdaptiveIconDrawable adaptiveIconDrawable =
- new AdaptiveIconDrawable(circleLayer, mTransparentDrawable);
-
- Drawable[] layers = {adaptiveIconDrawable, rippleLayer};
- LayerDrawable drawable = new LayerDrawable(layers);
- return drawable;
- }
-}
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuAdapter.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuAdapter.java
index c333a7a..aa1bbbd 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuAdapter.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuAdapter.java
@@ -16,7 +16,12 @@
package com.android.systemui.accessibility.accessibilitymenu.view;
+import android.content.res.Resources;
import android.graphics.Rect;
+import android.graphics.drawable.AdaptiveIconDrawable;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.InsetDrawable;
import android.view.LayoutInflater;
import android.view.TouchDelegate;
import android.view.View;
@@ -26,11 +31,12 @@
import android.widget.ImageButton;
import android.widget.TextView;
+import androidx.annotation.NonNull;
+
import com.android.systemui.accessibility.accessibilitymenu.AccessibilityMenuService;
import com.android.systemui.accessibility.accessibilitymenu.R;
import com.android.systemui.accessibility.accessibilitymenu.activity.A11yMenuSettingsActivity.A11yMenuPreferenceFragment;
import com.android.systemui.accessibility.accessibilitymenu.model.A11yMenuShortcut;
-import com.android.systemui.accessibility.accessibilitymenu.utils.ShortcutDrawableUtils;
import java.util.List;
@@ -43,16 +49,12 @@
private final AccessibilityMenuService mService;
private final List<A11yMenuShortcut> mShortcutDataList;
- private final ShortcutDrawableUtils mShortcutDrawableUtils;
public A11yMenuAdapter(
AccessibilityMenuService service,
List<A11yMenuShortcut> shortcutDataList) {
this.mService = service;
this.mShortcutDataList = shortcutDataList;
-
- mShortcutDrawableUtils = new ShortcutDrawableUtils(service);
-
mLargeTextSize =
service.getResources().getDimensionPixelOffset(R.dimen.large_label_text_size);
}
@@ -152,10 +154,10 @@
shortcutIconButton.setContentDescription(
mService.getString(shortcutItem.imgContentDescription));
shortcutLabel.setText(shortcutItem.labelText);
- shortcutIconButton.setImageResource(shortcutItem.imageSrc);
- shortcutIconButton.setBackground(
- mShortcutDrawableUtils.createAdaptiveIconDrawable(shortcutItem.imageColor));
+ AdaptiveIconDrawable iconDrawable = getAdaptiveIconDrawable(convertView,
+ shortcutItem);
+ shortcutIconButton.setImageDrawable(iconDrawable);
shortcutIconButton.setAccessibilityDelegate(new View.AccessibilityDelegate() {
@Override
@@ -167,4 +169,18 @@
});
}
}
+
+ @NonNull
+ private static AdaptiveIconDrawable getAdaptiveIconDrawable(@NonNull View convertView,
+ @NonNull A11yMenuShortcut shortcutItem) {
+ Resources resources = convertView.getResources();
+ // Note: from the official guide, the foreground image of the adaptive icon should be
+ // sized at 108 x 108 dp
+ Drawable icon = resources.getDrawable(shortcutItem.imageSrc);
+ float inset = AdaptiveIconDrawable.getExtraInsetFraction();
+ AdaptiveIconDrawable iconDrawable = new AdaptiveIconDrawable(
+ new ColorDrawable(resources.getColor(shortcutItem.imageColor)),
+ new InsetDrawable(icon, inset));
+ return iconDrawable;
+ }
}
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuViewPager.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuViewPager.java
index 35f1248..b899c45 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuViewPager.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuViewPager.java
@@ -306,6 +306,10 @@
(viewPagerHeight - topMargin - defaultMargin
- (rowsInGridView * gridItemHeight))
/ (rowsInGridView + 1);
+ // The interval is negative number when the viewPagerHeight is not able to fit
+ // the grid items, which result in text overlapping.
+ // Adjust the interval to 0 could solve the issue.
+ interval = Math.max(interval, 0);
mViewPagerAdapter.setVerticalSpacing(interval);
// Sets padding to view pager.
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index d1a59af..892f778 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -1396,3 +1396,9 @@
}
}
+flag {
+ name: "non_touchscreen_devices_bypass_falsing"
+ namespace: "systemui"
+ description: "Allow non-touchscreen devices to bypass falsing"
+ bug: "319809270"
+}
\ No newline at end of file
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
index ed12776..a4dc8fc 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
@@ -216,7 +216,7 @@
/** Scene containing the glanceable hub UI. */
@Composable
-private fun SceneScope.CommunalScene(
+fun SceneScope.CommunalScene(
backgroundType: CommunalBackgroundType,
colors: CommunalColors,
content: CommunalContent,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
index 201c419..c63b29d 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
@@ -65,6 +65,7 @@
import androidx.compose.foundation.lazy.grid.GridItemSpan
import androidx.compose.foundation.lazy.grid.LazyGridState
import androidx.compose.foundation.lazy.grid.LazyHorizontalGrid
+import androidx.compose.foundation.lazy.grid.itemsIndexed
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.selection.selectable
@@ -136,6 +137,7 @@
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.customActions
import androidx.compose.ui.semantics.onClick
+import androidx.compose.ui.semantics.paneTitle
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.semantics.testTagsAsResourceId
import androidx.compose.ui.text.style.TextAlign
@@ -240,10 +242,15 @@
}
}
+ val paneTitle = stringResource(R.string.accessibility_content_description_for_communal_hub)
+
Box(
modifier =
modifier
- .semantics { testTagsAsResourceId = true }
+ .semantics {
+ testTagsAsResourceId = true
+ this.paneTitle = paneTitle
+ }
.testTag(COMMUNAL_HUB_TEST_TAG)
.fillMaxSize()
// Observe taps for selecting items
@@ -690,21 +697,20 @@
horizontalArrangement = Arrangement.spacedBy(Dimensions.ItemSpacing),
verticalArrangement = Arrangement.spacedBy(Dimensions.ItemSpacing),
) {
- items(
- count = list.size,
- key = { index -> list[index].key },
- contentType = { index -> list[index].key },
- span = { index -> GridItemSpan(list[index].size.span) },
- ) { index ->
+ itemsIndexed(
+ items = list,
+ key = { _, item -> item.key },
+ contentType = { _, item -> item.key },
+ span = { _, item -> GridItemSpan(item.size.span) },
+ ) { index, item ->
val size =
SizeF(
Dimensions.CardWidth.value,
- list[index].size.dp().value,
+ item.size.dp().value,
)
val cardModifier = Modifier.requiredSize(width = size.width.dp, height = size.height.dp)
if (viewModel.isEditMode && dragDropState != null) {
- val selected by
- remember(index) { derivedStateOf { list[index].key == selectedKey.value } }
+ val selected = item.key == selectedKey.value
DraggableItem(
modifier =
if (dragDropState.draggingItemIndex == index) {
@@ -716,12 +722,12 @@
},
dragDropState = dragDropState,
selected = selected,
- enabled = list[index].isWidgetContent(),
+ enabled = item.isWidgetContent(),
index = index,
) { isDragging ->
CommunalContent(
modifier = cardModifier,
- model = list[index],
+ model = item,
viewModel = viewModel,
size = size,
selected = selected && !isDragging,
@@ -734,7 +740,7 @@
}
} else {
CommunalContent(
- model = list[index],
+ model = item,
viewModel = viewModel,
size = size,
selected = false,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt
index 8b6de6a..e41a7df 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalScene.kt
@@ -17,20 +17,21 @@
package com.android.systemui.communal.ui.compose
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
+import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.scene.SceneScope
import com.android.compose.animation.scene.Swipe
import com.android.compose.animation.scene.SwipeDirection
import com.android.compose.animation.scene.UserAction
import com.android.compose.animation.scene.UserActionResult
-import com.android.systemui.communal.ui.view.layout.sections.CommunalAppWidgetSection
+import com.android.systemui.communal.shared.model.CommunalBackgroundType
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
-import com.android.systemui.communal.widgets.WidgetInteractionHandler
+import com.android.systemui.communal.util.CommunalColors
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.lifecycle.ExclusiveActivatable
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.scene.ui.composable.Scene
-import com.android.systemui.statusbar.phone.SystemUIDialogFactory
import javax.inject.Inject
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.flow.Flow
@@ -43,9 +44,8 @@
@Inject
constructor(
private val viewModel: CommunalViewModel,
- private val dialogFactory: SystemUIDialogFactory,
- private val interactionHandler: WidgetInteractionHandler,
- private val widgetSection: CommunalAppWidgetSection,
+ private val communalColors: CommunalColors,
+ private val communalContent: CommunalContent,
) : ExclusiveActivatable(), Scene {
override val key = Scenes.Communal
@@ -63,12 +63,17 @@
@Composable
override fun SceneScope.Content(modifier: Modifier) {
- CommunalHub(
- modifier = modifier,
+ val backgroundType by
+ viewModel.communalBackground.collectAsStateWithLifecycle(
+ initialValue = CommunalBackgroundType.ANIMATED
+ )
+
+ CommunalScene(
+ backgroundType = backgroundType,
+ colors = communalColors,
+ content = communalContent,
viewModel = viewModel,
- interactionHandler = interactionHandler,
- widgetSection = widgetSection,
- dialogFactory = dialogFactory,
+ modifier = modifier.horizontalNestedScrollToScene(),
)
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/AlternateBouncer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/AlternateBouncer.kt
index ecb3d8c..c25a45d 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/AlternateBouncer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/AlternateBouncer.kt
@@ -52,6 +52,7 @@
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerDependencies
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerMessageAreaViewModel
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerUdfpsIconViewModel
+import com.android.systemui.log.LongPressHandlingViewLogger
import com.android.systemui.res.R
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -97,6 +98,7 @@
Box {
DeviceEntryIcon(
viewModel = alternateBouncerDependencies.udfpsIconViewModel,
+ logger = alternateBouncerDependencies.logger,
modifier =
Modifier.width { udfpsLocation.width }
.height { udfpsLocation.height }
@@ -151,13 +153,14 @@
@Composable
private fun DeviceEntryIcon(
viewModel: AlternateBouncerUdfpsIconViewModel,
+ logger: LongPressHandlingViewLogger,
modifier: Modifier = Modifier,
) {
AndroidView(
modifier = modifier,
factory = { context ->
val view =
- DeviceEntryIconView(context, null).apply {
+ DeviceEntryIconView(context, null, logger = logger).apply {
id = R.id.alternate_bouncer_udfps_icon_view
contentDescription =
context.resources.getString(R.string.accessibility_fingerprint_label)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt
index 4129c25..a525f36 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt
@@ -18,7 +18,6 @@
import android.content.Context
import android.util.DisplayMetrics
-import android.view.View
import android.view.WindowManager
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
@@ -45,9 +44,11 @@
import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryBackgroundViewModel
import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryForegroundViewModel
import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryIconViewModel
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LongPressHandlingViewLogger
+import com.android.systemui.log.dagger.LongPressTouchLog
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.res.R
-import com.android.systemui.shade.NotificationPanelView
import com.android.systemui.statusbar.VibratorHelper
import dagger.Lazy
import javax.inject.Inject
@@ -66,7 +67,7 @@
private val deviceEntryBackgroundViewModel: Lazy<DeviceEntryBackgroundViewModel>,
private val falsingManager: Lazy<FalsingManager>,
private val vibratorHelper: Lazy<VibratorHelper>,
- private val notificationPanelView: NotificationPanelView,
+ @LongPressTouchLog private val logBuffer: LogBuffer,
) {
@Composable
fun SceneScope.LockIcon(overrideColor: Color? = null, modifier: Modifier = Modifier) {
@@ -74,29 +75,30 @@
return
}
- notificationPanelView.findViewById<View?>(R.id.lock_icon_view)?.let {
- notificationPanelView.removeView(it)
- }
-
val context = LocalContext.current
AndroidView(
factory = { context ->
val view =
if (DeviceEntryUdfpsRefactor.isEnabled) {
- DeviceEntryIconView(context, null).apply {
- id = R.id.device_entry_icon_view
- DeviceEntryIconViewBinder.bind(
- applicationScope,
- this,
- deviceEntryIconViewModel.get(),
- deviceEntryForegroundViewModel.get(),
- deviceEntryBackgroundViewModel.get(),
- falsingManager.get(),
- vibratorHelper.get(),
- overrideColor,
+ DeviceEntryIconView(
+ context,
+ null,
+ logger = LongPressHandlingViewLogger(logBuffer, tag = TAG)
)
- }
+ .apply {
+ id = R.id.device_entry_icon_view
+ DeviceEntryIconViewBinder.bind(
+ applicationScope,
+ this,
+ deviceEntryIconViewModel.get(),
+ deviceEntryForegroundViewModel.get(),
+ deviceEntryBackgroundViewModel.get(),
+ falsingManager.get(),
+ vibratorHelper.get(),
+ overrideColor,
+ )
+ }
} else {
// KeyguardBottomAreaRefactor.isEnabled
LockIconView(context, null).apply {
@@ -185,6 +187,10 @@
return IntRect(center, radius)
}
+
+ companion object {
+ private const val TAG = "LockSection"
+ }
}
private val LockIconElementKey = ElementKey("LockIcon")
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt
index 4b3a39b..897a861 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/NotificationStackNestedScrollConnection.kt
@@ -23,11 +23,13 @@
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
-import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.unit.IntOffset
import com.android.compose.nestedscroll.PriorityNestedScrollConnection
+import com.android.systemui.common.ui.compose.windowinsets.LocalRawScreenHeight
+import kotlin.math.max
import kotlin.math.roundToInt
+import kotlin.math.tanh
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
@@ -36,6 +38,7 @@
coroutineScope: CoroutineScope,
canScrollForward: () -> Boolean
): Modifier {
+ val screenHeight = LocalRawScreenHeight.current
val overscrollOffset = remember { Animatable(0f) }
val stackNestedScrollConnection = remember {
NotificationStackNestedScrollConnection(
@@ -43,7 +46,13 @@
canScrollForward = canScrollForward,
onScroll = { offsetAvailable ->
coroutineScope.launch {
- overscrollOffset.snapTo(overscrollOffset.value + offsetAvailable * 0.3f)
+ val maxProgress = screenHeight * 0.2f
+ val tilt = 3f
+ var offset =
+ overscrollOffset.value +
+ maxProgress * tanh(x = offsetAvailable / (maxProgress * tilt))
+ offset = max(offset, -1f * maxProgress)
+ overscrollOffset.snapTo(offset)
}
},
onStop = { velocityAvailable ->
@@ -79,13 +88,7 @@
offsetAvailable < 0f && offsetBeforeStart < 0f && !canScrollForward()
},
canStartPostFling = { velocityAvailable -> velocityAvailable < 0f && !canScrollForward() },
- canContinueScroll = { source ->
- if (source == NestedScrollSource.SideEffect) {
- stackOffset() > STACK_OVERSCROLL_FLING_MIN_OFFSET
- } else {
- true
- }
- },
+ canContinueScroll = { stackOffset() > 0f },
canScrollOnFling = true,
onStart = { offsetAvailable -> onStart(offsetAvailable) },
onScroll = { offsetAvailable ->
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
index a2beba8..91ecfc1 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt
@@ -667,4 +667,3 @@
private val DEBUG_BOX_COLOR = Color(0f, 1f, 0f, 0.2f)
private const val HUN_SNOOZE_POSITIONAL_THRESHOLD_FRACTION = 0.25f
private const val HUN_SNOOZE_VELOCITY_THRESHOLD = -70f
-internal const val STACK_OVERSCROLL_FLING_MIN_OFFSET = -100f
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToCommunalTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToCommunalTransition.kt
index 5401936..826a255 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToCommunalTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/transitions/FromLockscreenToCommunalTransition.kt
@@ -19,14 +19,19 @@
import androidx.compose.animation.core.tween
import com.android.compose.animation.scene.Edge
import com.android.compose.animation.scene.TransitionBuilder
+import com.android.systemui.communal.ui.compose.AllElements
+import com.android.systemui.communal.ui.compose.Communal
import com.android.systemui.scene.shared.model.Scenes
fun TransitionBuilder.lockscreenToCommunalTransition() {
- spec = tween(durationMillis = 500)
+ spec = tween(durationMillis = 1000)
- // Translate lockscreen to the left.
+ // Translate lockscreen to the start direction.
translate(Scenes.Lockscreen.rootElementKey, Edge.Start)
- // Translate communal from the right.
- translate(Scenes.Communal.rootElementKey, Edge.End)
+ // Translate communal hub grid from the end direction.
+ translate(Communal.Elements.Grid, Edge.End)
+
+ // Fade all communal hub elements.
+ timestampRange(startMillis = 167, endMillis = 334) { fade(AllElements) }
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
index 24fef71..f3577fa 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/DraggableHandler.kt
@@ -190,14 +190,12 @@
private fun computeSwipes(startedPosition: Offset?, pointersDown: Int): Swipes {
val fromSource =
startedPosition?.let { position ->
- layoutImpl.swipeSourceDetector
- .source(
- layoutImpl.lastSize,
- position.round(),
- layoutImpl.density,
- orientation,
- )
- ?.resolve(layoutImpl.layoutDirection)
+ layoutImpl.swipeSourceDetector.source(
+ layoutImpl.lastSize,
+ position.round(),
+ layoutImpl.density,
+ orientation,
+ )
}
val upOrLeft =
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/EdgeDetector.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/EdgeDetector.kt
index 97c0cef..edd697b 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/EdgeDetector.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/EdgeDetector.kt
@@ -54,23 +54,23 @@
position: IntOffset,
density: Density,
orientation: Orientation,
- ): Edge? {
+ ): Edge.Resolved? {
val axisSize: Int
val axisPosition: Int
- val topOrLeft: Edge
- val bottomOrRight: Edge
+ val topOrLeft: Edge.Resolved
+ val bottomOrRight: Edge.Resolved
when (orientation) {
Orientation.Horizontal -> {
axisSize = layoutSize.width
axisPosition = position.x
- topOrLeft = Edge.Left
- bottomOrRight = Edge.Right
+ topOrLeft = Edge.Resolved.Left
+ bottomOrRight = Edge.Resolved.Right
}
Orientation.Vertical -> {
axisSize = layoutSize.height
axisPosition = position.y
- topOrLeft = Edge.Top
- bottomOrRight = Edge.Bottom
+ topOrLeft = Edge.Resolved.Top
+ bottomOrRight = Edge.Resolved.Bottom
}
}
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
index 061613f..004bb40 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayout.kt
@@ -379,6 +379,10 @@
return this to UserActionResult(toScene = scene)
}
+ infix fun to(overlay: OverlayKey): Pair<UserAction, UserActionResult> {
+ return this to UserActionResult(toOverlay = overlay)
+ }
+
/** Resolve this into a [Resolved] user action given [layoutDirection]. */
internal abstract fun resolve(layoutDirection: LayoutDirection): Resolved
@@ -475,7 +479,7 @@
position: IntOffset,
density: Density,
orientation: Orientation,
- ): SwipeSource?
+ ): SwipeSource.Resolved?
}
/** The result of performing a [UserAction]. */
diff --git a/packages/SystemUI/compose/scene/src/com/android/systemui/communal/ui/compose/CommunalSwipeDetector.kt b/packages/SystemUI/compose/scene/src/com/android/systemui/communal/ui/compose/CommunalSwipeDetector.kt
index 3fda9b8..41b015a 100644
--- a/packages/SystemUI/compose/scene/src/com/android/systemui/communal/ui/compose/CommunalSwipeDetector.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/systemui/communal/ui/compose/CommunalSwipeDetector.kt
@@ -33,7 +33,7 @@
* SwipeSourceDetector} to enable fullscreen swipe handling to transition to and from the glanceable
* hub.
*/
-class CommunalSwipeDetector(private var lastDirection: SwipeSource? = null) :
+class CommunalSwipeDetector(private var lastDirection: SwipeSource.Resolved? = null) :
SwipeSourceDetector, SwipeDetector {
companion object {
private const val TRAVEL_RATIO_THRESHOLD = .5f
@@ -44,15 +44,15 @@
position: IntOffset,
density: Density,
orientation: Orientation
- ): SwipeSource? {
+ ): SwipeSource.Resolved? {
return lastDirection
}
override fun detectSwipe(change: PointerInputChange): Boolean {
if (change.positionChange().x > 0) {
- lastDirection = Edge.Left
+ lastDirection = Edge.Resolved.Left
} else {
- lastDirection = Edge.Right
+ lastDirection = Edge.Resolved.Right
}
// Determine whether the ratio of the distance traveled horizontally to the distance
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/FixedSizeEdgeDetectorTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/FixedSizeEdgeDetectorTest.kt
index cceaf57..dea9283 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/FixedSizeEdgeDetectorTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/FixedSizeEdgeDetectorTest.kt
@@ -34,7 +34,7 @@
@Test
fun horizontalEdges() {
- fun horizontalEdge(position: Int): Edge? =
+ fun horizontalEdge(position: Int): Edge.Resolved? =
detector.source(
layoutSize,
position = IntOffset(position, 0),
@@ -42,17 +42,17 @@
Orientation.Horizontal,
)
- assertThat(horizontalEdge(0)).isEqualTo(Edge.Left)
- assertThat(horizontalEdge(30)).isEqualTo(Edge.Left)
+ assertThat(horizontalEdge(0)).isEqualTo(Edge.Resolved.Left)
+ assertThat(horizontalEdge(30)).isEqualTo(Edge.Resolved.Left)
assertThat(horizontalEdge(31)).isEqualTo(null)
assertThat(horizontalEdge(69)).isEqualTo(null)
- assertThat(horizontalEdge(70)).isEqualTo(Edge.Right)
- assertThat(horizontalEdge(100)).isEqualTo(Edge.Right)
+ assertThat(horizontalEdge(70)).isEqualTo(Edge.Resolved.Right)
+ assertThat(horizontalEdge(100)).isEqualTo(Edge.Resolved.Right)
}
@Test
fun verticalEdges() {
- fun verticalEdge(position: Int): Edge? =
+ fun verticalEdge(position: Int): Edge.Resolved? =
detector.source(
layoutSize,
position = IntOffset(0, position),
@@ -60,11 +60,11 @@
Orientation.Vertical,
)
- assertThat(verticalEdge(0)).isEqualTo(Edge.Top)
- assertThat(verticalEdge(30)).isEqualTo(Edge.Top)
+ assertThat(verticalEdge(0)).isEqualTo(Edge.Resolved.Top)
+ assertThat(verticalEdge(30)).isEqualTo(Edge.Resolved.Top)
assertThat(verticalEdge(31)).isEqualTo(null)
assertThat(verticalEdge(69)).isEqualTo(null)
- assertThat(verticalEdge(70)).isEqualTo(Edge.Bottom)
- assertThat(verticalEdge(100)).isEqualTo(Edge.Bottom)
+ assertThat(verticalEdge(70)).isEqualTo(Edge.Resolved.Bottom)
+ assertThat(verticalEdge(100)).isEqualTo(Edge.Resolved.Bottom)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogManagerTest.java
index 09aa2868..ca23228 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogManagerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogManagerTest.java
@@ -17,11 +17,11 @@
package com.android.systemui.accessibility.hearingaid;
import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.bluetooth.BluetoothDevice;
import android.testing.TestableLooper;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -51,6 +51,8 @@
@Rule
public MockitoRule mockito = MockitoJUnit.rule();
+ private static final int TEST_LAUNCH_SOURCE_ID = 1;
+
private final FakeExecutor mMainExecutor = new FakeExecutor(new FakeSystemClock());
private final FakeExecutor mBackgroundExecutor = new FakeExecutor(new FakeSystemClock());
@Mock
@@ -70,7 +72,7 @@
@Before
public void setUp() {
- when(mDialogFactory.create(anyBoolean())).thenReturn(mDialogDelegate);
+ when(mDialogFactory.create(anyBoolean(), anyInt())).thenReturn(mDialogDelegate);
when(mDialogDelegate.createDialog()).thenReturn(mDialog);
mManager = new HearingDevicesDialogManager(
@@ -86,21 +88,22 @@
public void showDialog_existHearingDevice_showPairNewDeviceFalse() {
when(mDevicesChecker.isAnyPairedHearingDevice()).thenReturn(true);
- mManager.showDialog(mExpandable);
+ mManager.showDialog(mExpandable, TEST_LAUNCH_SOURCE_ID);
mBackgroundExecutor.runAllReady();
mMainExecutor.runAllReady();
- verify(mDialogFactory).create(eq(/* showPairNewDevice= */ false));
+ verify(mDialogFactory).create(eq(/* showPairNewDevice= */ false),
+ eq(TEST_LAUNCH_SOURCE_ID));
}
@Test
public void showDialog_noHearingDevice_showPairNewDeviceTrue() {
when(mDevicesChecker.isAnyPairedHearingDevice()).thenReturn(false);
- mManager.showDialog(mExpandable);
+ mManager.showDialog(mExpandable, TEST_LAUNCH_SOURCE_ID);
mBackgroundExecutor.runAllReady();
mMainExecutor.runAllReady();
- verify(mDialogFactory).create(eq(/* showPairNewDevice= */ true));
+ verify(mDialogFactory).create(eq(/* showPairNewDevice= */ true), eq(TEST_LAUNCH_SOURCE_ID));
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java
index b7d99d2..65825b2 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -97,6 +97,8 @@
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
+import com.google.android.msdl.domain.MSDLPlayer;
+
import dagger.Lazy;
import org.junit.Before;
@@ -185,6 +187,8 @@
private Resources mResources;
@Mock
private VibratorHelper mVibratorHelper;
+ @Mock
+ private MSDLPlayer mMSDLPlayer;
private TestableContext mContextSpy;
private Execution mExecution;
@@ -1066,7 +1070,7 @@
() -> mLogContextInteractor, () -> mPromptSelectionInteractor,
() -> mCredentialViewModel, () -> mPromptViewModel, mInteractionJankMonitor,
mHandler, mBackgroundExecutor, mUdfpsUtils, mVibratorHelper,
- mLazyViewCapture);
+ mLazyViewCapture, mMSDLPlayer);
}
@Override
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorTest.kt
index 65236f0..e3b5f34 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorTest.kt
@@ -31,6 +31,8 @@
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.kosmos.testScope
+import com.android.systemui.scene.domain.interactor.sceneInteractor
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository
import com.android.systemui.telephony.data.repository.fakeTelephonyRepository
@@ -91,6 +93,8 @@
kosmos.fakeTelephonyRepository.setHasTelephonyRadio(true)
kosmos.telecomManager = telecomManager
+
+ kosmos.sceneInteractor.changeScene(Scenes.Bouncer, "")
}
@Test
@@ -130,6 +134,7 @@
assertThat(metricsLogger.logs.element().category)
.isEqualTo(MetricsProto.MetricsEvent.ACTION_EMERGENCY_CALL)
verify(activityTaskManager).stopSystemLockTaskMode()
+ assertThat(kosmos.sceneInteractor.currentScene.value).isEqualTo(Scenes.Lockscreen)
verify(telecomManager).showInCallScreen(eq(false))
}
@@ -156,6 +161,7 @@
assertThat(metricsLogger.logs.element().category)
.isEqualTo(MetricsProto.MetricsEvent.ACTION_EMERGENCY_CALL)
verify(activityTaskManager).stopSystemLockTaskMode()
+ assertThat(kosmos.sceneInteractor.currentScene.value).isEqualTo(Scenes.Lockscreen)
// TODO(b/25189994): Test the activity has been started once we switch to the
// ActivityStarter interface here.
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java
index 53d82d7..956c129 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/classifier/BrightLineFalsingManagerTest.java
@@ -95,6 +95,7 @@
when(mFalsingDataProvider.getRecentMotionEvents()).thenReturn(mMotionEventList);
when(mKeyguardStateController.isShowing()).thenReturn(true);
when(mFalsingDataProvider.isUnfolded()).thenReturn(false);
+ when(mFalsingDataProvider.isTouchScreenSource()).thenReturn(true);
mBrightLineFalsingManager = new BrightLineFalsingManager(mFalsingDataProvider,
mMetricsLogger, mClassifiers, mSingleTapClassifier, mLongTapClassifier,
mDoubleTapClassifier, mHistoryTracker, mKeyguardStateController,
@@ -193,6 +194,13 @@
}
@Test
+ public void testSkipNonTouchscreenDevices() {
+ assertThat(mBrightLineFalsingManager.isFalseTouch(Classifier.GENERIC)).isTrue();
+ when(mFalsingDataProvider.isTouchScreenSource()).thenReturn(false);
+ assertThat(mBrightLineFalsingManager.isFalseTouch(Classifier.GENERIC)).isFalse();
+ }
+
+ @Test
public void testTrackpadGesture() {
assertThat(mBrightLineFalsingManager.isFalseTouch(Classifier.GENERIC)).isTrue();
when(mFalsingDataProvider.isFromTrackpad()).thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/classifier/FalsingDataProviderTest.java
similarity index 81%
rename from packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/classifier/FalsingDataProviderTest.java
index 49c6239..df4b048 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingDataProviderTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/classifier/FalsingDataProviderTest.java
@@ -18,6 +18,7 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -25,13 +26,20 @@
import static org.mockito.Mockito.when;
import android.hardware.devicestate.DeviceStateManager.FoldStateListener;
+import android.hardware.input.IInputManager;
+import android.hardware.input.InputManagerGlobal;
+import android.os.RemoteException;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.util.DisplayMetrics;
+import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import com.android.systemui.Flags;
import com.android.systemui.classifier.FalsingDataProvider.GestureFinalizedListener;
import com.android.systemui.dock.DockManagerFake;
import com.android.systemui.statusbar.policy.BatteryController;
@@ -56,11 +64,15 @@
private FoldStateListener mFoldStateListener;
private final DockManagerFake mDockManager = new DockManagerFake();
private DisplayMetrics mDisplayMetrics;
+ private IInputManager mIInputManager;
+ private InputManagerGlobal.TestSession inputManagerGlobalTestSession;
@Before
public void setup() {
super.setup();
MockitoAnnotations.initMocks(this);
+ mIInputManager = mock(IInputManager.Stub.class);
+ inputManagerGlobalTestSession = InputManagerGlobal.createTestSession(mIInputManager);
mDisplayMetrics = new DisplayMetrics();
mDisplayMetrics.xdpi = 100;
mDisplayMetrics.ydpi = 100;
@@ -73,6 +85,7 @@
public void tearDown() {
super.tearDown();
mDataProvider.onSessionEnd();
+ inputManagerGlobalTestSession.close();
}
@Test
@@ -378,6 +391,79 @@
}
@Test
+ @DisableFlags(Flags.FLAG_NON_TOUCHSCREEN_DEVICES_BYPASS_FALSING)
+ public void test_isTouchscreenSource_flagOff_alwaysTrue() {
+ assertThat(mDataProvider.isTouchScreenSource()).isTrue();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_NON_TOUCHSCREEN_DEVICES_BYPASS_FALSING)
+ public void test_isTouchscreenSource_recentEventsEmpty_true() {
+ //send no events into the data provider
+ assertThat(mDataProvider.isTouchScreenSource()).isTrue();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_NON_TOUCHSCREEN_DEVICES_BYPASS_FALSING)
+ public void test_isTouchscreenSource_latestDeviceTouchscreen_true() throws RemoteException {
+ int deviceId = 999;
+
+ InputDevice device = new InputDevice.Builder()
+ .setSources(InputDevice.SOURCE_CLASS_TRACKBALL | InputDevice.SOURCE_TOUCHSCREEN)
+ .setId(deviceId)
+ .build();
+ when(mIInputManager.getInputDeviceIds()).thenReturn(new int[]{deviceId});
+ when(mIInputManager.getInputDevice(anyInt())).thenReturn(device);
+
+ MotionEvent event = MotionEvent.obtain(1, 0, MotionEvent.ACTION_UP, 1,
+ MotionEvent.PointerProperties.createArray(1),
+ MotionEvent.PointerCoords.createArray(1), 0, 0, 1.0f, 1.0f, deviceId, 0,
+ InputDevice.SOURCE_CLASS_NONE, 0, 0, 0);
+
+ mDataProvider.onMotionEvent(event);
+ boolean result = mDataProvider.isTouchScreenSource();
+ assertThat(result).isTrue();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_NON_TOUCHSCREEN_DEVICES_BYPASS_FALSING)
+ public void test_isTouchscreenSource_latestDeviceNonTouchscreen_false() throws RemoteException {
+ int deviceId = 9999;
+
+ InputDevice device = new InputDevice.Builder()
+ .setSources(InputDevice.SOURCE_CLASS_TRACKBALL)
+ .setId(deviceId)
+ .build();
+ when(mIInputManager.getInputDeviceIds()).thenReturn(new int[]{deviceId});
+ when(mIInputManager.getInputDevice(anyInt())).thenReturn(device);
+
+ MotionEvent event = MotionEvent.obtain(1, 0, MotionEvent.ACTION_UP, 1,
+ MotionEvent.PointerProperties.createArray(1),
+ MotionEvent.PointerCoords.createArray(1), 0, 0, 1.0f, 1.0f, deviceId, 0,
+ InputDevice.SOURCE_CLASS_NONE, 0, 0, 0);
+
+ mDataProvider.onMotionEvent(event);
+ boolean result = mDataProvider.isTouchScreenSource();
+ assertThat(result).isFalse();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_NON_TOUCHSCREEN_DEVICES_BYPASS_FALSING)
+ public void test_isTouchscreenSource_latestDeviceNull_true() {
+ // Do not mock InputManager for this test
+ inputManagerGlobalTestSession.close();
+
+ int nonExistentDeviceId = 9997;
+ MotionEvent event = MotionEvent.obtain(1, 0, MotionEvent.ACTION_UP, 1,
+ MotionEvent.PointerProperties.createArray(1),
+ MotionEvent.PointerCoords.createArray(1), 0, 0, 1.0f, 1.0f, nonExistentDeviceId, 0,
+ InputDevice.SOURCE_CLASS_NONE, 0, 0, 0);
+
+ mDataProvider.onMotionEvent(event);
+ assertThat(mDataProvider.isTouchScreenSource()).isTrue();
+ }
+
+ @Test
public void test_UnfoldedState_Folded() {
FalsingDataProvider falsingDataProvider = createWithFoldCapability(true);
when(mFoldStateListener.getFolded()).thenReturn(true);
@@ -413,7 +499,7 @@
}
private FalsingDataProvider createWithFoldCapability(boolean foldable) {
- return new FalsingDataProvider(
- mDisplayMetrics, mBatteryController, mFoldStateListener, mDockManager, foldable);
+ return new FalsingDataProvider(mDisplayMetrics, mBatteryController, mFoldStateListener,
+ mDockManager, foldable);
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandlerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandlerTest.kt
index bb400f2..f06cd6a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandlerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandlerTest.kt
@@ -67,7 +67,8 @@
isAttachedToWindow = { isAttachedToWindow },
onLongPressDetected = onLongPressDetected,
onSingleTapDetected = onSingleTapDetected,
- longPressDuration = { ViewConfiguration.getLongPressTimeout().toLong() }
+ longPressDuration = { ViewConfiguration.getLongPressTimeout().toLong() },
+ allowedTouchSlop = ViewConfiguration.getTouchSlop(),
)
underTest.isLongPressHandlingEnabled = true
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractorTest.kt
index 4e58069..5bd3645 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractorTest.kt
@@ -31,6 +31,7 @@
import com.android.systemui.qs.tiles.base.interactor.QSTileInput
import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
import com.android.systemui.recordissue.RecordIssueDialogDelegate
+import com.android.systemui.screenrecord.RecordingController
import com.android.systemui.settings.UserContextProvider
import com.android.systemui.settings.userTracker
import com.android.systemui.statusbar.phone.KeyguardDismissUtil
@@ -40,12 +41,16 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Mock
import org.mockito.Mockito.mock
+import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(AndroidJUnit4::class)
class IssueRecordingUserActionInteractorTest : SysuiTestCase() {
+ @Mock private lateinit var recordingController: RecordingController
+
val user = UserHandle(1)
val kosmos = Kosmos().also { it.testCase = this }
@@ -56,6 +61,7 @@
@Before
fun setup() {
+ MockitoAnnotations.initMocks(this)
hasCreatedDialogDelegate = false
with(kosmos) {
val factory =
@@ -84,7 +90,8 @@
dialogTransitionAnimator,
panelInteractor,
userTracker,
- factory
+ factory,
+ recordingController,
)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt
index 91d8e2a..de3dc57 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractorTest.kt
@@ -25,7 +25,9 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.R
+import com.android.settingslib.notification.modes.TestModeBuilder
import com.android.systemui.SysuiTestCase
+import com.android.systemui.SysuiTestableContext
import com.android.systemui.common.shared.model.asIcon
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
@@ -62,7 +64,12 @@
context.orCreateTestableResources.apply {
addOverride(MODES_DRAWABLE_ID, MODES_DRAWABLE)
addOverride(R.drawable.ic_zen_mode_type_bedtime, BEDTIME_DRAWABLE)
- addOverride(R.drawable.ic_zen_mode_type_driving, DRIVING_DRAWABLE)
+ }
+
+ val customPackageContext = SysuiTestableContext(context)
+ context.prepareCreatePackageContext(CUSTOM_PACKAGE, customPackageContext)
+ customPackageContext.orCreateTestableResources.apply {
+ addOverride(CUSTOM_DRAWABLE_ID, CUSTOM_DRAWABLE)
}
}
@@ -146,35 +153,41 @@
assertThat(tileData?.icon).isEqualTo(MODES_ICON)
assertThat(tileData?.iconResId).isEqualTo(MODES_DRAWABLE_ID)
- // Add an active mode: icon should be the mode icon. No iconResId, because we don't
- // really know that it's a system icon.
+ // Add an active mode with a default icon: icon should be the mode icon, and the
+ // iconResId is also populated, because we know it's a system icon.
zenModeRepository.addMode(
- id = "Bedtime",
+ id = "Bedtime with default icon",
type = AutomaticZenRule.TYPE_BEDTIME,
active = true
)
runCurrent()
assertThat(tileData?.icon).isEqualTo(BEDTIME_ICON)
- assertThat(tileData?.iconResId).isNull()
+ assertThat(tileData?.iconResId).isEqualTo(R.drawable.ic_zen_mode_type_bedtime)
- // Add another, less-prioritized mode: icon should remain the first mode icon
+ // Add another, less-prioritized mode that has a *custom* icon: for now, icon should
+ // remain the first mode icon
zenModeRepository.addMode(
- id = "Driving",
- type = AutomaticZenRule.TYPE_DRIVING,
- active = true
+ TestModeBuilder()
+ .setId("Driving with custom icon")
+ .setType(AutomaticZenRule.TYPE_DRIVING)
+ .setPackage(CUSTOM_PACKAGE)
+ .setIconResId(CUSTOM_DRAWABLE_ID)
+ .setActive(true)
+ .build()
)
runCurrent()
assertThat(tileData?.icon).isEqualTo(BEDTIME_ICON)
- assertThat(tileData?.iconResId).isNull()
+ assertThat(tileData?.iconResId).isEqualTo(R.drawable.ic_zen_mode_type_bedtime)
// Deactivate more important mode: icon should be the less important, still active mode
- zenModeRepository.deactivateMode("Bedtime")
+ // And because it's a package-provided icon, iconResId is not populated.
+ zenModeRepository.deactivateMode("Bedtime with default icon")
runCurrent()
- assertThat(tileData?.icon).isEqualTo(DRIVING_ICON)
+ assertThat(tileData?.icon).isEqualTo(CUSTOM_ICON)
assertThat(tileData?.iconResId).isNull()
// Deactivate remaining mode: back to the default modes icon
- zenModeRepository.deactivateMode("Driving")
+ zenModeRepository.deactivateMode("Driving with custom icon")
runCurrent()
assertThat(tileData?.icon).isEqualTo(MODES_ICON)
assertThat(tileData?.iconResId).isEqualTo(MODES_DRAWABLE_ID)
@@ -241,15 +254,17 @@
private companion object {
val TEST_USER = UserHandle.of(1)!!
+ const val CUSTOM_PACKAGE = "com.some.mode.owner.package"
val MODES_DRAWABLE_ID = R.drawable.ic_zen_priority_modes
+ const val CUSTOM_DRAWABLE_ID = 12345
val MODES_DRAWABLE = TestStubDrawable("modes_icon")
val BEDTIME_DRAWABLE = TestStubDrawable("bedtime")
- val DRIVING_DRAWABLE = TestStubDrawable("driving")
+ val CUSTOM_DRAWABLE = TestStubDrawable("custom")
val MODES_ICON = MODES_DRAWABLE.asIcon()
val BEDTIME_ICON = BEDTIME_DRAWABLE.asIcon()
- val DRIVING_ICON = DRIVING_DRAWABLE.asIcon()
+ val CUSTOM_ICON = CUSTOM_DRAWABLE.asIcon()
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapperTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapperTest.kt
index f7bdcb8..c3d45db 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapperTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapperTest.kt
@@ -18,7 +18,6 @@
import android.app.Flags
import android.graphics.drawable.TestStubDrawable
-import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -109,26 +108,7 @@
}
@Test
- @EnableFlags(Flags.FLAG_MODES_UI_ICONS)
- fun state_withEnabledFlag_noIconResId() {
- val icon = TestStubDrawable("res123").asIcon()
- val model =
- ModesTileModel(
- isActivated = false,
- activeModes = emptyList(),
- icon = icon,
- iconResId = 123 // Should not be populated, but is ignored even if present
- )
-
- val state = underTest.map(config, model)
-
- assertThat(state.icon()).isEqualTo(icon)
- assertThat(state.iconRes).isNull()
- }
-
- @Test
- @DisableFlags(Flags.FLAG_MODES_UI_ICONS)
- fun state_withDisabledFlag_includesIconResId() {
+ fun state_modelHasIconResId_includesIconResId() {
val icon = TestStubDrawable("res123").asIcon()
val model =
ModesTileModel(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
index ec79cc6..d180460 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt
@@ -21,6 +21,8 @@
import android.app.StatusBarManager
import android.hardware.face.FaceManager
import android.os.PowerManager
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
import android.view.Display
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -28,6 +30,8 @@
import com.android.compose.animation.scene.SceneKey
import com.android.internal.logging.uiEventLoggerFake
import com.android.internal.policy.IKeyguardDismissCallback
+import com.android.keyguard.AuthInteractionProperties
+import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
@@ -48,6 +52,7 @@
import com.android.systemui.deviceentry.shared.model.FailedFaceAuthenticationStatus
import com.android.systemui.deviceentry.shared.model.SuccessFaceAuthenticationStatus
import com.android.systemui.flags.EnableSceneContainer
+import com.android.systemui.haptics.msdl.fakeMSDLPlayer
import com.android.systemui.haptics.vibratorHelper
import com.android.systemui.keyevent.data.repository.fakeKeyEventRepository
import com.android.systemui.keyguard.data.repository.biometricSettingsRepository
@@ -95,6 +100,7 @@
import com.android.systemui.statusbar.sysuiStatusBarStateController
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.mock
+import com.google.android.msdl.data.model.MSDLToken
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
@@ -137,6 +143,8 @@
private val powerInteractor = kosmos.powerInteractor
private val fakeTrustRepository = kosmos.fakeTrustRepository
private val uiEventLoggerFake = kosmos.uiEventLoggerFake
+ private val msdlPlayer = kosmos.fakeMSDLPlayer
+ private val authInteractionProperties = AuthInteractionProperties()
private lateinit var underTest: SceneContainerStartable
@@ -654,6 +662,7 @@
}
@Test
+ @DisableFlags(Flags.FLAG_MSDL_FEEDBACK)
fun playSuccessHaptics_onSuccessfulLockscreenAuth_udfps() =
testScope.runTest {
val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
@@ -680,6 +689,31 @@
}
@Test
+ @EnableFlags(Flags.FLAG_MSDL_FEEDBACK)
+ fun playSuccessMSDLHaptics_onSuccessfulLockscreenAuth_udfps() =
+ testScope.runTest {
+ val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
+ val playSuccessHaptic by
+ collectLastValue(deviceEntryHapticsInteractor.playSuccessHaptic)
+
+ setupBiometricAuth(hasUdfps = true)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+ assertThat(kosmos.deviceEntryInteractor.isDeviceEntered.value).isFalse()
+
+ underTest.start()
+ unlockWithFingerprintAuth()
+
+ assertThat(playSuccessHaptic).isNotNull()
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+ assertThat(msdlPlayer.latestTokenPlayed).isEqualTo(MSDLToken.UNLOCK)
+ assertThat(msdlPlayer.latestPropertiesPlayed).isEqualTo(authInteractionProperties)
+
+ updateFingerprintAuthStatus(isSuccess = true)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Gone)
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_MSDL_FEEDBACK)
fun playSuccessHaptics_onSuccessfulLockscreenAuth_sfps() =
testScope.runTest {
val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
@@ -707,6 +741,32 @@
}
@Test
+ @EnableFlags(Flags.FLAG_MSDL_FEEDBACK)
+ fun playSuccessMSDLHaptics_onSuccessfulLockscreenAuth_sfps() =
+ testScope.runTest {
+ val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
+ val playSuccessHaptic by
+ collectLastValue(deviceEntryHapticsInteractor.playSuccessHaptic)
+
+ setupBiometricAuth(hasSfps = true)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+ assertThat(kosmos.deviceEntryInteractor.isDeviceEntered.value).isFalse()
+
+ underTest.start()
+ allowHapticsOnSfps()
+ unlockWithFingerprintAuth()
+
+ assertThat(playSuccessHaptic).isNotNull()
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+ assertThat(msdlPlayer.latestTokenPlayed).isEqualTo(MSDLToken.UNLOCK)
+ assertThat(msdlPlayer.latestPropertiesPlayed).isEqualTo(authInteractionProperties)
+
+ updateFingerprintAuthStatus(isSuccess = true)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Gone)
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_MSDL_FEEDBACK)
fun playErrorHaptics_onFailedLockscreenAuth_udfps() =
testScope.runTest {
val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
@@ -727,6 +787,27 @@
}
@Test
+ @EnableFlags(Flags.FLAG_MSDL_FEEDBACK)
+ fun playMSDLErrorHaptics_onFailedLockscreenAuth_udfps() =
+ testScope.runTest {
+ val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
+ val playErrorHaptic by collectLastValue(deviceEntryHapticsInteractor.playErrorHaptic)
+
+ setupBiometricAuth(hasUdfps = true)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+ assertThat(kosmos.deviceEntryInteractor.isDeviceEntered.value).isFalse()
+
+ underTest.start()
+ updateFingerprintAuthStatus(isSuccess = false)
+
+ assertThat(playErrorHaptic).isNotNull()
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+ assertThat(msdlPlayer.latestTokenPlayed).isEqualTo(MSDLToken.FAILURE)
+ assertThat(msdlPlayer.latestPropertiesPlayed).isEqualTo(authInteractionProperties)
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_MSDL_FEEDBACK)
fun playErrorHaptics_onFailedLockscreenAuth_sfps() =
testScope.runTest {
val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
@@ -747,6 +828,27 @@
}
@Test
+ @EnableFlags(Flags.FLAG_MSDL_FEEDBACK)
+ fun playMSDLErrorHaptics_onFailedLockscreenAuth_sfps() =
+ testScope.runTest {
+ val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
+ val playErrorHaptic by collectLastValue(deviceEntryHapticsInteractor.playErrorHaptic)
+
+ setupBiometricAuth(hasSfps = true)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+ assertThat(kosmos.deviceEntryInteractor.isDeviceEntered.value).isFalse()
+
+ underTest.start()
+ updateFingerprintAuthStatus(isSuccess = false)
+
+ assertThat(playErrorHaptic).isNotNull()
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+ assertThat(msdlPlayer.latestTokenPlayed).isEqualTo(MSDLToken.FAILURE)
+ assertThat(msdlPlayer.latestPropertiesPlayed).isEqualTo(authInteractionProperties)
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_MSDL_FEEDBACK)
fun skipsSuccessHaptics_whenPowerButtonDown_sfps() =
testScope.runTest {
val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
@@ -774,6 +876,32 @@
}
@Test
+ @EnableFlags(Flags.FLAG_MSDL_FEEDBACK)
+ fun skipsMSDLSuccessHaptics_whenPowerButtonDown_sfps() =
+ testScope.runTest {
+ val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
+ val playSuccessHaptic by
+ collectLastValue(deviceEntryHapticsInteractor.playSuccessHaptic)
+
+ setupBiometricAuth(hasSfps = true)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+ assertThat(kosmos.deviceEntryInteractor.isDeviceEntered.value).isFalse()
+
+ underTest.start()
+ allowHapticsOnSfps(isPowerButtonDown = true)
+ unlockWithFingerprintAuth()
+
+ assertThat(playSuccessHaptic).isNull()
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+ assertThat(msdlPlayer.latestTokenPlayed).isNull()
+ assertThat(msdlPlayer.latestPropertiesPlayed).isNull()
+
+ updateFingerprintAuthStatus(isSuccess = true)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Gone)
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_MSDL_FEEDBACK)
fun skipsSuccessHaptics_whenPowerButtonRecentlyPressed_sfps() =
testScope.runTest {
val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
@@ -801,6 +929,32 @@
}
@Test
+ @EnableFlags(Flags.FLAG_MSDL_FEEDBACK)
+ fun skipsMSDLSuccessHaptics_whenPowerButtonRecentlyPressed_sfps() =
+ testScope.runTest {
+ val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
+ val playSuccessHaptic by
+ collectLastValue(deviceEntryHapticsInteractor.playSuccessHaptic)
+
+ setupBiometricAuth(hasSfps = true)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+ assertThat(kosmos.deviceEntryInteractor.isDeviceEntered.value).isFalse()
+
+ underTest.start()
+ allowHapticsOnSfps(lastPowerPress = 50)
+ unlockWithFingerprintAuth()
+
+ assertThat(playSuccessHaptic).isNull()
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+ assertThat(msdlPlayer.latestTokenPlayed).isNull()
+ assertThat(msdlPlayer.latestPropertiesPlayed).isNull()
+
+ updateFingerprintAuthStatus(isSuccess = true)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Gone)
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_MSDL_FEEDBACK)
fun skipsErrorHaptics_whenPowerButtonDown_sfps() =
testScope.runTest {
val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
@@ -822,6 +976,28 @@
}
@Test
+ @EnableFlags(Flags.FLAG_MSDL_FEEDBACK)
+ fun skipsMSDLErrorHaptics_whenPowerButtonDown_sfps() =
+ testScope.runTest {
+ val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
+ val playErrorHaptic by collectLastValue(deviceEntryHapticsInteractor.playErrorHaptic)
+
+ setupBiometricAuth(hasSfps = true)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+ assertThat(kosmos.deviceEntryInteractor.isDeviceEntered.value).isFalse()
+
+ underTest.start()
+ kosmos.fakeKeyEventRepository.setPowerButtonDown(true)
+ updateFingerprintAuthStatus(isSuccess = false)
+
+ assertThat(playErrorHaptic).isNull()
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+ assertThat(msdlPlayer.latestTokenPlayed).isNull()
+ assertThat(msdlPlayer.latestPropertiesPlayed).isNull()
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_MSDL_FEEDBACK)
fun skipsFaceErrorHaptics_nonSfps_coEx() =
testScope.runTest {
val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
@@ -842,6 +1018,26 @@
}
@Test
+ @EnableFlags(Flags.FLAG_MSDL_FEEDBACK)
+ fun skipsMSDLFaceErrorHaptics_nonSfps_coEx() =
+ testScope.runTest {
+ val currentSceneKey by collectLastValue(sceneInteractor.currentScene)
+ val playErrorHaptic by collectLastValue(deviceEntryHapticsInteractor.playErrorHaptic)
+
+ setupBiometricAuth(hasUdfps = true, hasFace = true)
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+ assertThat(kosmos.deviceEntryInteractor.isDeviceEntered.value).isFalse()
+
+ underTest.start()
+ updateFaceAuthStatus(isSuccess = false)
+
+ assertThat(playErrorHaptic).isNull()
+ assertThat(currentSceneKey).isEqualTo(Scenes.Lockscreen)
+ assertThat(msdlPlayer.latestTokenPlayed).isNull()
+ assertThat(msdlPlayer.latestPropertiesPlayed).isNull()
+ }
+
+ @Test
fun hydrateSystemUiState() =
testScope.runTest {
val transitionStateFlow = prepareState()
diff --git a/packages/SystemUI/res/layout/notification_template_en_route_contracted.xml b/packages/SystemUI/res/layout/notification_template_en_route_contracted.xml
index 59cfecc..e7a40d1 100644
--- a/packages/SystemUI/res/layout/notification_template_en_route_contracted.xml
+++ b/packages/SystemUI/res/layout/notification_template_en_route_contracted.xml
@@ -16,7 +16,7 @@
<com.android.systemui.statusbar.notification.row.ui.view.EnRouteView
xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/status_bar_latest_event_content"
+ android:id="@*android:id/status_bar_latest_event_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
diff --git a/packages/SystemUI/res/layout/notification_template_en_route_expanded.xml b/packages/SystemUI/res/layout/notification_template_en_route_expanded.xml
new file mode 100644
index 0000000..ca6d66a
--- /dev/null
+++ b/packages/SystemUI/res/layout/notification_template_en_route_expanded.xml
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2014 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT 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.statusbar.notification.row.ui.view.EnRouteView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@*android:id/status_bar_latest_event_content"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:clipChildren="false"
+ android:tag="big"
+ >
+
+ <LinearLayout
+ android:id="@*android:id/notification_action_list_margin_target"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="@*android:dimen/notification_content_margin"
+ android:orientation="vertical"
+ >
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_gravity="top"
+ >
+
+ <include layout="@*android:layout/notification_template_header" />
+
+ <LinearLayout
+ android:id="@*android:id/notification_main_column"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@*android:dimen/notification_content_margin_start"
+ android:layout_marginEnd="@*android:dimen/notification_content_margin_end"
+ android:layout_marginTop="@*android:dimen/notification_content_margin_top"
+ android:orientation="vertical"
+ >
+
+ <include layout="@*android:layout/notification_template_part_line1" />
+
+ <include layout="@*android:layout/notification_template_text_multiline" />
+
+ <include
+ android:layout_width="match_parent"
+ android:layout_height="@*android:dimen/notification_progress_bar_height"
+ android:layout_marginTop="@*android:dimen/notification_progress_margin_top"
+ layout="@*android:layout/notification_template_progress"
+ />
+ </LinearLayout>
+
+ <include layout="@*android:layout/notification_template_right_icon" />
+ </FrameLayout>
+
+ <ViewStub
+ android:layout="@*android:layout/notification_material_reply_text"
+ android:id="@*android:id/notification_material_reply_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ />
+
+ <include
+ layout="@*android:layout/notification_template_smart_reply_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginStart="@*android:dimen/notification_content_margin_start"
+ android:layout_marginEnd="@*android:dimen/notification_content_margin_end"
+ android:layout_marginTop="@*android:dimen/notification_content_margin"
+ />
+
+ <include layout="@*android:layout/notification_material_action_list" />
+ </LinearLayout>
+</com.android.systemui.statusbar.notification.row.ui.view.EnRouteView>
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
index d08653c3..60edaae 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java
@@ -52,7 +52,6 @@
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
-import com.android.internal.logging.UiEventLogger;
import com.android.settingslib.bluetooth.BluetoothCallback;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.HapClientProfile;
@@ -105,7 +104,8 @@
private final AudioManager mAudioManager;
private final LocalBluetoothProfileManager mProfileManager;
private final HapClientProfile mHapClientProfile;
- private final UiEventLogger mUiEventLogger;
+ private final HearingDevicesUiEventLogger mUiEventLogger;
+ private final int mLaunchSourceId;
private HearingDevicesListAdapter mDeviceListAdapter;
private HearingDevicesPresetsController mPresetsController;
private Context mApplicationContext;
@@ -153,20 +153,22 @@
public interface Factory {
/** Create a {@link HearingDevicesDialogDelegate} instance */
HearingDevicesDialogDelegate create(
- boolean showPairNewDevice);
+ boolean showPairNewDevice,
+ @HearingDevicesUiEventLogger.LaunchSourceId int launchSource);
}
@AssistedInject
public HearingDevicesDialogDelegate(
@Application Context applicationContext,
@Assisted boolean showPairNewDevice,
+ @Assisted @HearingDevicesUiEventLogger.LaunchSourceId int launchSourceId,
SystemUIDialog.Factory systemUIDialogFactory,
ActivityStarter activityStarter,
DialogTransitionAnimator dialogTransitionAnimator,
@Nullable LocalBluetoothManager localBluetoothManager,
@Main Handler handler,
AudioManager audioManager,
- UiEventLogger uiEventLogger) {
+ HearingDevicesUiEventLogger uiEventLogger) {
mApplicationContext = applicationContext;
mShowPairNewDevice = showPairNewDevice;
mSystemUIDialogFactory = systemUIDialogFactory;
@@ -178,6 +180,7 @@
mProfileManager = localBluetoothManager.getProfileManager();
mHapClientProfile = mProfileManager.getHapClientProfile();
mUiEventLogger = uiEventLogger;
+ mLaunchSourceId = launchSourceId;
}
@Override
@@ -191,7 +194,7 @@
@Override
public void onDeviceItemGearClicked(@NonNull DeviceItem deviceItem, @NonNull View view) {
- mUiEventLogger.log(HearingDevicesUiEvent.HEARING_DEVICES_GEAR_CLICK);
+ mUiEventLogger.log(HearingDevicesUiEvent.HEARING_DEVICES_GEAR_CLICK, mLaunchSourceId);
dismissDialogIfExists();
Intent intent = new Intent(ACTION_BLUETOOTH_DEVICE_DETAILS);
Bundle bundle = new Bundle();
@@ -207,15 +210,17 @@
CachedBluetoothDevice cachedBluetoothDevice = deviceItem.getCachedBluetoothDevice();
switch (deviceItem.getType()) {
case ACTIVE_MEDIA_BLUETOOTH_DEVICE, CONNECTED_BLUETOOTH_DEVICE -> {
- mUiEventLogger.log(HearingDevicesUiEvent.HEARING_DEVICES_DISCONNECT);
+ mUiEventLogger.log(HearingDevicesUiEvent.HEARING_DEVICES_DISCONNECT,
+ mLaunchSourceId);
cachedBluetoothDevice.disconnect();
}
case AVAILABLE_MEDIA_BLUETOOTH_DEVICE -> {
- mUiEventLogger.log(HearingDevicesUiEvent.HEARING_DEVICES_SET_ACTIVE);
+ mUiEventLogger.log(HearingDevicesUiEvent.HEARING_DEVICES_SET_ACTIVE,
+ mLaunchSourceId);
cachedBluetoothDevice.setActive();
}
case SAVED_BLUETOOTH_DEVICE -> {
- mUiEventLogger.log(HearingDevicesUiEvent.HEARING_DEVICES_CONNECT);
+ mUiEventLogger.log(HearingDevicesUiEvent.HEARING_DEVICES_CONNECT, mLaunchSourceId);
cachedBluetoothDevice.connect();
}
}
@@ -275,7 +280,7 @@
if (mLocalBluetoothManager == null) {
return;
}
- mUiEventLogger.log(HearingDevicesUiEvent.HEARING_DEVICES_DIALOG_SHOW);
+ mUiEventLogger.log(HearingDevicesUiEvent.HEARING_DEVICES_DIALOG_SHOW, mLaunchSourceId);
mPairButton = dialog.requireViewById(R.id.pair_new_device_button);
mDeviceList = dialog.requireViewById(R.id.device_list);
mPresetSpinner = dialog.requireViewById(R.id.preset_spinner);
@@ -363,7 +368,8 @@
mPresetSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
- mUiEventLogger.log(HearingDevicesUiEvent.HEARING_DEVICES_PRESET_SELECT);
+ mUiEventLogger.log(HearingDevicesUiEvent.HEARING_DEVICES_PRESET_SELECT,
+ mLaunchSourceId);
mPresetsController.selectPreset(
mPresetsController.getAllPresetInfo().get(position).getIndex());
}
@@ -381,7 +387,7 @@
private void setupPairNewDeviceButton(SystemUIDialog dialog, @Visibility int visibility) {
if (visibility == VISIBLE) {
mPairButton.setOnClickListener(v -> {
- mUiEventLogger.log(HearingDevicesUiEvent.HEARING_DEVICES_PAIR);
+ mUiEventLogger.log(HearingDevicesUiEvent.HEARING_DEVICES_PAIR, mLaunchSourceId);
dismissDialogIfExists();
final Intent intent = new Intent(Settings.ACTION_HEARING_DEVICE_PAIRING_SETTINGS);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
@@ -485,7 +491,8 @@
final String name = intent.getComponent() != null
? intent.getComponent().flattenToString()
: intent.getPackage() + "/" + intent.getAction();
- mUiEventLogger.log(HearingDevicesUiEvent.HEARING_DEVICES_RELATED_TOOL_CLICK, 0, name);
+ mUiEventLogger.log(HearingDevicesUiEvent.HEARING_DEVICES_RELATED_TOOL_CLICK,
+ mLaunchSourceId, name);
dismissDialogIfExists();
mActivityStarter.postStartActivityDismissingKeyguard(intent, /* delay= */ 0,
mDialogTransitionAnimator.createActivityTransitionController(view));
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogManager.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogManager.java
index bc4cb45..3d24177 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogManager.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogManager.java
@@ -70,8 +70,10 @@
* Shows the dialog.
*
* @param expandable {@link Expandable} from which the dialog is shown.
+ * @param launchSourceId the id indicates where the dialog is launched from.
*/
- public void showDialog(Expandable expandable) {
+ public void showDialog(Expandable expandable,
+ @HearingDevicesUiEventLogger.LaunchSourceId int launchSourceId) {
if (mDialog != null) {
if (DEBUG) {
Log.d(TAG, "HearingDevicesDialog already showing. Destroy it first.");
@@ -91,7 +93,8 @@
});
pairedHearingDeviceCheckTask.addListener(() -> {
try {
- mDialog = mDialogFactory.create(!pairedHearingDeviceCheckTask.get()).createDialog();
+ mDialog = mDialogFactory.create(!pairedHearingDeviceCheckTask.get(),
+ launchSourceId).createDialog();
if (expandable != null) {
DialogTransitionAnimator.Controller controller =
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogReceiver.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogReceiver.java
index 6a34d19..02e65fd 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogReceiver.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogReceiver.java
@@ -16,6 +16,8 @@
package com.android.systemui.accessibility.hearingaid;
+import static com.android.systemui.accessibility.hearingaid.HearingDevicesUiEventLogger.LAUNCH_SOURCE_A11Y;
+
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -46,7 +48,7 @@
}
if (ACTION.equals(intent.getAction())) {
- mDialogManager.showDialog(/* view= */ null);
+ mDialogManager.showDialog(/* expandable= */ null, LAUNCH_SOURCE_A11Y);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesUiEvent.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesUiEvent.java
deleted file mode 100644
index 3fbe56e..0000000
--- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesUiEvent.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.accessibility.hearingaid;
-
-import com.android.internal.logging.UiEvent;
-import com.android.internal.logging.UiEventLogger;
-
-public enum HearingDevicesUiEvent implements UiEventLogger.UiEventEnum {
-
- @UiEvent(doc = "Hearing devices dialog is shown")
- HEARING_DEVICES_DIALOG_SHOW(1848),
- @UiEvent(doc = "Pair new device")
- HEARING_DEVICES_PAIR(1849),
- @UiEvent(doc = "Connect to the device")
- HEARING_DEVICES_CONNECT(1850),
- @UiEvent(doc = "Disconnect from the device")
- HEARING_DEVICES_DISCONNECT(1851),
- @UiEvent(doc = "Set the device as active device")
- HEARING_DEVICES_SET_ACTIVE(1852),
- @UiEvent(doc = "Click on the device gear to enter device detail page")
- HEARING_DEVICES_GEAR_CLICK(1853),
- @UiEvent(doc = "Select a preset from preset spinner")
- HEARING_DEVICES_PRESET_SELECT(1854),
- @UiEvent(doc = "Click on related tool")
- HEARING_DEVICES_RELATED_TOOL_CLICK(1856);
-
- private final int mId;
-
- HearingDevicesUiEvent(int id) {
- mId = id;
- }
-
- @Override
- public int getId() {
- return mId;
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesUiEvent.kt b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesUiEvent.kt
new file mode 100644
index 0000000..9e77b02
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesUiEvent.kt
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.accessibility.hearingaid
+
+import com.android.internal.logging.UiEvent
+import com.android.internal.logging.UiEventLogger
+
+enum class HearingDevicesUiEvent(private val id: Int) : UiEventLogger.UiEventEnum {
+
+ @UiEvent(doc = "Hearing devices dialog is shown") HEARING_DEVICES_DIALOG_SHOW(1848),
+ @UiEvent(doc = "Pair new device") HEARING_DEVICES_PAIR(1849),
+ @UiEvent(doc = "Connect to the device") HEARING_DEVICES_CONNECT(1850),
+ @UiEvent(doc = "Disconnect from the device") HEARING_DEVICES_DISCONNECT(1851),
+ @UiEvent(doc = "Set the device as active device") HEARING_DEVICES_SET_ACTIVE(1852),
+ @UiEvent(doc = "Click on the device gear to enter device detail page")
+ HEARING_DEVICES_GEAR_CLICK(1853),
+ @UiEvent(doc = "Select a preset from preset spinner") HEARING_DEVICES_PRESET_SELECT(1854),
+ @UiEvent(doc = "Click on related tool") HEARING_DEVICES_RELATED_TOOL_CLICK(1856);
+
+ override fun getId(): Int = this.id
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesUiEventLogger.kt b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesUiEventLogger.kt
new file mode 100644
index 0000000..0b32cfc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesUiEventLogger.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.accessibility.hearingaid
+
+import android.annotation.IntDef
+import com.android.internal.logging.UiEventLogger
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+
+@SysUISingleton
+class HearingDevicesUiEventLogger @Inject constructor(private val uiEventLogger: UiEventLogger) {
+
+ /** Logs the given event */
+ fun log(event: UiEventLogger.UiEventEnum, launchSourceId: Int) {
+ log(event, launchSourceId, null)
+ }
+
+ fun log(event: UiEventLogger.UiEventEnum, launchSourceId: Int, pkgName: String?) {
+ uiEventLogger.log(event, launchSourceId, pkgName)
+ }
+
+ /**
+ * The possible launch source of hearing devices dialog
+ *
+ * @hide
+ */
+ @IntDef(LAUNCH_SOURCE_UNKNOWN, LAUNCH_SOURCE_A11Y, LAUNCH_SOURCE_QS_TILE)
+ annotation class LaunchSourceId
+
+ companion object {
+ const val LAUNCH_SOURCE_UNKNOWN = 0
+ const val LAUNCH_SOURCE_A11Y = 1 // launch from AccessibilityManagerService
+ const val LAUNCH_SOURCE_QS_TILE = 2
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 970fdea..69ab976 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -78,6 +78,8 @@
import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.util.concurrency.DelayableExecutor;
+import com.google.android.msdl.domain.MSDLPlayer;
+
import kotlin.Lazy;
import kotlinx.coroutines.CoroutineScope;
@@ -157,6 +159,8 @@
private final @Background DelayableExecutor mBackgroundExecutor;
+ private final MSDLPlayer mMSDLPlayer;
+
// Non-null only if the dialog is in the act of dismissing and has not sent the reason yet.
@Nullable @AuthDialogCallback.DismissedReason private Integer mPendingCallbackReason;
// HAT received from LockSettingsService when credential is verified.
@@ -292,7 +296,8 @@
@NonNull Provider<CredentialViewModel> credentialViewModelProvider,
@NonNull @Background DelayableExecutor bgExecutor,
@NonNull VibratorHelper vibratorHelper,
- Lazy<ViewCapture> lazyViewCapture) {
+ Lazy<ViewCapture> lazyViewCapture,
+ @NonNull MSDLPlayer msdlPlayer) {
super(config.mContext);
mConfig = config;
@@ -309,6 +314,7 @@
.getDimension(R.dimen.biometric_dialog_animation_translation_offset);
mLinearOutSlowIn = Interpolators.LINEAR_OUT_SLOW_IN;
mBiometricCallback = new BiometricCallback();
+ mMSDLPlayer = msdlPlayer;
final BiometricModalities biometricModalities = new BiometricModalities(
Utils.findFirstSensorProperties(fpProps, mConfig.mSensorIds),
@@ -379,7 +385,7 @@
getJankListener(mLayout, TRANSIT,
BiometricViewSizeBinder.ANIMATE_MEDIUM_TO_LARGE_DURATION_MS),
mBackgroundView, mBiometricCallback, mApplicationCoroutineScope,
- vibratorHelper);
+ vibratorHelper, mMSDLPlayer);
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index 097ab72..b39aae9 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -89,6 +89,8 @@
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.concurrency.Execution;
+import com.google.android.msdl.domain.MSDLPlayer;
+
import dagger.Lazy;
import kotlin.Unit;
@@ -183,6 +185,7 @@
private final @Background DelayableExecutor mBackgroundExecutor;
private final DisplayInfo mCachedDisplayInfo = new DisplayInfo();
@NonNull private final VibratorHelper mVibratorHelper;
+ @NonNull private final MSDLPlayer mMSDLPlayer;
private final kotlin.Lazy<ViewCapture> mLazyViewCapture;
@@ -742,7 +745,8 @@
@Background DelayableExecutor bgExecutor,
@NonNull UdfpsUtils udfpsUtils,
@NonNull VibratorHelper vibratorHelper,
- Lazy<ViewCapture> daggerLazyViewCapture) {
+ Lazy<ViewCapture> daggerLazyViewCapture,
+ @NonNull MSDLPlayer msdlPlayer) {
mContext = context;
mExecution = execution;
mUserManager = userManager;
@@ -764,6 +768,7 @@
mUdfpsUtils = udfpsUtils;
mApplicationCoroutineScope = applicationCoroutineScope;
mVibratorHelper = vibratorHelper;
+ mMSDLPlayer = msdlPlayer;
mLogContextInteractor = logContextInteractor;
mPromptSelectorInteractor = promptSelectorInteractorProvider;
@@ -1327,7 +1332,7 @@
wakefulnessLifecycle, userManager, lockPatternUtils,
mInteractionJankMonitor, mPromptSelectorInteractor, viewModel,
mCredentialViewModelProvider, bgExecutor, mVibratorHelper,
- mLazyViewCapture);
+ mLazyViewCapture, mMSDLPlayer);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
index 0b440ad..e7e8d8f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
@@ -25,7 +25,6 @@
import android.hardware.biometrics.Flags
import android.hardware.face.FaceManager
import android.util.Log
-import android.view.HapticFeedbackConstants
import android.view.MotionEvent
import android.view.View
import android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO
@@ -59,6 +58,7 @@
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.res.R
import com.android.systemui.statusbar.VibratorHelper
+import com.google.android.msdl.domain.MSDLPlayer
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.combine
@@ -83,6 +83,7 @@
legacyCallback: Spaghetti.Callback,
applicationScope: CoroutineScope,
vibratorHelper: VibratorHelper,
+ msdlPlayer: MSDLPlayer,
): Spaghetti {
val accessibilityManager = view.context.getSystemService(AccessibilityManager::class.java)!!
@@ -434,21 +435,27 @@
// Play haptics
launch {
viewModel.hapticsToPlay.collect { haptics ->
- if (haptics.hapticFeedbackConstant != HapticFeedbackConstants.NO_HAPTICS) {
- if (haptics.flag != null) {
- vibratorHelper.performHapticFeedback(
- view,
- haptics.hapticFeedbackConstant,
- haptics.flag,
- )
- } else {
- vibratorHelper.performHapticFeedback(
- view,
- haptics.hapticFeedbackConstant,
- )
+ when (haptics) {
+ is PromptViewModel.HapticsToPlay.HapticConstant -> {
+ if (haptics.flag != null) {
+ vibratorHelper.performHapticFeedback(
+ view,
+ haptics.constant,
+ haptics.flag,
+ )
+ } else {
+ vibratorHelper.performHapticFeedback(
+ view,
+ haptics.constant,
+ )
+ }
}
- viewModel.clearHaptics()
+ is PromptViewModel.HapticsToPlay.MSDL -> {
+ msdlPlayer.playToken(haptics.token, haptics.properties)
+ }
+ is PromptViewModel.HapticsToPlay.None -> {}
}
+ viewModel.clearHaptics()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
index 85c3ae3..d69e875 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt
@@ -18,13 +18,11 @@
import android.animation.Animator
import android.animation.AnimatorSet
-import android.animation.ValueAnimator
import android.graphics.Outline
import android.graphics.Rect
import android.transition.AutoTransition
import android.transition.TransitionManager
import android.util.TypedValue
-import android.view.Surface
import android.view.View
import android.view.ViewGroup
import android.view.ViewOutlineProvider
@@ -413,13 +411,12 @@
ANIMATE_SMALL_TO_MEDIUM_DURATION_MS.toLong()
)
- TransitionManager.beginDelayedTransition(view, autoTransition)
-
if (position.isLeft) {
flipConstraintSet.applyTo(view)
} else {
mediumConstraintSet.applyTo(view)
}
+ TransitionManager.beginDelayedTransition(view, autoTransition)
}
size.isMedium -> {
if (position.isLeft) {
@@ -428,14 +425,18 @@
mediumConstraintSet.applyTo(view)
}
}
- size.isLarge && currentSize.isMedium -> {
+ size.isLarge -> {
val autoTransition = AutoTransition()
autoTransition.setDuration(
- ANIMATE_MEDIUM_TO_LARGE_DURATION_MS.toLong()
+ if (currentSize.isSmall) {
+ ANIMATE_SMALL_TO_MEDIUM_DURATION_MS.toLong()
+ } else {
+ ANIMATE_MEDIUM_TO_LARGE_DURATION_MS.toLong()
+ }
)
- TransitionManager.beginDelayedTransition(view, autoTransition)
largeConstraintSet.applyTo(view)
+ TransitionManager.beginDelayedTransition(view, autoTransition)
}
}
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 4c2fe07..168ba11 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
@@ -35,7 +35,9 @@
import android.util.RotationUtils
import android.view.HapticFeedbackConstants
import android.view.MotionEvent
+import com.android.keyguard.AuthInteractionProperties
import com.android.launcher3.icons.IconProvider
+import com.android.systemui.Flags.msdlFeedback
import com.android.systemui.biometrics.UdfpsUtils
import com.android.systemui.biometrics.Utils
import com.android.systemui.biometrics.Utils.isSystem
@@ -53,6 +55,8 @@
import com.android.systemui.keyguard.shared.model.AcquiredFingerprintAuthenticationStatus
import com.android.systemui.res.R
import com.android.systemui.util.kotlin.combine
+import com.google.android.msdl.data.model.MSDLToken
+import com.google.android.msdl.domain.InteractionProperties
import javax.inject.Inject
import kotlinx.coroutines.Job
import kotlinx.coroutines.coroutineScope
@@ -245,8 +249,9 @@
private val _forceLargeSize = MutableStateFlow(false)
private val _forceMediumSize = MutableStateFlow(false)
- private val _hapticsToPlay =
- MutableStateFlow(HapticsToPlay(HapticFeedbackConstants.NO_HAPTICS, /* flag= */ null))
+ private val authInteractionProperties = AuthInteractionProperties()
+ private val _hapticsToPlay: MutableStateFlow<HapticsToPlay> =
+ MutableStateFlow(HapticsToPlay.None)
/** Event fired to the view indicating a [HapticsToPlay] */
val hapticsToPlay = _hapticsToPlay.asStateFlow()
@@ -939,26 +944,52 @@
}
private fun vibrateOnSuccess() {
- _hapticsToPlay.value =
- HapticsToPlay(
- HapticFeedbackConstants.BIOMETRIC_CONFIRM,
- null,
- )
+ val haptics =
+ if (msdlFeedback()) {
+ HapticsToPlay.MSDL(MSDLToken.UNLOCK, authInteractionProperties)
+ } else {
+ HapticsToPlay.HapticConstant(
+ HapticFeedbackConstants.BIOMETRIC_CONFIRM,
+ flag = null,
+ )
+ }
+ _hapticsToPlay.value = haptics
}
private fun vibrateOnError() {
- _hapticsToPlay.value =
- HapticsToPlay(
- HapticFeedbackConstants.BIOMETRIC_REJECT,
- null,
- )
+ val haptics =
+ if (msdlFeedback()) {
+ HapticsToPlay.MSDL(MSDLToken.FAILURE, authInteractionProperties)
+ } else {
+ HapticsToPlay.HapticConstant(
+ HapticFeedbackConstants.BIOMETRIC_REJECT,
+ flag = null,
+ )
+ }
+ _hapticsToPlay.value = haptics
}
/** Clears the [hapticsToPlay] variable by setting its constant to the NO_HAPTICS default. */
fun clearHaptics() {
- _hapticsToPlay.update { previous ->
- HapticsToPlay(HapticFeedbackConstants.NO_HAPTICS, previous.flag)
- }
+ _hapticsToPlay.update { HapticsToPlay.None }
+ }
+
+ /** The state of haptic feedback to play. */
+ sealed interface HapticsToPlay {
+ /**
+ * Haptics using [HapticFeedbackConstants]. It is composed by a [HapticFeedbackConstants]
+ * and a [HapticFeedbackConstants] flag.
+ */
+ data class HapticConstant(val constant: Int, val flag: Int?) : HapticsToPlay
+
+ /**
+ * Haptics using MSDL feedback. It is composed by a [MSDLToken] and optional
+ * [InteractionProperties]
+ */
+ data class MSDL(val token: MSDLToken, val properties: InteractionProperties?) :
+ HapticsToPlay
+
+ data object None : HapticsToPlay
}
companion object {
@@ -1095,9 +1126,3 @@
val isStarted: Boolean
get() = this == Normal || this == Delayed
}
-
-/**
- * The state of haptic feedback to play. It is composed by a [HapticFeedbackConstants] and a
- * [HapticFeedbackConstants] flag.
- */
-data class HapticsToPlay(val hapticFeedbackConstant: Int, val flag: Int?)
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractor.kt
index f36ef66..8b5a09b 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractor.kt
@@ -34,10 +34,14 @@
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.doze.DozeLogger
+import com.android.systemui.scene.domain.interactor.SceneInteractor
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
+import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
import com.android.systemui.telephony.domain.interactor.TelephonyInteractor
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.EmergencyDialerConstants
+import dagger.Lazy
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
@@ -69,6 +73,7 @@
private val emergencyDialerIntentFactory: EmergencyDialerIntentFactory,
private val metricsLogger: MetricsLogger,
private val dozeLogger: DozeLogger,
+ private val sceneInteractor: Lazy<SceneInteractor>,
) {
/** The bouncer action button. If `null`, the button should not be shown. */
val actionButton: Flow<BouncerActionButtonModel?> =
@@ -158,14 +163,17 @@
}
private fun prepareToPerformAction() {
- // TODO(b/308001302): Trigger occlusion and resetting bouncer state.
+ if (SceneContainerFlag.isEnabled) {
+ sceneInteractor.get().changeScene(Scenes.Lockscreen, "Bouncer action button clicked")
+ }
+
metricsLogger.action(MetricsEvent.ACTION_EMERGENCY_CALL)
activityTaskManager.stopSystemLockTaskMode()
}
@SuppressLint("MissingPermission")
private fun returnToCall() {
- telecomManager?.showInCallScreen(/* showDialpad = */ false)
+ telecomManager?.showInCallScreen(/* showDialpad= */ false)
}
private val <T> Flow<T>.asUnitFlow: Flow<Unit>
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
index e1ba93c..83d4091 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
@@ -34,8 +34,6 @@
import com.android.systemui.classifier.FalsingDataProvider.SessionListener;
import com.android.systemui.classifier.HistoryTracker.BeliefListener;
import com.android.systemui.dagger.qualifiers.TestHarness;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -396,6 +394,7 @@
|| mDataProvider.isA11yAction()
|| mDataProvider.isFromTrackpad()
|| mDataProvider.isFromKeyboard()
+ || !mDataProvider.isTouchScreenSource()
|| mDataProvider.isUnfolded();
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
index 2eca02c..962ab99 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
@@ -21,6 +21,7 @@
import android.hardware.SensorManager;
import android.hardware.biometrics.BiometricSourceType;
import android.util.Log;
+import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
@@ -28,6 +29,7 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.systemui.Flags;
import com.android.systemui.communal.domain.interactor.CommunalInteractor;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
@@ -343,7 +345,9 @@
// will be ignored by the collector until another MotionEvent.ACTION_DOWN is passed in.
// avoidGesture must be called immediately following the MotionEvent.ACTION_DOWN, before
// any other events are processed, otherwise the whole gesture will be recorded.
- if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ //
+ // We should only delay processing of these events for touchscreen sources
+ if (ev.getActionMasked() == MotionEvent.ACTION_DOWN && isTouchscreenSource(ev)) {
// Make a copy of ev, since it will be recycled after we exit this method.
mPendingDownEvent = MotionEvent.obtain(ev);
mAvoidGesture = false;
@@ -410,6 +414,22 @@
mFalsingDataProvider.onA11yAction();
}
+ /**
+ * returns {@code true} if the device supports Touchscreen, {@code false} otherwise. Defaults to
+ * {@code true} if the device is {@code null}
+ */
+ private boolean isTouchscreenSource(MotionEvent ev) {
+ if (!Flags.nonTouchscreenDevicesBypassFalsing()) {
+ return true;
+ }
+ InputDevice device = ev.getDevice();
+ if (device != null) {
+ return device.supportsSource(InputDevice.SOURCE_TOUCHSCREEN);
+ } else {
+ return true;
+ }
+ }
+
private boolean shouldSessionBeActive() {
return mScreenOn
&& (mState == StatusBarState.KEYGUARD)
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java
index 1501701..769976e 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java
@@ -20,11 +20,13 @@
import android.hardware.devicestate.DeviceStateManager.FoldStateListener;
import android.util.DisplayMetrics;
+import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.MotionEvent.PointerCoords;
import android.view.MotionEvent.PointerProperties;
+import com.android.systemui.Flags;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dock.DockManager;
import com.android.systemui.statusbar.policy.BatteryController;
@@ -281,6 +283,9 @@
}
public boolean isFromTrackpad() {
+ if (Flags.nonTouchscreenDevicesBypassFalsing()) {
+ return false;
+ }
if (mRecentMotionEvents.isEmpty()) {
return false;
}
@@ -290,6 +295,25 @@
|| classification == MotionEvent.CLASSIFICATION_TWO_FINGER_SWIPE;
}
+ /**
+ * returns {@code true} if the device supports Touchscreen, {@code false} otherwise. Defaults to
+ * {@code true} if the device is {@code null}
+ */
+ public boolean isTouchScreenSource() {
+ if (!Flags.nonTouchscreenDevicesBypassFalsing()) {
+ return true;
+ }
+ if (mRecentMotionEvents.isEmpty()) {
+ return true;
+ }
+ InputDevice device = mRecentMotionEvents.get(mRecentMotionEvents.size() - 1).getDevice();
+ if (device != null) {
+ return device.supportsSource(InputDevice.SOURCE_TOUCHSCREEN);
+ } else {
+ return true;
+ }
+ }
+
private void recalculateData() {
if (!mDirty) {
return;
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/view/LongPressHandlingView.kt b/packages/SystemUI/src/com/android/systemui/common/ui/view/LongPressHandlingView.kt
index b6ace81..9c4736a 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/view/LongPressHandlingView.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/view/LongPressHandlingView.kt
@@ -27,6 +27,7 @@
import android.view.accessibility.AccessibilityNodeInfo
import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat
+import com.android.systemui.log.LongPressHandlingViewLogger
import com.android.systemui.shade.TouchLogger
import kotlin.math.pow
import kotlin.math.sqrt
@@ -42,6 +43,8 @@
context: Context,
attrs: AttributeSet?,
longPressDuration: () -> Long,
+ allowedTouchSlop: Int = ViewConfiguration.getTouchSlop(),
+ logger: LongPressHandlingViewLogger? = null,
) :
View(
context,
@@ -97,6 +100,8 @@
},
onSingleTapDetected = { listener?.onSingleTapDetected(this@LongPressHandlingView) },
longPressDuration = longPressDuration,
+ allowedTouchSlop = allowedTouchSlop,
+ logger = logger,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandler.kt b/packages/SystemUI/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandler.kt
index d3fc610..4e38a49 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/view/LongPressHandlingViewInteractionHandler.kt
@@ -17,7 +17,7 @@
package com.android.systemui.common.ui.view
-import android.view.ViewConfiguration
+import com.android.systemui.log.LongPressHandlingViewLogger
import kotlinx.coroutines.DisposableHandle
/** Encapsulates logic to handle complex touch interactions with a [LongPressHandlingView]. */
@@ -35,6 +35,14 @@
private val onSingleTapDetected: () -> Unit,
/** Time for the touch to be considered a long-press in ms */
var longPressDuration: () -> Long,
+ /**
+ * Default touch slop that is allowed, if the movement between [MotionEventModel.Down] and
+ * [MotionEventModel.Up] is more than [allowedTouchSlop] then the touch is not processed as
+ * single tap or a long press.
+ */
+ val allowedTouchSlop: Int,
+ /** Optional logger that can be passed in to log touch events */
+ val logger: LongPressHandlingViewLogger? = null,
) {
sealed class MotionEventModel {
object Other : MotionEventModel()
@@ -70,22 +78,26 @@
true
}
is MotionEventModel.Move -> {
- if (event.distanceMoved > ViewConfiguration.getTouchSlop()) {
+ if (event.distanceMoved > allowedTouchSlop) {
+ logger?.cancelingLongPressDueToTouchSlop(event.distanceMoved, allowedTouchSlop)
cancelScheduledLongPress()
}
false
}
is MotionEventModel.Up -> {
+ logger?.onUpEvent(event.distanceMoved, allowedTouchSlop, event.gestureDuration)
cancelScheduledLongPress()
if (
- event.distanceMoved <= ViewConfiguration.getTouchSlop() &&
+ event.distanceMoved <= allowedTouchSlop &&
event.gestureDuration < longPressDuration()
) {
+ logger?.dispatchingSingleTap()
dispatchSingleTap()
}
false
}
is MotionEventModel.Cancel -> {
+ logger?.motionEventCancelled()
cancelScheduledLongPress()
false
}
@@ -97,15 +109,18 @@
x: Int,
y: Int,
) {
+ val duration = longPressDuration()
+ logger?.schedulingLongPress(duration)
scheduledLongPressHandle =
postDelayed(
{
+ logger?.longPressTriggered()
dispatchLongPress(
x = x,
y = y,
)
},
- longPressDuration(),
+ duration,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StatusBarDisableFlagsInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StatusBarDisableFlagsInteractor.kt
index e00e33d..43aab35 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StatusBarDisableFlagsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StatusBarDisableFlagsInteractor.kt
@@ -39,12 +39,14 @@
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.power.shared.model.WakeSleepReason
import com.android.systemui.power.shared.model.WakefulnessModel
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -75,62 +77,66 @@
private val disableToken: IBinder = Binder()
private val disableFlagsForUserId =
- combine(
- selectedUserInteractor.selectedUser,
- keyguardTransitionInteractor.startedKeyguardTransitionStep.map { it.to },
- deviceConfigInteractor.property(
- namespace = DeviceConfig.NAMESPACE_SYSTEMUI,
- name = SystemUiDeviceConfigFlags.NAV_BAR_HANDLE_SHOW_OVER_LOCKSCREEN,
- default = true,
- ),
- navigationInteractor.isGesturalMode,
- authenticationInteractor.authenticationMethod,
- powerInteractor.detailedWakefulness,
- ) { values ->
- val selectedUserId = values[0] as Int
- val startedState = values[1] as KeyguardState
- val isShowHomeOverLockscreen = values[2] as Boolean
- val isGesturalMode = values[3] as Boolean
- val authenticationMethod = values[4] as AuthenticationMethodModel
- val wakefulnessModel = values[5] as WakefulnessModel
- val isOccluded = startedState == KeyguardState.OCCLUDED
+ if (!KeyguardWmStateRefactor.isEnabled || SceneContainerFlag.isEnabled) {
+ flowOf(Pair(0, StatusBarManager.DISABLE_NONE))
+ } else {
+ combine(
+ selectedUserInteractor.selectedUser,
+ keyguardTransitionInteractor.startedKeyguardTransitionStep.map { it.to },
+ deviceConfigInteractor.property(
+ namespace = DeviceConfig.NAMESPACE_SYSTEMUI,
+ name = SystemUiDeviceConfigFlags.NAV_BAR_HANDLE_SHOW_OVER_LOCKSCREEN,
+ default = true,
+ ),
+ navigationInteractor.isGesturalMode,
+ authenticationInteractor.authenticationMethod,
+ powerInteractor.detailedWakefulness,
+ ) { values ->
+ val selectedUserId = values[0] as Int
+ val startedState = values[1] as KeyguardState
+ val isShowHomeOverLockscreen = values[2] as Boolean
+ val isGesturalMode = values[3] as Boolean
+ val authenticationMethod = values[4] as AuthenticationMethodModel
+ val wakefulnessModel = values[5] as WakefulnessModel
+ val isOccluded = startedState == KeyguardState.OCCLUDED
- val hideHomeAndRecentsForBouncer =
- startedState == KeyguardState.PRIMARY_BOUNCER ||
- startedState == KeyguardState.ALTERNATE_BOUNCER
- val isKeyguardShowing = startedState != KeyguardState.GONE
- val isPowerGestureIntercepted =
- with(wakefulnessModel) {
- isAwake() &&
- powerButtonLaunchGestureTriggered &&
- lastSleepReason == WakeSleepReason.POWER_BUTTON
+ val hideHomeAndRecentsForBouncer =
+ startedState == KeyguardState.PRIMARY_BOUNCER ||
+ startedState == KeyguardState.ALTERNATE_BOUNCER
+ val isKeyguardShowing = startedState != KeyguardState.GONE
+ val isPowerGestureIntercepted =
+ with(wakefulnessModel) {
+ isAwake() &&
+ powerButtonLaunchGestureTriggered &&
+ lastSleepReason == WakeSleepReason.POWER_BUTTON
+ }
+
+ var flags = StatusBarManager.DISABLE_NONE
+
+ if (hideHomeAndRecentsForBouncer || (isKeyguardShowing && !isOccluded)) {
+ if (!isShowHomeOverLockscreen || !isGesturalMode) {
+ flags = flags or StatusBarManager.DISABLE_HOME
+ }
+ flags = flags or StatusBarManager.DISABLE_RECENT
}
- var flags = StatusBarManager.DISABLE_NONE
-
- if (hideHomeAndRecentsForBouncer || (isKeyguardShowing && !isOccluded)) {
- if (!isShowHomeOverLockscreen || !isGesturalMode) {
- flags = flags or StatusBarManager.DISABLE_HOME
+ if (
+ isPowerGestureIntercepted &&
+ isOccluded &&
+ authenticationMethod.isSecure &&
+ deviceEntryFaceAuthInteractor.isFaceAuthEnabledAndEnrolled()
+ ) {
+ flags = flags or StatusBarManager.DISABLE_RECENT
}
- flags = flags or StatusBarManager.DISABLE_RECENT
- }
- if (
- isPowerGestureIntercepted &&
- isOccluded &&
- authenticationMethod.isSecure &&
- deviceEntryFaceAuthInteractor.isFaceAuthEnabledAndEnrolled()
- ) {
- flags = flags or StatusBarManager.DISABLE_RECENT
+ selectedUserId to flags
}
-
- selectedUserId to flags
- }
- .distinctUntilChanged()
+ .distinctUntilChanged()
+ }
@SuppressLint("WrongConstant", "NonInjectedService")
override fun start() {
- if (!KeyguardWmStateRefactor.isEnabled) {
+ if (!KeyguardWmStateRefactor.isEnabled || SceneContainerFlag.isEnabled) {
return
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
index 91a7f7f..7696273 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinder.kt
@@ -42,6 +42,7 @@
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerUdfpsIconViewModel
import com.android.systemui.keyguard.ui.viewmodel.AlternateBouncerWindowViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.log.LongPressHandlingViewLogger
import com.android.systemui.res.R
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scrim.ScrimView
@@ -191,6 +192,7 @@
optionallyAddUdfpsViews(
view = view,
+ logger = alternateBouncerDependencies.logger,
udfpsIconViewModel = alternateBouncerDependencies.udfpsIconViewModel,
udfpsA11yOverlayViewModel =
alternateBouncerDependencies.udfpsAccessibilityOverlayViewModel,
@@ -248,6 +250,7 @@
private fun optionallyAddUdfpsViews(
view: ConstraintLayout,
+ logger: LongPressHandlingViewLogger,
udfpsIconViewModel: AlternateBouncerUdfpsIconViewModel,
udfpsA11yOverlayViewModel: Lazy<AlternateBouncerUdfpsAccessibilityOverlayViewModel>,
) {
@@ -276,7 +279,7 @@
var udfpsView = view.getViewById(udfpsViewId)
if (udfpsView == null) {
udfpsView =
- DeviceEntryIconView(view.context, null).apply {
+ DeviceEntryIconView(view.context, null, logger = logger).apply {
id = udfpsViewId
contentDescription =
context.resources.getString(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
index 4d6577c..b951b73 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt
@@ -19,6 +19,7 @@
import android.annotation.SuppressLint
import android.content.res.ColorStateList
+import android.util.Log
import android.util.StateSet
import android.view.HapticFeedbackConstants
import android.view.View
@@ -83,6 +84,11 @@
if (
!isA11yAction && falsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)
) {
+ Log.d(
+ TAG,
+ "Long press rejected because it is not a11yAction " +
+ "and it is a falseLongTap"
+ )
return
}
vibratorHelper.performHapticFeedback(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt
index 3e6d5da..8d2e939 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/DeviceEntryIconView.kt
@@ -23,6 +23,7 @@
import android.util.StateSet
import android.view.Gravity
import android.view.View
+import android.view.ViewConfiguration
import android.view.ViewGroup
import android.view.accessibility.AccessibilityNodeInfo
import android.widget.FrameLayout
@@ -31,6 +32,7 @@
import com.airbnb.lottie.LottieCompositionFactory
import com.airbnb.lottie.LottieDrawable
import com.android.systemui.common.ui.view.LongPressHandlingView
+import com.android.systemui.log.LongPressHandlingViewLogger
import com.android.systemui.res.R
class DeviceEntryIconView
@@ -39,8 +41,17 @@
context: Context,
attrs: AttributeSet?,
defStyleAttrs: Int = 0,
+ logger: LongPressHandlingViewLogger? = null,
) : FrameLayout(context, attrs, defStyleAttrs) {
- val longPressHandlingView: LongPressHandlingView = LongPressHandlingView(context, attrs)
+
+ val longPressHandlingView: LongPressHandlingView =
+ LongPressHandlingView(
+ context = context,
+ attrs = attrs,
+ longPressDuration = { ViewConfiguration.getLongPressTimeout().toLong() },
+ allowedTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(),
+ logger = logger,
+ )
val iconView: ImageView = ImageView(context, attrs).apply { id = R.id.device_entry_icon_fg }
val bgView: ImageView = ImageView(context, attrs).apply { id = R.id.device_entry_icon_bg }
val aodFpDrawable: LottieDrawable = LottieDrawable()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
index 51230dd..782d37b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt
@@ -42,6 +42,9 @@
import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryBackgroundViewModel
import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryForegroundViewModel
import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryIconViewModel
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LongPressHandlingViewLogger
+import com.android.systemui.log.dagger.LongPressTouchLog
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.res.R
import com.android.systemui.shade.NotificationPanelView
@@ -69,6 +72,7 @@
private val deviceEntryBackgroundViewModel: Lazy<DeviceEntryBackgroundViewModel>,
private val falsingManager: Lazy<FalsingManager>,
private val vibratorHelper: Lazy<VibratorHelper>,
+ @LongPressTouchLog private val logBuffer: LogBuffer,
) : KeyguardSection() {
private val deviceEntryIconViewId = R.id.device_entry_icon_view
private var disposableHandle: DisposableHandle? = null
@@ -88,7 +92,16 @@
val view =
if (DeviceEntryUdfpsRefactor.isEnabled) {
- DeviceEntryIconView(context, null).apply { id = deviceEntryIconViewId }
+ DeviceEntryIconView(
+ context,
+ null,
+ logger =
+ LongPressHandlingViewLogger(
+ logBuffer = logBuffer,
+ TAG
+ )
+ )
+ .apply { id = deviceEntryIconViewId }
} else {
// KeyguardBottomAreaRefactor.isEnabled or MigrateClocksToBlueprint.isEnabled
LockIconView(context, null).apply { id = R.id.lock_icon_view }
@@ -258,4 +271,8 @@
}
}
}
+
+ companion object {
+ private const val TAG = "DefaultDeviceEntrySection"
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerDependencies.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerDependencies.kt
index b432417..9f8e9c5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerDependencies.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerDependencies.kt
@@ -18,6 +18,9 @@
import com.android.systemui.deviceentry.ui.viewmodel.AlternateBouncerUdfpsAccessibilityOverlayViewModel
import com.android.systemui.keyguard.ui.SwipeUpAnywhereGestureHandler
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LongPressHandlingViewLogger
+import com.android.systemui.log.dagger.LongPressTouchLog
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.statusbar.gesture.TapGestureDetector
import dagger.Lazy
@@ -37,4 +40,11 @@
Lazy<AlternateBouncerUdfpsAccessibilityOverlayViewModel>,
val messageAreaViewModel: AlternateBouncerMessageAreaViewModel,
val powerInteractor: PowerInteractor,
-)
+ @LongPressTouchLog private val touchLogBuffer: LogBuffer,
+) {
+ val logger: LongPressHandlingViewLogger =
+ LongPressHandlingViewLogger(logBuffer = touchLogBuffer, TAG)
+ companion object {
+ private const val TAG = "AlternateBouncer"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/log/LongPressHandlingViewLogger.kt b/packages/SystemUI/src/com/android/systemui/log/LongPressHandlingViewLogger.kt
new file mode 100644
index 0000000..4ff8118
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/LongPressHandlingViewLogger.kt
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.log
+
+import com.android.systemui.log.core.LogLevel.DEBUG
+import com.google.errorprone.annotations.CompileTimeConstant
+
+data class LongPressHandlingViewLogger
+constructor(
+ private val logBuffer: LogBuffer,
+ @CompileTimeConstant private val tag: String = "LongPressHandlingViewLogger"
+) {
+ fun schedulingLongPress(delay: Long) {
+ logBuffer.log(
+ tag,
+ DEBUG,
+ { long1 = delay },
+ { "on MotionEvent.Down: scheduling long press activation after $long1 ms" }
+ )
+ }
+
+ fun longPressTriggered() {
+ logBuffer.log(tag, DEBUG, "long press event detected and dispatched")
+ }
+
+ fun motionEventCancelled() {
+ logBuffer.log(tag, DEBUG, "Long press may be cancelled due to MotionEventModel.Cancel")
+ }
+
+ fun dispatchingSingleTap() {
+ logBuffer.log(tag, DEBUG, "Dispatching single tap instead of long press")
+ }
+
+ fun onUpEvent(distanceMoved: Float, touchSlop: Int, gestureDuration: Long) {
+ logBuffer.log(
+ tag,
+ DEBUG,
+ {
+ double1 = distanceMoved.toDouble()
+ int1 = touchSlop
+ long1 = gestureDuration
+ },
+ {
+ "on MotionEvent.Up: distanceMoved: $double1, " +
+ "allowedTouchSlop: $int1, " +
+ "eventDuration: $long1"
+ }
+ )
+ }
+
+ fun cancelingLongPressDueToTouchSlop(distanceMoved: Float, allowedTouchSlop: Int) {
+ logBuffer.log(
+ tag,
+ DEBUG,
+ {
+ double1 = distanceMoved.toDouble()
+ int1 = allowedTouchSlop
+ },
+ {
+ "on MotionEvent.Motion: May cancel long press due to movement: " +
+ "distanceMoved: $double1, " +
+ "allowedTouchSlop: $int1 "
+ }
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
index 19906fd..498c34c 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -717,4 +717,12 @@
public static LogBuffer provideVolumeLogBuffer(LogBufferFactory factory) {
return factory.create("VolumeLog", 50);
}
+
+ /** Provides a {@link LogBuffer} for use by long touch event handlers. */
+ @Provides
+ @SysUISingleton
+ @LongPressTouchLog
+ public static LogBuffer providesLongPressTouchLog(LogBufferFactory factory) {
+ return factory.create("LongPressViewLog", 200);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/log/dagger/LongPressTouchLog.kt b/packages/SystemUI/src/com/android/systemui/log/dagger/LongPressTouchLog.kt
new file mode 100644
index 0000000..1163d74
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LongPressTouchLog.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.log.dagger
+
+import javax.inject.Qualifier
+
+/** Log buffer for logging touch/long press events */
+@Qualifier
+@MustBeDocumented
+@Retention(AnnotationRetention.RUNTIME)
+annotation class LongPressTouchLog
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HearingDevicesTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HearingDevicesTile.java
index b96e83d..f723ff2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HearingDevicesTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HearingDevicesTile.java
@@ -16,6 +16,8 @@
package com.android.systemui.qs.tiles;
+import static com.android.systemui.accessibility.hearingaid.HearingDevicesUiEventLogger.LAUNCH_SOURCE_QS_TILE;
+
import android.content.Intent;
import android.os.Handler;
import android.os.Looper;
@@ -96,7 +98,7 @@
@Override
protected void handleClick(@Nullable Expandable expandable) {
- mUiHandler.post(() -> mDialogManager.showDialog(expandable));
+ mUiHandler.post(() -> mDialogManager.showDialog(expandable, LAUNCH_SOURCE_QS_TILE));
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt
index a3feb2b..d89e73d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RecordIssueTile.kt
@@ -43,12 +43,14 @@
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor
import com.android.systemui.qs.tileimpl.QSTileImpl
-import com.android.systemui.recordissue.IssueRecordingService
+import com.android.systemui.recordissue.IssueRecordingService.Companion.getStartIntent
+import com.android.systemui.recordissue.IssueRecordingService.Companion.getStopIntent
import com.android.systemui.recordissue.IssueRecordingState
import com.android.systemui.recordissue.RecordIssueDialogDelegate
import com.android.systemui.recordissue.RecordIssueModule.Companion.TILE_SPEC
import com.android.systemui.recordissue.TraceurMessageSender
import com.android.systemui.res.R
+import com.android.systemui.screenrecord.RecordingController
import com.android.systemui.screenrecord.RecordingService
import com.android.systemui.settings.UserContextProvider
import com.android.systemui.statusbar.phone.KeyguardDismissUtil
@@ -56,6 +58,9 @@
import java.util.concurrent.Executor
import javax.inject.Inject
+const val DELAY_MS: Long = 0
+const val INTERVAL_MS: Long = 1000
+
class RecordIssueTile
@Inject
constructor(
@@ -77,6 +82,7 @@
@Background private val bgExecutor: Executor,
private val issueRecordingState: IssueRecordingState,
private val delegateFactory: RecordIssueDialogDelegate.Factory,
+ private val recordingController: RecordingController,
) :
QSTileImpl<QSTile.BooleanState>(
host,
@@ -132,23 +138,25 @@
}
private fun startIssueRecordingService() =
- PendingIntent.getForegroundService(
- userContextProvider.userContext,
- RecordingService.REQUEST_CODE,
- IssueRecordingService.getStartIntent(userContextProvider.userContext),
- PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
- )
- .send(BroadcastOptions.makeBasic().apply { isInteractive = true }.toBundle())
+ recordingController.startCountdown(
+ DELAY_MS,
+ INTERVAL_MS,
+ pendingServiceIntent(getStartIntent(userContextProvider.userContext)),
+ pendingServiceIntent(getStopIntent(userContextProvider.userContext))
+ )
private fun stopIssueRecordingService() =
- PendingIntent.getService(
- userContextProvider.userContext,
- RecordingService.REQUEST_CODE,
- IssueRecordingService.getStopIntent(userContextProvider.userContext),
- PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
- )
+ pendingServiceIntent(getStopIntent(userContextProvider.userContext))
.send(BroadcastOptions.makeBasic().apply { isInteractive = true }.toBundle())
+ private fun pendingServiceIntent(action: Intent) =
+ PendingIntent.getService(
+ userContextProvider.userContext,
+ RecordingService.REQUEST_CODE,
+ action,
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
+ )
+
private fun showPrompt(expandable: Expandable?) {
val dialog: AlertDialog =
delegateFactory
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractor.kt
index 4971fef..0c8a375 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/irecording/IssueRecordingUserActionInteractor.kt
@@ -19,6 +19,7 @@
import android.app.AlertDialog
import android.app.BroadcastOptions
import android.app.PendingIntent
+import android.content.Intent
import android.util.Log
import com.android.internal.jank.InteractionJankMonitor
import com.android.systemui.animation.DialogCuj
@@ -27,12 +28,16 @@
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor
+import com.android.systemui.qs.tiles.DELAY_MS
+import com.android.systemui.qs.tiles.INTERVAL_MS
import com.android.systemui.qs.tiles.base.interactor.QSTileInput
import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor
import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction
-import com.android.systemui.recordissue.IssueRecordingService
+import com.android.systemui.recordissue.IssueRecordingService.Companion.getStartIntent
+import com.android.systemui.recordissue.IssueRecordingService.Companion.getStopIntent
import com.android.systemui.recordissue.RecordIssueDialogDelegate
import com.android.systemui.recordissue.RecordIssueModule.Companion.TILE_SPEC
+import com.android.systemui.screenrecord.RecordingController
import com.android.systemui.screenrecord.RecordingService
import com.android.systemui.settings.UserContextProvider
import com.android.systemui.statusbar.phone.KeyguardDismissUtil
@@ -53,6 +58,7 @@
private val panelInteractor: PanelInteractor,
private val userContextProvider: UserContextProvider,
private val delegateFactory: RecordIssueDialogDelegate.Factory,
+ private val recordingController: RecordingController,
) : QSTileUserActionInteractor<IssueRecordingModel> {
override suspend fun handleInput(input: QSTileInput<IssueRecordingModel>) {
@@ -95,20 +101,22 @@
}
private fun startIssueRecordingService() =
- PendingIntent.getForegroundService(
- userContextProvider.userContext,
- RecordingService.REQUEST_CODE,
- IssueRecordingService.getStartIntent(userContextProvider.userContext),
- PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
- )
- .send(BroadcastOptions.makeBasic().apply { isInteractive = true }.toBundle())
+ recordingController.startCountdown(
+ DELAY_MS,
+ INTERVAL_MS,
+ pendingServiceIntent(getStartIntent(userContextProvider.userContext)),
+ pendingServiceIntent(getStopIntent(userContextProvider.userContext))
+ )
private fun stopIssueRecordingService() =
- PendingIntent.getService(
- userContextProvider.userContext,
- RecordingService.REQUEST_CODE,
- IssueRecordingService.getStopIntent(userContextProvider.userContext),
- PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
- )
+ pendingServiceIntent(getStopIntent(userContextProvider.userContext))
.send(BroadcastOptions.makeBasic().apply { isInteractive = true }.toBundle())
+
+ private fun pendingServiceIntent(action: Intent) =
+ PendingIntent.getService(
+ userContextProvider.userContext,
+ RecordingService.REQUEST_CODE,
+ action,
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
+ )
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt
index c2d112e..483373d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/domain/interactor/ModesTileDataInteractor.kt
@@ -20,13 +20,16 @@
import android.content.Context
import android.os.UserHandle
import com.android.app.tracing.coroutines.flow.map
+import com.android.systemui.common.shared.model.Icon
import com.android.systemui.common.shared.model.asIcon
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.qs.tiles.ModesTile
import com.android.systemui.qs.tiles.base.interactor.DataUpdateTrigger
import com.android.systemui.qs.tiles.base.interactor.QSTileDataInteractor
import com.android.systemui.qs.tiles.impl.modes.domain.model.ModesTileModel
import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor
import com.android.systemui.statusbar.policy.domain.model.ActiveZenModes
+import com.android.systemui.statusbar.policy.domain.model.ZenModeInfo
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
@@ -61,28 +64,39 @@
suspend fun getCurrentTileModel() = buildTileData(zenModeInteractor.getActiveModes())
private fun buildTileData(activeModes: ActiveZenModes): ModesTileModel {
- val modesIconResId = com.android.internal.R.drawable.ic_zen_priority_modes
-
if (usesModeIcons()) {
- val mainModeDrawable = activeModes.mainMode?.icon?.drawable
- val iconResId = if (mainModeDrawable == null) modesIconResId else null
-
+ val tileIcon = getTileIcon(activeModes.mainMode)
return ModesTileModel(
isActivated = activeModes.isAnyActive(),
- icon = (mainModeDrawable ?: context.getDrawable(modesIconResId)!!).asIcon(),
- iconResId = iconResId,
+ icon = tileIcon.icon,
+ iconResId = tileIcon.resId,
activeModes = activeModes.modeNames
)
} else {
return ModesTileModel(
isActivated = activeModes.isAnyActive(),
- icon = context.getDrawable(modesIconResId)!!.asIcon(),
- iconResId = modesIconResId,
+ icon = context.getDrawable(ModesTile.ICON_RES_ID)!!.asIcon(),
+ iconResId = ModesTile.ICON_RES_ID,
activeModes = activeModes.modeNames
)
}
}
+ private data class TileIcon(val icon: Icon.Loaded, val resId: Int?)
+
+ private fun getTileIcon(activeMode: ZenModeInfo?): TileIcon {
+ return if (activeMode != null) {
+ // ZenIconKey.resPackage is null if its resId is a system icon.
+ if (activeMode.icon.key.resPackage == null) {
+ TileIcon(activeMode.icon.drawable.asIcon(), activeMode.icon.key.resId)
+ } else {
+ TileIcon(activeMode.icon.drawable.asIcon(), null)
+ }
+ } else {
+ TileIcon(context.getDrawable(ModesTile.ICON_RES_ID)!!.asIcon(), ModesTile.ICON_RES_ID)
+ }
+ }
+
override fun availability(user: UserHandle): Flow<Boolean> = flowOf(Flags.modesUi())
private fun usesModeIcons() = Flags.modesApi() && Flags.modesUi() && Flags.modesUiIcons()
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt
index 7f571b1..69da313 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/modes/ui/ModesTileMapper.kt
@@ -36,9 +36,7 @@
) : QSTileDataToStateMapper<ModesTileModel> {
override fun map(config: QSTileConfig, data: ModesTileModel): QSTileState =
QSTileState.build(resources, theme, config.uiConfig) {
- if (!android.app.Flags.modesUiIcons()) {
- iconRes = data.iconResId
- }
+ iconRes = data.iconResId
icon = { data.icon }
activationState =
if (data.isActivated) {
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
index e251c9e..98907b0 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt
@@ -22,7 +22,9 @@
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.compose.animation.scene.SceneKey
import com.android.internal.logging.UiEventLogger
+import com.android.keyguard.AuthInteractionProperties
import com.android.systemui.CoreStartable
+import com.android.systemui.Flags
import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
@@ -73,6 +75,8 @@
import com.android.systemui.util.kotlin.sample
import com.android.systemui.util.printSection
import com.android.systemui.util.println
+import com.google.android.msdl.data.model.MSDLToken
+import com.google.android.msdl.domain.MSDLPlayer
import dagger.Lazy
import java.io.PrintWriter
import java.util.Optional
@@ -139,10 +143,13 @@
private val statusBarStateController: SysuiStatusBarStateController,
private val alternateBouncerInteractor: AlternateBouncerInteractor,
private val vibratorHelper: VibratorHelper,
+ private val msdlPlayer: MSDLPlayer,
) : CoreStartable {
private val centralSurfaces: CentralSurfaces?
get() = centralSurfacesOptLazy.get().getOrNull()
+ private val authInteractionProperties = AuthInteractionProperties()
+
override fun start() {
if (SceneContainerFlag.isEnabled) {
sceneLogger.logFrameworkEnabled(isEnabled = true)
@@ -541,9 +548,16 @@
deviceEntryHapticsInteractor.playSuccessHaptic
.sample(sceneInteractor.currentScene)
.collect { currentScene ->
- vibratorHelper.vibrateAuthSuccess(
- "$TAG, $currentScene device-entry::success"
- )
+ if (Flags.msdlFeedback()) {
+ msdlPlayer.playToken(
+ MSDLToken.UNLOCK,
+ authInteractionProperties,
+ )
+ } else {
+ vibratorHelper.vibrateAuthSuccess(
+ "$TAG, $currentScene device-entry::success"
+ )
+ }
}
}
@@ -551,9 +565,16 @@
deviceEntryHapticsInteractor.playErrorHaptic
.sample(sceneInteractor.currentScene)
.collect { currentScene ->
- vibratorHelper.vibrateAuthError(
- "$TAG, $currentScene device-entry::error"
- )
+ if (Flags.msdlFeedback()) {
+ msdlPlayer.playToken(
+ MSDLToken.FAILURE,
+ authInteractionProperties,
+ )
+ } else {
+ vibratorHelper.vibrateAuthError(
+ "$TAG, $currentScene device-entry::error"
+ )
+ }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java
index 64dfc6c..735b4c3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java
@@ -54,6 +54,8 @@
VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK);
private static final VibrationAttributes HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES =
VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK);
+ private static final VibrationAttributes COMMUNICATION_REQUEST_VIBRATION_ATTRIBUTES =
+ VibrationAttributes.createForUsage(VibrationAttributes.USAGE_COMMUNICATION_REQUEST);
private final Executor mExecutor;
@@ -151,7 +153,7 @@
vibrate(Process.myUid(),
"com.android.systemui",
BIOMETRIC_SUCCESS_VIBRATION_EFFECT, reason,
- HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES);
+ COMMUNICATION_REQUEST_VIBRATION_ATTRIBUTES);
}
/**
@@ -160,7 +162,7 @@
public void vibrateAuthError(String reason) {
vibrate(Process.myUid(), "com.android.systemui",
BIOMETRIC_ERROR_VIBRATION_EFFECT, reason,
- HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES);
+ COMMUNICATION_REQUEST_VIBRATION_ATTRIBUTES);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RichOngoingNotificationViewInflater.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RichOngoingNotificationViewInflater.kt
index 2c462b7..77c4130 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RichOngoingNotificationViewInflater.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RichOngoingNotificationViewInflater.kt
@@ -198,12 +198,22 @@
parentView,
/* attachToRoot= */ false
) as EnRouteView
-
InflatedContentViewHolder(newView) {
EnRouteViewBinder.bindWhileAttached(newView, createViewModel())
}
}
- RichOngoingNotificationViewType.Expanded,
+ RichOngoingNotificationViewType.Expanded -> {
+ val newView =
+ LayoutInflater.from(systemUiContext)
+ .inflate(
+ R.layout.notification_template_en_route_expanded,
+ parentView,
+ /* attachToRoot= */ false
+ ) as EnRouteView
+ InflatedContentViewHolder(newView) {
+ EnRouteViewBinder.bindWhileAttached(newView, createViewModel())
+ }
+ }
RichOngoingNotificationViewType.HeadsUp -> NullContentView
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 7c01784..8ff1ab6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -3691,6 +3691,10 @@
if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
switch (event.getAction()) {
case MotionEvent.ACTION_SCROLL: {
+ // If scene container is active, NSSL should not control its own scrolling.
+ if (SceneContainerFlag.isEnabled()) {
+ return false;
+ }
if (!mIsBeingDragged) {
final float vscroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
if (vscroll != 0) {
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 43f9af6..dd4b000 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -992,7 +992,7 @@
} else {
showBouncerOrKeyguard(hideBouncerWhenShowing, isFalsingReset);
}
- if (!SceneContainerFlag.isEnabled() && hideBouncerWhenShowing) {
+ if (!SceneContainerFlag.isEnabled() && hideBouncerWhenShowing && isBouncerShowing()) {
hideAlternateBouncer(true);
mDismissCallbackRegistry.notifyDismissCancelled();
mPrimaryBouncerInteractor.setDismissAction(null, null);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/NetworkNameModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/NetworkNameModel.kt
index 85bbe7e..d601319 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/NetworkNameModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/NetworkNameModel.kt
@@ -100,7 +100,14 @@
val showSpn = getBooleanExtra(EXTRA_SHOW_SPN, false)
val spn =
if (statusBarSwitchToSpnFromDataSpn()) {
- getStringExtra(EXTRA_SPN)
+ // Context: b/358669494. Use DATA_SPN if it exists, since that allows carriers to
+ // customize the display name. Otherwise, fall back to the SPN
+ val dataSpn = getStringExtra(EXTRA_DATA_SPN)
+ if (dataSpn.isNullOrEmpty()) {
+ getStringExtra(EXTRA_SPN)
+ } else {
+ dataSpn
+ }
} else {
getStringExtra(EXTRA_DATA_SPN)
}
@@ -109,10 +116,8 @@
val plmn = getStringExtra(EXTRA_PLMN)
val str = StringBuilder()
- val strData = StringBuilder()
if (showPlmn && plmn != null) {
str.append(plmn)
- strData.append(plmn)
}
if (showSpn && spn != null) {
if (str.isNotEmpty()) {
diff --git a/packages/SystemUI/src/com/android/systemui/telephony/domain/interactor/TelephonyInteractor.kt b/packages/SystemUI/src/com/android/systemui/telephony/domain/interactor/TelephonyInteractor.kt
index 4b0e5d1..6d99183 100644
--- a/packages/SystemUI/src/com/android/systemui/telephony/domain/interactor/TelephonyInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/telephony/domain/interactor/TelephonyInteractor.kt
@@ -29,11 +29,12 @@
class TelephonyInteractor
@Inject
constructor(
- repository: TelephonyRepository,
+ private val repository: TelephonyRepository,
) {
@Annotation.CallState val callState: Flow<Int> = repository.callState
val isInCall: StateFlow<Boolean> = repository.isInCall
- val hasTelephonyRadio: Boolean = repository.hasTelephonyRadio
+ val hasTelephonyRadio: Boolean
+ get() = repository.hasTelephonyRadio
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
index 8934d8f..d9e72bf 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
@@ -1305,13 +1305,12 @@
if (action.equals(AudioManager.VOLUME_CHANGED_ACTION)) {
final int stream = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE,
STREAM_UNKNOWN);
- final int level = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, -1);
final int oldLevel = intent
.getIntExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, -1);
if (D.BUG) Log.d(TAG, "onReceive VOLUME_CHANGED_ACTION stream=" + stream
- + " level=" + level + " oldLevel=" + oldLevel);
+ + " oldLevel=" + oldLevel);
if (stream != STREAM_UNKNOWN) {
- changed = updateStreamLevelW(stream, level);
+ changed |= onVolumeChangedW(stream, 0);
}
} else if (action.equals(AudioManager.STREAM_DEVICES_CHANGED_ACTION)) {
final int stream = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE,
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/EmergencyButtonControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/EmergencyButtonControllerTest.kt
index 43a78035..c42e25b 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/EmergencyButtonControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/EmergencyButtonControllerTest.kt
@@ -29,7 +29,7 @@
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
-import com.android.systemui.haptics.msdl.FakeMSDLPlayer
+import com.android.systemui.haptics.msdl.fakeMSDLPlayer
import com.android.systemui.haptics.msdl.msdlPlayer
import com.android.systemui.shade.ShadeController
import com.android.systemui.statusbar.policy.ConfigurationController
@@ -72,7 +72,7 @@
val mainExecutor = FakeExecutor(fakeSystemClock)
val backgroundExecutor = FakeExecutor(fakeSystemClock)
private val kosmos = testKosmos()
- private val msdlPlayer: FakeMSDLPlayer = kosmos.msdlPlayer
+ private val msdlPlayer = kosmos.fakeMSDLPlayer
lateinit var underTest: EmergencyButtonController
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
index d3b7d22..662815e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegateTest.java
@@ -52,7 +52,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import com.android.internal.logging.UiEventLogger;
import com.android.settingslib.bluetooth.BluetoothEventManager;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
@@ -92,6 +91,7 @@
@Rule
public MockitoRule mockito = MockitoJUnit.rule();
+ private static final int TEST_LAUNCH_SOURCE_ID = 1;
private static final String DEVICE_ADDRESS = "AA:BB:CC:DD:EE:FF";
private static final String DEVICE_NAME = "test_name";
private static final String TEST_PKG = "pkg";
@@ -124,7 +124,7 @@
@Mock
private AudioManager mAudioManager;
@Mock
- private UiEventLogger mUiEventLogger;
+ private HearingDevicesUiEventLogger mUiEventLogger;
@Mock
private CachedBluetoothDevice mCachedDevice;
@Mock
@@ -182,7 +182,8 @@
anyInt(), any());
assertThat(intentCaptor.getValue().getAction()).isEqualTo(
Settings.ACTION_HEARING_DEVICE_PAIRING_SETTINGS);
- verify(mUiEventLogger).log(HearingDevicesUiEvent.HEARING_DEVICES_PAIR);
+ verify(mUiEventLogger).log(HearingDevicesUiEvent.HEARING_DEVICES_PAIR,
+ TEST_LAUNCH_SOURCE_ID);
}
@Test
@@ -196,7 +197,8 @@
anyInt(), any());
assertThat(intentCaptor.getValue().getAction()).isEqualTo(
HearingDevicesDialogDelegate.ACTION_BLUETOOTH_DEVICE_DETAILS);
- verify(mUiEventLogger).log(HearingDevicesUiEvent.HEARING_DEVICES_GEAR_CLICK);
+ verify(mUiEventLogger).log(HearingDevicesUiEvent.HEARING_DEVICES_GEAR_CLICK,
+ TEST_LAUNCH_SOURCE_ID);
}
@Test
@@ -207,7 +209,8 @@
mDialogDelegate.onDeviceItemClicked(mHearingDeviceItem, new View(mContext));
verify(mCachedDevice).disconnect();
- verify(mUiEventLogger).log(HearingDevicesUiEvent.HEARING_DEVICES_DISCONNECT);
+ verify(mUiEventLogger).log(HearingDevicesUiEvent.HEARING_DEVICES_DISCONNECT,
+ TEST_LAUNCH_SOURCE_ID);
}
@Test
@@ -304,6 +307,7 @@
mDialogDelegate = new HearingDevicesDialogDelegate(
mContext,
true,
+ TEST_LAUNCH_SOURCE_ID,
mDialogFactory,
mActivityStarter,
mDialogTransitionAnimator,
@@ -327,6 +331,7 @@
mDialogDelegate = new HearingDevicesDialogDelegate(
mContext,
false,
+ TEST_LAUNCH_SOURCE_ID,
mDialogFactory,
mActivityStarter,
mDialogTransitionAnimator,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
index 1e23690..7889b3c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -61,10 +61,12 @@
import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel
import com.android.systemui.biometrics.ui.viewmodel.PromptViewModel
import com.android.systemui.display.data.repository.FakeDisplayRepository
+import com.android.systemui.haptics.msdl.msdlPlayer
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.res.R
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.statusbar.events.ANIMATING_OUT
+import com.android.systemui.testKosmos
import com.android.systemui.user.domain.interactor.SelectedUserInteractor
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.time.FakeSystemClock
@@ -145,6 +147,9 @@
private val credentialViewModel = CredentialViewModel(mContext, bpCredentialInteractor)
private val defaultLogoIcon = context.getDrawable(R.drawable.ic_android)
+ private val kosmos = testKosmos()
+ private val msdlPlayer = kosmos.msdlPlayer
+
private var authContainer: TestAuthContainerView? = null
@Before
@@ -668,7 +673,8 @@
{ credentialViewModel },
fakeExecutor,
vibrator,
- lazyViewCapture
+ lazyViewCapture,
+ msdlPlayer,
) {
override fun postOnAnimation(runnable: Runnable) {
runnable.run()
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 4fc4166..55fd344 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
@@ -37,12 +37,15 @@
import android.hardware.face.FaceSensorPropertiesInternal
import android.hardware.fingerprint.FingerprintSensorProperties
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
+import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.view.HapticFeedbackConstants
import android.view.MotionEvent
import android.view.Surface
import androidx.test.filters.SmallTest
import com.android.app.activityTaskManager
+import com.android.keyguard.AuthInteractionProperties
+import com.android.systemui.Flags
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.AuthController
import com.android.systemui.biometrics.Utils.toBitmap
@@ -72,6 +75,7 @@
import com.android.systemui.kosmos.testScope
import com.android.systemui.res.R
import com.android.systemui.util.mockito.withArgCaptor
+import com.google.android.msdl.data.model.MSDLToken
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.first
@@ -124,6 +128,7 @@
private val defaultLogoDescriptionFromActivityInfo = "Test Coke App"
private val logoDescriptionFromApp = "Test Cake App"
private val packageNameForLogoWithOverrides = "should.use.overridden.logo"
+ private val authInteractionProperties = AuthInteractionProperties()
/** Prompt panel size padding */
private val smallHorizontalGuidelinePadding =
@@ -707,31 +712,66 @@
}
@Test
+ @DisableFlags(Flags.FLAG_MSDL_FEEDBACK)
fun set_haptic_on_confirm_when_confirmation_required_otherwise_on_authenticated() =
runGenericTest {
val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false)
kosmos.promptViewModel.showAuthenticated(testCase.authenticatedModality, 1_000L)
- val confirmHaptics by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
- assertThat(confirmHaptics?.hapticFeedbackConstant)
- .isEqualTo(
- if (expectConfirmation) HapticFeedbackConstants.NO_HAPTICS
- else HapticFeedbackConstants.BIOMETRIC_CONFIRM
- )
- assertThat(confirmHaptics?.flag).isNull()
+ val hapticsPreConfirm by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
+ if (expectConfirmation) {
+ assertThat(hapticsPreConfirm).isEqualTo(PromptViewModel.HapticsToPlay.None)
+ } else {
+ val confirmHaptics =
+ hapticsPreConfirm as PromptViewModel.HapticsToPlay.HapticConstant
+ assertThat(confirmHaptics.constant)
+ .isEqualTo(HapticFeedbackConstants.BIOMETRIC_CONFIRM)
+ assertThat(confirmHaptics.flag).isNull()
+ }
if (expectConfirmation) {
kosmos.promptViewModel.confirmAuthenticated()
}
- val confirmedHaptics by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
- assertThat(confirmedHaptics?.hapticFeedbackConstant)
+ val hapticsPostConfirm by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
+ val confirmedHaptics =
+ hapticsPostConfirm as PromptViewModel.HapticsToPlay.HapticConstant
+ assertThat(confirmedHaptics.constant)
.isEqualTo(HapticFeedbackConstants.BIOMETRIC_CONFIRM)
- assertThat(confirmedHaptics?.flag).isNull()
+ assertThat(confirmedHaptics.flag).isNull()
}
@Test
+ @EnableFlags(Flags.FLAG_MSDL_FEEDBACK)
+ fun set_msdl_haptic_on_confirm_when_confirmation_required_otherwise_on_authenticated() =
+ runGenericTest {
+ val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false)
+
+ kosmos.promptViewModel.showAuthenticated(testCase.authenticatedModality, 1_000L)
+
+ val hapticsPreConfirm by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
+
+ if (expectConfirmation) {
+ assertThat(hapticsPreConfirm).isEqualTo(PromptViewModel.HapticsToPlay.None)
+ } else {
+ val confirmHaptics = hapticsPreConfirm as PromptViewModel.HapticsToPlay.MSDL
+ assertThat(confirmHaptics.token).isEqualTo(MSDLToken.UNLOCK)
+ assertThat(confirmHaptics.properties).isEqualTo(authInteractionProperties)
+ }
+
+ if (expectConfirmation) {
+ kosmos.promptViewModel.confirmAuthenticated()
+ }
+
+ val hapticsPostConfirm by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
+ val confirmedHaptics = hapticsPostConfirm as PromptViewModel.HapticsToPlay.MSDL
+ assertThat(confirmedHaptics.token).isEqualTo(MSDLToken.UNLOCK)
+ assertThat(confirmedHaptics.properties).isEqualTo(authInteractionProperties)
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_MSDL_FEEDBACK)
fun playSuccessHaptic_SetsConfirmConstant() = runGenericTest {
val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false)
kosmos.promptViewModel.showAuthenticated(testCase.authenticatedModality, 1_000L)
@@ -740,20 +780,48 @@
kosmos.promptViewModel.confirmAuthenticated()
}
- val currentHaptics by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
- assertThat(currentHaptics?.hapticFeedbackConstant)
- .isEqualTo(HapticFeedbackConstants.BIOMETRIC_CONFIRM)
- assertThat(currentHaptics?.flag).isNull()
+ val haptics by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
+ val currentHaptics = haptics as PromptViewModel.HapticsToPlay.HapticConstant
+ assertThat(currentHaptics.constant).isEqualTo(HapticFeedbackConstants.BIOMETRIC_CONFIRM)
+ assertThat(currentHaptics.flag).isNull()
}
@Test
+ @EnableFlags(Flags.FLAG_MSDL_FEEDBACK)
+ fun playSuccessHaptic_SetsUnlockMSDLFeedback() = runGenericTest {
+ val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false)
+ kosmos.promptViewModel.showAuthenticated(testCase.authenticatedModality, 1_000L)
+
+ if (expectConfirmation) {
+ kosmos.promptViewModel.confirmAuthenticated()
+ }
+
+ val haptics by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
+ val currentHaptics = haptics as PromptViewModel.HapticsToPlay.MSDL
+ assertThat(currentHaptics.token).isEqualTo(MSDLToken.UNLOCK)
+ assertThat(currentHaptics.properties).isEqualTo(authInteractionProperties)
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_MSDL_FEEDBACK)
fun playErrorHaptic_SetsRejectConstant() = runGenericTest {
kosmos.promptViewModel.showTemporaryError("test", "messageAfterError", false)
- val currentHaptics by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
- assertThat(currentHaptics?.hapticFeedbackConstant)
- .isEqualTo(HapticFeedbackConstants.BIOMETRIC_REJECT)
- assertThat(currentHaptics?.flag).isNull()
+ val haptics by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
+ val currentHaptics = haptics as PromptViewModel.HapticsToPlay.HapticConstant
+ assertThat(currentHaptics.constant).isEqualTo(HapticFeedbackConstants.BIOMETRIC_REJECT)
+ assertThat(currentHaptics.flag).isNull()
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_MSDL_FEEDBACK)
+ fun playErrorHaptic_SetsFailureMSDLFeedback() = runGenericTest {
+ kosmos.promptViewModel.showTemporaryError("test", "messageAfterError", false)
+
+ val haptics by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
+ val currentHaptics = haptics as PromptViewModel.HapticsToPlay.MSDL
+ assertThat(currentHaptics.token).isEqualTo(MSDLToken.FAILURE)
+ assertThat(currentHaptics.properties).isEqualTo(authInteractionProperties)
}
// biometricprompt_sfps_fingerprint_authenticating reused across rotations
@@ -855,6 +923,7 @@
}
@Test
+ @DisableFlags(Flags.FLAG_MSDL_FEEDBACK)
fun set_haptic_on_errors() = runGenericTest {
kosmos.promptViewModel.showTemporaryError(
"so sad",
@@ -863,13 +932,30 @@
hapticFeedback = true,
)
- val haptics by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
- assertThat(haptics?.hapticFeedbackConstant)
- .isEqualTo(HapticFeedbackConstants.BIOMETRIC_REJECT)
- assertThat(haptics?.flag).isNull()
+ val hapticsToPlay by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
+ val haptics = hapticsToPlay as PromptViewModel.HapticsToPlay.HapticConstant
+ assertThat(haptics.constant).isEqualTo(HapticFeedbackConstants.BIOMETRIC_REJECT)
+ assertThat(haptics.flag).isNull()
}
@Test
+ @EnableFlags(Flags.FLAG_MSDL_FEEDBACK)
+ fun set_msdl_haptic_on_errors() = runGenericTest {
+ kosmos.promptViewModel.showTemporaryError(
+ "so sad",
+ messageAfterError = "",
+ authenticateAfterError = false,
+ hapticFeedback = true,
+ )
+
+ val hapticsToPlay by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
+ val haptics = hapticsToPlay as PromptViewModel.HapticsToPlay.MSDL
+ assertThat(haptics.token).isEqualTo(MSDLToken.FAILURE)
+ assertThat(haptics.properties).isEqualTo(authInteractionProperties)
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_MSDL_FEEDBACK)
fun plays_haptic_on_errors_unless_skipped() = runGenericTest {
kosmos.promptViewModel.showTemporaryError(
"still sad",
@@ -878,11 +964,26 @@
hapticFeedback = false,
)
- val haptics by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
- assertThat(haptics?.hapticFeedbackConstant).isEqualTo(HapticFeedbackConstants.NO_HAPTICS)
+ val hapticsToPlay by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
+ assertThat(hapticsToPlay).isEqualTo(PromptViewModel.HapticsToPlay.None)
}
@Test
+ @EnableFlags(Flags.FLAG_MSDL_FEEDBACK)
+ fun plays_msdl_haptic_on_errors_unless_skipped() = runGenericTest {
+ kosmos.promptViewModel.showTemporaryError(
+ "still sad",
+ messageAfterError = "",
+ authenticateAfterError = false,
+ hapticFeedback = false,
+ )
+
+ val hapticsToPlay by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
+ assertThat(hapticsToPlay).isEqualTo(PromptViewModel.HapticsToPlay.None)
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_MSDL_FEEDBACK)
fun plays_haptic_on_error_after_auth_when_confirmation_needed() = runGenericTest {
val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false)
kosmos.promptViewModel.showAuthenticated(testCase.authenticatedModality, 0)
@@ -894,17 +995,39 @@
hapticFeedback = true,
)
- val haptics by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
+ val hapticsToPlay by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
+ val haptics = hapticsToPlay as PromptViewModel.HapticsToPlay.HapticConstant
if (expectConfirmation) {
- assertThat(haptics?.hapticFeedbackConstant)
- .isEqualTo(HapticFeedbackConstants.BIOMETRIC_REJECT)
- assertThat(haptics?.flag).isNull()
+ assertThat(haptics.constant).isEqualTo(HapticFeedbackConstants.BIOMETRIC_REJECT)
+ assertThat(haptics.flag).isNull()
} else {
- assertThat(haptics?.hapticFeedbackConstant)
- .isEqualTo(HapticFeedbackConstants.BIOMETRIC_CONFIRM)
+ assertThat(haptics.constant).isEqualTo(HapticFeedbackConstants.BIOMETRIC_CONFIRM)
}
}
+ @Test
+ @EnableFlags(Flags.FLAG_MSDL_FEEDBACK)
+ fun plays_msdl_haptic_on_error_after_auth_when_confirmation_needed() = runGenericTest {
+ val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false)
+ kosmos.promptViewModel.showAuthenticated(testCase.authenticatedModality, 0)
+
+ kosmos.promptViewModel.showTemporaryError(
+ "still sad",
+ messageAfterError = "",
+ authenticateAfterError = false,
+ hapticFeedback = true,
+ )
+
+ val hapticsToPlay by collectLastValue(kosmos.promptViewModel.hapticsToPlay)
+ val haptics = hapticsToPlay as PromptViewModel.HapticsToPlay.MSDL
+ if (expectConfirmation) {
+ assertThat(haptics.token).isEqualTo(MSDLToken.FAILURE)
+ } else {
+ assertThat(haptics.token).isEqualTo(MSDLToken.UNLOCK)
+ }
+ assertThat(haptics.properties).isEqualTo(authInteractionProperties)
+ }
+
private suspend fun TestScope.showTemporaryErrors(
restart: Boolean,
helpAfterError: String = "",
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java
index a1bea06..6377717 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java
@@ -104,6 +104,7 @@
when(mFalsingDataProvider.getRecentMotionEvents()).thenReturn(mMotionEventList);
when(mKeyguardStateController.isShowing()).thenReturn(true);
when(mFalsingDataProvider.isUnfolded()).thenReturn(false);
+ when(mFalsingDataProvider.isTouchScreenSource()).thenReturn(true);
mBrightLineFalsingManager = new BrightLineFalsingManager(mFalsingDataProvider,
mMetricsLogger, mClassifiers, mSingleTapClassfier, mLongTapClassifier,
mDoubleTapClassifier, mHistoryTracker, mKeyguardStateController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt
index 7cc9185..bfb8a57 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt
@@ -34,6 +34,7 @@
import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryBackgroundViewModel
import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryForegroundViewModel
import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryIconViewModel
+import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.res.R
import com.android.systemui.shade.NotificationPanelView
@@ -86,6 +87,7 @@
{ mock(DeviceEntryBackgroundViewModel::class.java) },
{ falsingManager },
{ mock(VibratorHelper::class.java) },
+ logcatLogBuffer(),
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HearingDevicesTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HearingDevicesTileTest.java
index 76c8cf0..7d41a20 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HearingDevicesTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/HearingDevicesTileTest.java
@@ -16,6 +16,8 @@
package com.android.systemui.qs.tiles;
+import static com.android.systemui.accessibility.hearingaid.HearingDevicesUiEventLogger.LAUNCH_SOURCE_QS_TILE;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
@@ -151,7 +153,7 @@
mTile.handleClick(expandable);
mTestableLooper.processAllMessages();
- verify(mHearingDevicesDialogManager).showDialog(expandable);
+ verify(mHearingDevicesDialogManager).showDialog(expandable, LAUNCH_SOURCE_QS_TILE);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt
index 73548ba..ca518f9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/RecordIssueTileTest.kt
@@ -35,6 +35,7 @@
import com.android.systemui.recordissue.RecordIssueDialogDelegate
import com.android.systemui.recordissue.TraceurMessageSender
import com.android.systemui.res.R
+import com.android.systemui.screenrecord.RecordingController
import com.android.systemui.settings.UserContextProvider
import com.android.systemui.statusbar.phone.KeyguardDismissUtil
import com.android.systemui.statusbar.phone.SystemUIDialog
@@ -65,6 +66,7 @@
@Mock private lateinit var qsEventLogger: QsEventLogger
@Mock private lateinit var metricsLogger: MetricsLogger
@Mock private lateinit var statusBarStateController: StatusBarStateController
+ @Mock private lateinit var recordingController: RecordingController
@Mock private lateinit var activityStarter: ActivityStarter
@Mock private lateinit var qsLogger: QSLogger
@Mock private lateinit var keyguardDismissUtil: KeyguardDismissUtil
@@ -109,6 +111,7 @@
Executors.newSingleThreadExecutor(),
issueRecordingState,
delegateFactory,
+ recordingController,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index 3e3c046..01a3d36 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -779,6 +779,26 @@
@Test
@DisableSceneContainer
+ public void testResetDoesNotHideBouncerWhenNotShowing() {
+ reset(mDismissCallbackRegistry);
+ reset(mPrimaryBouncerInteractor);
+
+ // GIVEN the keyguard is showing
+ reset(mAlternateBouncerInteractor);
+ when(mKeyguardStateController.isShowing()).thenReturn(true);
+ when(mPrimaryBouncerInteractor.isFullyShowing()).thenReturn(false);
+
+ // WHEN SBKV is reset with hideBouncerWhenShowing=true
+ mStatusBarKeyguardViewManager.reset(true);
+
+ // THEN no calls to hide should be made
+ verify(mAlternateBouncerInteractor, never()).hide();
+ verify(mDismissCallbackRegistry, never()).notifyDismissCancelled();
+ verify(mPrimaryBouncerInteractor, never()).setDismissAction(eq(null), eq(null));
+ }
+
+ @Test
+ @DisableSceneContainer
public void testResetHideBouncerWhenShowing_alternateBouncerHides() {
reset(mDismissCallbackRegistry);
reset(mPrimaryBouncerInteractor);
@@ -786,6 +806,7 @@
// GIVEN the keyguard is showing
reset(mAlternateBouncerInteractor);
when(mKeyguardStateController.isShowing()).thenReturn(true);
+ when(mPrimaryBouncerInteractor.isFullyShowing()).thenReturn(true);
// WHEN SBKV is reset with hideBouncerWhenShowing=true
mStatusBarKeyguardViewManager.reset(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
index fe408e3..7634490 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionRepositoryTest.kt
@@ -817,7 +817,7 @@
captor.lastValue.onReceive(context, intent)
// spnIntent() sets all values to true and test strings
- assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$SPN"))
+ assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$DATA_SPN"))
job.cancel()
}
@@ -852,7 +852,7 @@
verify(context).registerReceiver(captor.capture(), any())
captor.lastValue.onReceive(context, intent)
- assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$SPN"))
+ assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$DATA_SPN"))
// WHEN an intent with a different subId is sent
val wrongSubIntent = spnIntent(subId = 101)
@@ -860,7 +860,7 @@
captor.lastValue.onReceive(context, wrongSubIntent)
// THEN the previous intent's name is still used
- assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$SPN"))
+ assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$DATA_SPN"))
job.cancel()
}
@@ -902,7 +902,7 @@
verify(context).registerReceiver(captor.capture(), any())
captor.lastValue.onReceive(context, intent)
- assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$SPN"))
+ assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$DATA_SPN"))
val intentWithoutInfo =
spnIntent(
@@ -961,7 +961,7 @@
// The value is still there despite no active subscribers
assertThat(underTest.networkName.value)
- .isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$SPN"))
+ .isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$DATA_SPN"))
}
@Test
@@ -986,7 +986,7 @@
@Test
@EnableFlags(Flags.FLAG_STATUS_BAR_SWITCH_TO_SPN_FROM_DATA_SPN)
- fun networkName_allFieldsSet_doesNotUseDataSpn() =
+ fun networkName_allFieldsSet_prioritizesDataSpnOverSpn() =
testScope.runTest {
val latest by collectLastValue(underTest.networkName)
val captor = argumentCaptor<BroadcastReceiver>()
@@ -1002,6 +1002,27 @@
plmn = PLMN,
)
captor.lastValue.onReceive(context, intent)
+ assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$DATA_SPN"))
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_STATUS_BAR_SWITCH_TO_SPN_FROM_DATA_SPN)
+ fun networkName_spnAndPlmn_fallbackToSpnWhenNullDataSpn() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.networkName)
+ val captor = argumentCaptor<BroadcastReceiver>()
+ verify(context).registerReceiver(captor.capture(), any())
+
+ val intent =
+ spnIntent(
+ subId = SUB_1_ID,
+ showSpn = true,
+ spn = SPN,
+ dataSpn = null,
+ showPlmn = true,
+ plmn = PLMN,
+ )
+ captor.lastValue.onReceive(context, intent)
assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$SPN"))
}
@@ -1043,7 +1064,27 @@
plmn = PLMN,
)
captor.lastValue.onReceive(context, intent)
- assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN"))
+ assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$DATA_SPN"))
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_STATUS_BAR_SWITCH_TO_SPN_FROM_DATA_SPN)
+ fun networkName_showPlmn_plmnNotNull_showSpn_spnNotNull_dataSpnNull() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.networkName)
+ val captor = argumentCaptor<BroadcastReceiver>()
+ verify(context).registerReceiver(captor.capture(), any())
+ val intent =
+ spnIntent(
+ subId = SUB_1_ID,
+ showSpn = true,
+ spn = SPN,
+ dataSpn = null,
+ showPlmn = true,
+ plmn = PLMN,
+ )
+ captor.lastValue.onReceive(context, intent)
+ assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$PLMN$SEP$SPN"))
}
@Test
@@ -1102,10 +1143,50 @@
plmn = null,
)
captor.lastValue.onReceive(context, intent)
+ assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$DATA_SPN"))
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_STATUS_BAR_SWITCH_TO_SPN_FROM_DATA_SPN)
+ fun networkName_showPlmn_plmnNull_showSpn_dataSpnNull() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.networkName)
+ val captor = argumentCaptor<BroadcastReceiver>()
+ verify(context).registerReceiver(captor.capture(), any())
+ val intent =
+ spnIntent(
+ subId = SUB_1_ID,
+ showSpn = true,
+ spn = SPN,
+ dataSpn = null,
+ showPlmn = true,
+ plmn = null,
+ )
+ captor.lastValue.onReceive(context, intent)
assertThat(latest).isEqualTo(NetworkNameModel.IntentDerived("$SPN"))
}
@Test
+ @EnableFlags(Flags.FLAG_STATUS_BAR_SWITCH_TO_SPN_FROM_DATA_SPN)
+ fun networkName_showPlmn_plmnNull_showSpn_bothSpnNull() =
+ testScope.runTest {
+ val latest by collectLastValue(underTest.networkName)
+ val captor = argumentCaptor<BroadcastReceiver>()
+ verify(context).registerReceiver(captor.capture(), any())
+ val intent =
+ spnIntent(
+ subId = SUB_1_ID,
+ showSpn = true,
+ spn = null,
+ dataSpn = null,
+ showPlmn = true,
+ plmn = null,
+ )
+ captor.lastValue.onReceive(context, intent)
+ assertThat(latest).isEqualTo(DEFAULT_NAME_MODEL)
+ }
+
+ @Test
@DisableFlags(Flags.FLAG_STATUS_BAR_SWITCH_TO_SPN_FROM_DATA_SPN)
fun networkName_showPlmn_plmnNull_showSpn_flagOff() =
testScope.runTest {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestableContext.java b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestableContext.java
index 0358474..3041240 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestableContext.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestableContext.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.PackageManager;
import android.hardware.display.DisplayManager;
import android.os.Handler;
import android.os.UserHandle;
@@ -40,6 +41,7 @@
@GuardedBy("mRegisteredReceivers")
private final Set<BroadcastReceiver> mRegisteredReceivers = new ArraySet<>();
private final Map<UserHandle, Context> mContextForUser = new HashMap<>();
+ private final Map<String, Context> mContextForPackage = new HashMap<>();
public SysuiTestableContext(Context base) {
super(base);
@@ -175,4 +177,22 @@
}
return super.createContextAsUser(user, flags);
}
+
+ /**
+ * Sets a Context object that will be returned as the result of {@link #createPackageContext}
+ * for a specific {@code packageName}.
+ */
+ public void prepareCreatePackageContext(String packageName, Context context) {
+ mContextForPackage.put(packageName, context);
+ }
+
+ @Override
+ public Context createPackageContext(String packageName, int flags)
+ throws PackageManager.NameNotFoundException {
+ Context packageContext = mContextForPackage.get(packageName);
+ if (packageContext != null) {
+ return packageContext;
+ }
+ return super.createPackageContext(packageName, flags);
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorKosmos.kt
index 5ced578..3087d01 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/bouncer/domain/interactor/BouncerActionButtonInteractorKosmos.kt
@@ -26,6 +26,7 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.statusbar.pipeline.mobile.data.repository.mobileConnectionsRepository
import com.android.systemui.telephony.domain.interactor.telephonyInteractor
import com.android.systemui.user.domain.interactor.selectedUserInteractor
@@ -50,5 +51,6 @@
},
metricsLogger = metricsLogger,
dozeLogger = mock(),
+ sceneInteractor = { sceneInteractor },
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/msdl/MSDLPlayerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/msdl/MSDLPlayerKosmos.kt
index f5a05b4..4f5c32a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/msdl/MSDLPlayerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/haptics/msdl/MSDLPlayerKosmos.kt
@@ -17,5 +17,7 @@
package com.android.systemui.haptics.msdl
import com.android.systemui.kosmos.Kosmos
+import com.google.android.msdl.domain.MSDLPlayer
-val Kosmos.msdlPlayer by Kosmos.Fixture { FakeMSDLPlayer() }
+var Kosmos.msdlPlayer: MSDLPlayer by Kosmos.Fixture { fakeMSDLPlayer }
+val Kosmos.fakeMSDLPlayer by Kosmos.Fixture { FakeMSDLPlayer() }
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinderKosmos.kt
index 1e95fc1..740d891 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinderKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/binder/AlternateBouncerViewBinderKosmos.kt
@@ -34,6 +34,7 @@
import com.android.systemui.keyguard.ui.viewmodel.alternateBouncerWindowViewModel
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.power.domain.interactor.powerInteractor
import com.android.systemui.statusbar.gesture.TapGestureDetector
import com.android.systemui.util.mockito.mock
@@ -64,6 +65,7 @@
},
messageAreaViewModel = mock<AlternateBouncerMessageAreaViewModel>(),
powerInteractor = powerInteractor,
+ touchLogBuffer = logcatLogBuffer(),
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
index 851a378..457bd28 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt
@@ -36,7 +36,7 @@
import com.android.systemui.deviceentry.domain.interactor.deviceEntryUdfpsInteractor
import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
import com.android.systemui.globalactions.domain.interactor.globalActionsInteractor
-import com.android.systemui.haptics.msdl.msdlPlayer
+import com.android.systemui.haptics.msdl.fakeMSDLPlayer
import com.android.systemui.haptics.qs.qsLongPressEffect
import com.android.systemui.jank.interactionJankMonitor
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
@@ -155,5 +155,5 @@
val scrimController by lazy { kosmos.scrimController }
val scrimStartable by lazy { kosmos.scrimStartable }
val sceneContainerOcclusionInteractor by lazy { kosmos.sceneContainerOcclusionInteractor }
- val msdlPlayer by lazy { kosmos.msdlPlayer }
+ val msdlPlayer by lazy { kosmos.fakeMSDLPlayer }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt
index b612a8b..9a5698c 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt
@@ -27,6 +27,7 @@
import com.android.systemui.deviceentry.domain.interactor.deviceEntryHapticsInteractor
import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor
+import com.android.systemui.haptics.msdl.msdlPlayer
import com.android.systemui.haptics.vibratorHelper
import com.android.systemui.keyguard.dismissCallbackRegistry
import com.android.systemui.keyguard.domain.interactor.keyguardEnabledInteractor
@@ -86,5 +87,6 @@
statusBarStateController = sysuiStatusBarStateController,
alternateBouncerInteractor = alternateBouncerInteractor,
vibratorHelper = vibratorHelper,
+ msdlPlayer = msdlPlayer,
)
}
diff --git a/ravenwood/Android.bp b/ravenwood/Android.bp
index 9b0c8e5..333fe4c 100644
--- a/ravenwood/Android.bp
+++ b/ravenwood/Android.bp
@@ -127,6 +127,7 @@
libs: [
"framework-minus-apex.ravenwood",
"ravenwood-junit",
+ "ravenwood-helper-libcore-runtime",
],
visibility: ["//visibility:private"],
}
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java
index f237ba9..1d182da 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodAwareTestRunnerHook.java
@@ -27,6 +27,9 @@
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.internal.os.RuntimeInit;
+import com.android.ravenwood.common.RavenwoodCommonUtils;
+
import org.junit.runner.Description;
import org.junit.runner.Runner;
import org.junit.runners.model.TestClass;
@@ -35,7 +38,7 @@
* Provide hook points created by {@link RavenwoodAwareTestRunner}.
*/
public class RavenwoodAwareTestRunnerHook {
- private static final String TAG = "RavenwoodAwareTestRunnerHook";
+ private static final String TAG = RavenwoodAwareTestRunner.TAG;
private RavenwoodAwareTestRunnerHook() {
}
@@ -56,20 +59,36 @@
* Called when a runner starts, before the inner runner gets a chance to run.
*/
public static void onRunnerInitializing(Runner runner, TestClass testClass) {
+ // TODO: Move the initialization code to a better place.
+
+ initOnce();
+
// This log call also ensures the framework JNI is loaded.
Log.i(TAG, "onRunnerInitializing: testClass=" + testClass.getJavaClass()
+ " runner=" + runner);
- // TODO: Move the initialization code to a better place.
+ // This is needed to make AndroidJUnit4ClassRunner happy.
+ InstrumentationRegistry.registerInstance(null, Bundle.EMPTY);
+ }
+
+ private static boolean sInitialized = false;
+
+ private static void initOnce() {
+ if (sInitialized) {
+ return;
+ }
+ sInitialized = true;
+
+ // We haven't initialized liblog yet, so directly write to System.out here.
+ RavenwoodCommonUtils.log(TAG, "initOnce()");
+
+ // Redirect stdout/stdin to liblog.
+ RuntimeInit.redirectLogStreams();
// This will let AndroidJUnit4 use the original runner.
System.setProperty("android.junit.runner",
"androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner");
System.setProperty(RAVENWOOD_VERSION_JAVA_SYSPROP, "1");
-
-
- // This is needed to make AndroidJUnit4ClassRunner happy.
- InstrumentationRegistry.registerInstance(null, Bundle.EMPTY);
}
/**
@@ -87,7 +106,7 @@
*/
public static boolean onBefore(RavenwoodAwareTestRunner runner, Description description,
Scope scope, Order order) {
- Log.i(TAG, "onBefore: description=" + description + ", " + scope + ", " + order);
+ Log.v(TAG, "onBefore: description=" + description + ", " + scope + ", " + order);
if (scope == Scope.Class && order == Order.First) {
// Keep track of the current class.
@@ -113,7 +132,7 @@
*/
public static boolean onAfter(RavenwoodAwareTestRunner runner, Description description,
Scope scope, Order order, Throwable th) {
- Log.i(TAG, "onAfter: description=" + description + ", " + scope + ", " + order + ", " + th);
+ Log.v(TAG, "onAfter: description=" + description + ", " + scope + ", " + order + ", " + th);
if (scope == Scope.Instance && order == Order.First) {
getStats().onTestFinished(sCurrentClassDescription, description,
diff --git a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
index a2088fd..0059360 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
@@ -36,7 +36,6 @@
import androidx.test.platform.app.InstrumentationRegistry;
-import com.android.internal.os.RuntimeInit;
import com.android.server.LocalServices;
import org.junit.runner.Description;
@@ -92,8 +91,6 @@
Thread.setDefaultUncaughtExceptionHandler(sUncaughtExceptionHandler);
}
- RuntimeInit.redirectLogStreams();
-
android.os.Process.init$ravenwood(rule.mUid, rule.mPid);
android.os.Binder.init$ravenwood();
setSystemProperties(rule.mSystemProperties);
diff --git a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
index 7d99166..bfde9cb 100644
--- a/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
+++ b/ravenwood/junit-src/android/platform/test/ravenwood/RavenwoodAwareTestRunner.java
@@ -23,7 +23,6 @@
import android.util.Log;
-import com.android.ravenwood.common.RavenwoodCommonUtils;
import com.android.ravenwood.common.SneakyThrow;
import org.junit.Assume;
@@ -75,7 +74,7 @@
* (no hooks, etc.)
*/
public class RavenwoodAwareTestRunner extends Runner implements Filterable, Orderable {
- private static final String TAG = "RavenwoodAwareTestRunner";
+ public static final String TAG = "Ravenwood";
@Inherited
@Target({TYPE})
@@ -142,16 +141,9 @@
private Description mDescription = null;
private Throwable mExceptionInConstructor = null;
- /** Simple logging method. */
- private void log(String message) {
- RavenwoodCommonUtils.log(TAG, "[" + getTestClass().getJavaClass() + " @" + this + "] "
- + message);
- }
-
- private Error logAndFail(String message, Throwable innerException) {
- log(message);
- log(" Exception=" + innerException);
- throw new AssertionError(message, innerException);
+ private Error logAndFail(String message, Throwable exception) {
+ Log.e(TAG, message, exception);
+ throw new AssertionError(message, exception);
}
public TestClass getTestClass() {
@@ -165,6 +157,8 @@
try {
mTestClass = new TestClass(testClass);
+ onRunnerInitializing();
+
/*
* If the class has @DisabledOnRavenwood, then we'll delegate to
* ClassSkippingTestRunner, which simply skips it.
@@ -186,10 +180,8 @@
realRunnerClass = BlockJUnit4ClassRunner.class;
}
- onRunnerInitializing();
-
try {
- log("Initializing the inner runner: " + realRunnerClass);
+ Log.i(TAG, "Initializing the inner runner: " + realRunnerClass);
mRealRunner = instantiateRealRunner(realRunnerClass, testClass);
mDescription = mRealRunner.getDescription();
@@ -201,8 +193,7 @@
} catch (Throwable th) {
// If we throw in the constructor, Tradefed may not report it and just ignore the class,
// so record it and throw it when the test actually started.
- log("Fatal: Exception detected in constructor: " + th.getMessage() + "\n"
- + Log.getStackTraceString(th));
+ Log.e(TAG, "Fatal: Exception detected in constructor", th);
mExceptionInConstructor = new RuntimeException("Exception detected in constructor",
th);
mDescription = Description.createTestDescription(testClass, "Constructor");
@@ -236,8 +227,7 @@
if (!isOnRavenwood()) {
return;
}
-
- log("onRunnerInitializing");
+ // DO NOT USE android.util.Log before calling onRunnerInitializing().
RavenwoodAwareTestRunnerHook.onRunnerInitializing(this, mTestClass);
@@ -250,7 +240,7 @@
if (!isOnRavenwood()) {
return;
}
- log("runAnnotatedMethodsOnRavenwood() " + annotationClass.getName());
+ Log.v(TAG, "runAnnotatedMethodsOnRavenwood() " + annotationClass.getName());
for (var method : getTestClass().getAnnotatedMethods(annotationClass)) {
ensureIsPublicVoidMethod(method.getMethod(), /* isStatic=*/ instance == null);
diff --git a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/runtimehelper/ClassLoadHook.java b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/runtimehelper/ClassLoadHook.java
index 0f955e7..c519204 100644
--- a/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/runtimehelper/ClassLoadHook.java
+++ b/ravenwood/runtime-helper-src/framework/com/android/platform/test/ravenwood/runtimehelper/ClassLoadHook.java
@@ -15,6 +15,9 @@
*/
package com.android.platform.test.ravenwood.runtimehelper;
+import android.system.ErrnoException;
+import android.system.Os;
+
import com.android.ravenwood.common.RavenwoodCommonUtils;
import java.io.File;
@@ -37,6 +40,14 @@
private static final boolean SKIP_LOADING_LIBANDROID = "1".equals(System.getenv(
"RAVENWOOD_SKIP_LOADING_LIBANDROID"));
+ /**
+ * If set to 1, and if $ANDROID_LOG_TAGS isn't set, we enable the verbose logging.
+ *
+ * (See also InitLogging() in http://ac/system/libbase/logging.cpp)
+ */
+ private static final boolean RAVENWOOD_VERBOSE_LOGGING = "1".equals(System.getenv(
+ "RAVENWOOD_VERBOSE"));
+
public static final String CORE_NATIVE_CLASSES = "core_native_classes";
public static final String ICU_DATA_PATH = "icu.data.path";
public static final String KEYBOARD_PATHS = "keyboard_paths";
@@ -123,6 +134,15 @@
return;
}
+ if (RAVENWOOD_VERBOSE_LOGGING) {
+ log("Force enabling verbose logging");
+ try {
+ Os.setenv("ANDROID_LOG_TAGS", "*:v", true);
+ } catch (ErrnoException e) {
+ // Shouldn't happen.
+ }
+ }
+
// Make sure these properties are not set.
ensurePropertyNotSet(CORE_NATIVE_CLASSES);
ensurePropertyNotSet(ICU_DATA_PATH);
diff --git a/ravenwood/runtime-helper-src/libcore-fake/android/system/Os.java b/ravenwood/runtime-helper-src/libcore-fake/android/system/Os.java
index a5c0b54..c94ef31 100644
--- a/ravenwood/runtime-helper-src/libcore-fake/android/system/Os.java
+++ b/ravenwood/runtime-helper-src/libcore-fake/android/system/Os.java
@@ -93,4 +93,8 @@
throw new ErrnoException("pread", OsConstants.EIO, e);
}
}
+
+ public static void setenv(String name, String value, boolean overwrite) throws ErrnoException {
+ RavenwoodRuntimeNative.setenv(name, value, overwrite);
+ }
}
diff --git a/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodRuntimeNative.java b/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodRuntimeNative.java
index 0d8408c..ad80d92 100644
--- a/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodRuntimeNative.java
+++ b/ravenwood/runtime-helper-src/libcore-fake/com/android/ravenwood/RavenwoodRuntimeNative.java
@@ -53,6 +53,9 @@
private static native int nOpen(String path, int flags, int mode) throws ErrnoException;
+ public static native void setenv(String name, String value, boolean overwrite)
+ throws ErrnoException;
+
public static long lseek(FileDescriptor fd, long offset, int whence) throws ErrnoException {
return nLseek(JvmWorkaround.getInstance().getFdInt(fd), offset, whence);
}
diff --git a/ravenwood/runtime-jni/ravenwood_runtime.cpp b/ravenwood/runtime-jni/ravenwood_runtime.cpp
index f5cb019f..c255be5 100644
--- a/ravenwood/runtime-jni/ravenwood_runtime.cpp
+++ b/ravenwood/runtime-jni/ravenwood_runtime.cpp
@@ -214,6 +214,19 @@
return throwIfMinusOne(env, "open", TEMP_FAILURE_RETRY(open(path.c_str(), flags, mode)));
}
+static void Linux_setenv(JNIEnv* env, jobject, jstring javaName, jstring javaValue,
+ jboolean overwrite) {
+ ScopedRealUtf8Chars name(env, javaName);
+ if (name.c_str() == NULL) {
+ jniThrowNullPointerException(env);
+ }
+ ScopedRealUtf8Chars value(env, javaValue);
+ if (value.c_str() == NULL) {
+ jniThrowNullPointerException(env);
+ }
+ throwIfMinusOne(env, "setenv", setenv(name.c_str(), value.c_str(), overwrite ? 1 : 0));
+}
+
// ---- Registration ----
static const JNINativeMethod sMethods[] =
@@ -227,6 +240,7 @@
{ "lstat", "(Ljava/lang/String;)Landroid/system/StructStat;", (void*)Linux_lstat },
{ "stat", "(Ljava/lang/String;)Landroid/system/StructStat;", (void*)Linux_stat },
{ "nOpen", "(Ljava/lang/String;II)I", (void*)Linux_open },
+ { "setenv", "(Ljava/lang/String;Ljava/lang/String;Z)V", (void*)Linux_setenv },
};
extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig
index ffa4841..ee3bbca 100644
--- a/services/accessibility/accessibility.aconfig
+++ b/services/accessibility/accessibility.aconfig
@@ -216,6 +216,16 @@
}
flag {
+ name: "reset_input_dispatcher_before_first_touch_exploration"
+ namespace: "accessibility"
+ description: "Resets InputDispatcher state by sending ACTION_CANCEL before the first TouchExploration hover events"
+ bug: "364408887"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "scan_packages_without_lock"
namespace: "accessibility"
description: "Scans packages for accessibility service/activity info without holding the A11yMS lock"
@@ -228,6 +238,7 @@
description: "Sends accessibility events in TouchExplorer#onAccessibilityEvent based on internal state to keep it consistent. This reduces test flakiness."
bug: "295575684"
}
+
flag {
name: "send_hover_events_based_on_event_stream"
namespace: "accessibility"
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
index 04b42e4..0ed239e 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java
@@ -1613,6 +1613,19 @@
dispatchGesture(gestureEvent);
}
if (!mEvents.isEmpty() && !mRawEvents.isEmpty()) {
+ if (Flags.resetInputDispatcherBeforeFirstTouchExploration()
+ && !mState.hasResetInputDispatcherState()) {
+ // Cancel any possible ongoing touch gesture from before touch exploration
+ // started. This clears out the InputDispatcher event stream state so that it
+ // is ready to accept new injected HOVER events.
+ mDispatcher.sendMotionEvent(
+ mEvents.get(0),
+ ACTION_CANCEL,
+ mRawEvents.get(0),
+ mPointerIdBits,
+ mPolicyFlags);
+ setHasResetInputDispatcherState(true);
+ }
// Deliver a down event.
mDispatcher.sendMotionEvent(
mEvents.get(0),
@@ -1773,4 +1786,9 @@
+ ", mDraggingPointerId: " + mDraggingPointerId
+ " }";
}
+
+ @VisibleForTesting
+ void setHasResetInputDispatcherState(boolean value) {
+ mState.setHasResetInputDispatcherState(value);
+ }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java
index e13994e..f15b8ee 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchState.java
@@ -86,6 +86,7 @@
private MotionEvent mLastInjectedHoverEvent;
// The last injected hover event used for performing clicks.
private MotionEvent mLastInjectedHoverEventForClick;
+ private boolean mHasResetInputDispatcherState;
// The time of the last injected down.
private long mLastInjectedDownEventTime;
// Keep track of which pointers sent to the system are down.
@@ -361,6 +362,14 @@
return mLastInjectedDownEventTime;
}
+ boolean hasResetInputDispatcherState() {
+ return mHasResetInputDispatcherState;
+ }
+
+ void setHasResetInputDispatcherState(boolean value) {
+ mHasResetInputDispatcherState = value;
+ }
+
public int getLastTouchedWindowId() {
return mLastTouchedWindowId;
}
diff --git a/services/appfunctions/java/com/android/server/appfunctions/SyncAppSearchCallHelper.java b/services/appfunctions/java/com/android/server/appfunctions/FutureAppSearchSession.java
similarity index 61%
rename from services/appfunctions/java/com/android/server/appfunctions/SyncAppSearchCallHelper.java
rename to services/appfunctions/java/com/android/server/appfunctions/FutureAppSearchSession.java
index e56f81f0..eba628d 100644
--- a/services/appfunctions/java/com/android/server/appfunctions/SyncAppSearchCallHelper.java
+++ b/services/appfunctions/java/com/android/server/appfunctions/FutureAppSearchSession.java
@@ -20,11 +20,16 @@
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
+import android.app.appsearch.AppSearchBatchResult;
import android.app.appsearch.AppSearchManager;
import android.app.appsearch.AppSearchManager.SearchContext;
import android.app.appsearch.AppSearchResult;
import android.app.appsearch.AppSearchSession;
import android.app.appsearch.GetSchemaResponse;
+import android.app.appsearch.PutDocumentsRequest;
+import android.app.appsearch.SearchResult;
+import android.app.appsearch.SearchResults;
+import android.app.appsearch.SearchSpec;
import android.app.appsearch.SetSchemaRequest;
import android.app.appsearch.SetSchemaResponse;
import android.util.Slog;
@@ -33,22 +38,20 @@
import java.io.Closeable;
import java.io.IOException;
+import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executor;
/**
- * Helper class for interacting with a system server local appsearch session asynchronously.
- *
- * <p>Converts the AppSearch Callback API to {@link AndroidFuture}.
+ * A future API wrapper of {@link AppSearchSession} APIs.
*/
@FlaggedApi(FLAG_ENABLE_APP_FUNCTION_MANAGER)
-public class SyncAppSearchCallHelper implements Closeable {
- private static final String TAG = SyncAppSearchCallHelper.class.getSimpleName();
+public class FutureAppSearchSession implements Closeable {
+ private static final String TAG = FutureAppSearchSession.class.getSimpleName();
private final Executor mExecutor;
- private final AppSearchManager mAppSearchManager;
private final AndroidFuture<AppSearchResult<AppSearchSession>> mSettableSessionFuture;
- public SyncAppSearchCallHelper(
+ public FutureAppSearchSession(
@NonNull AppSearchManager appSearchManager,
@NonNull Executor executor,
@NonNull SearchContext appSearchContext) {
@@ -57,22 +60,21 @@
Objects.requireNonNull(appSearchContext);
mExecutor = executor;
- mAppSearchManager = appSearchManager;
mSettableSessionFuture = new AndroidFuture<>();
- mAppSearchManager.createSearchSession(
+ appSearchManager.createSearchSession(
appSearchContext, mExecutor, mSettableSessionFuture::complete);
}
/** Converts a failed app search result codes into an exception. */
@NonNull
- private static Exception failedResultToException(@NonNull AppSearchResult appSearchResult) {
+ private static Exception failedResultToException(@NonNull AppSearchResult<?> appSearchResult) {
return switch (appSearchResult.getResultCode()) {
- case AppSearchResult.RESULT_INVALID_ARGUMENT ->
- new IllegalArgumentException(appSearchResult.getErrorMessage());
- case AppSearchResult.RESULT_IO_ERROR ->
- new IOException(appSearchResult.getErrorMessage());
- case AppSearchResult.RESULT_SECURITY_ERROR ->
- new SecurityException(appSearchResult.getErrorMessage());
+ case AppSearchResult.RESULT_INVALID_ARGUMENT -> new IllegalArgumentException(
+ appSearchResult.getErrorMessage());
+ case AppSearchResult.RESULT_IO_ERROR -> new IOException(
+ appSearchResult.getErrorMessage());
+ case AppSearchResult.RESULT_SECURITY_ERROR -> new SecurityException(
+ appSearchResult.getErrorMessage());
default -> new IllegalStateException(appSearchResult.getErrorMessage());
};
}
@@ -91,7 +93,7 @@
/** Gets the schema for a given app search session. */
public AndroidFuture<GetSchemaResponse> getSchema() {
return getSessionAsync()
- .thenComposeAsync(
+ .thenCompose(
session -> {
AndroidFuture<AppSearchResult<GetSchemaResponse>>
settableSchemaResponse = new AndroidFuture<>();
@@ -105,14 +107,13 @@
failedResultToException(result));
}
});
- },
- mExecutor);
+ });
}
/** Sets the schema for a given app search session. */
public AndroidFuture<SetSchemaResponse> setSchema(@NonNull SetSchemaRequest setSchemaRequest) {
return getSessionAsync()
- .thenComposeAsync(
+ .thenCompose(
session -> {
AndroidFuture<AppSearchResult<SetSchemaResponse>>
settableSchemaResponse = new AndroidFuture<>();
@@ -130,8 +131,32 @@
failedResultToException(result));
}
});
- },
- mExecutor);
+ });
+ }
+
+ /** Indexes documents into the AppSearchSession database. */
+ public AndroidFuture<AppSearchBatchResult<String, Void>> put(
+ @NonNull PutDocumentsRequest putDocumentsRequest) {
+ return getSessionAsync().thenCompose(
+ session -> {
+ AndroidFuture<AppSearchBatchResult<String, Void>> batchResultFuture =
+ new AndroidFuture<>();
+
+ session.put(putDocumentsRequest, mExecutor, batchResultFuture::complete);
+ return batchResultFuture;
+ });
+ }
+
+ /**
+ * Retrieves documents from the open AppSearchSession that match a given query string and type
+ * of search provided.
+ */
+ public AndroidFuture<FutureSearchResults> search(
+ @NonNull String queryExpression,
+ @NonNull SearchSpec searchSpec) {
+ return getSessionAsync().thenApply(
+ session -> session.search(queryExpression, searchSpec))
+ .thenApply(result -> new FutureSearchResults(result, mExecutor));
}
@Override
@@ -142,4 +167,32 @@
Slog.e(TAG, "Failed to close app search session", ex);
}
}
+
+ /** A future API wrapper of {@link android.app.appsearch.SearchResults}. */
+ public static class FutureSearchResults {
+ private final SearchResults mSearchResults;
+ private final Executor mExecutor;
+
+ public FutureSearchResults(@NonNull SearchResults searchResults,
+ @NonNull Executor executor) {
+ mSearchResults = Objects.requireNonNull(searchResults);
+ mExecutor = Objects.requireNonNull(executor);
+ }
+
+ public AndroidFuture<List<SearchResult>> getNextPage() {
+ AndroidFuture<AppSearchResult<List<SearchResult>>> nextPageFuture =
+ new AndroidFuture<>();
+
+ mSearchResults.getNextPage(mExecutor, nextPageFuture::complete);
+ return nextPageFuture.thenApply(result -> {
+ if (result.isSuccess()) {
+ return result.getResultValue();
+ } else {
+ throw new RuntimeException(
+ failedResultToException(result));
+ }
+ });
+ }
+
+ }
}
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 3068398..b53bf98 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -220,6 +220,15 @@
// See {@link Provider#pendingDeletedWidgetIds}.
private static final String PENDING_DELETED_IDS_ATTR = "pending_deleted_ids";
+ // Hard limit of number of hosts an app can create, note that the app that hosts the widgets
+ // can have multiple instances of {@link AppWidgetHost}, typically in respect to different
+ // surfaces in the host app.
+ // @see AppWidgetHost
+ // @see AppWidgetHost#mHostId
+ private static final int MAX_NUMBER_OF_HOSTS_PER_PACKAGE = 20;
+ // Hard limit of number of widgets can be pinned by a host.
+ private static final int MAX_NUMBER_OF_WIDGETS_PER_HOST = 200;
+
// Handles user and package related broadcasts.
// See {@link #registerBroadcastReceiver}
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@@ -2284,7 +2293,7 @@
if (host != null) {
return host;
}
-
+ ensureHostCountBeforeAddLocked(id);
host = new Host();
host.id = id;
mHosts.add(host);
@@ -2292,6 +2301,24 @@
return host;
}
+ /**
+ * Ensures that the number of hosts for a package is less than the maximum number of hosts per
+ * package. If the number of hosts is greater than the maximum number of hosts per package, then
+ * removes the oldest host.
+ */
+ private void ensureHostCountBeforeAddLocked(@NonNull final HostId hostId) {
+ final List<Host> hosts = new ArrayList<>();
+ for (Host host : mHosts) {
+ if (host.id.uid == hostId.uid
+ && host.id.packageName.equals(hostId.packageName)) {
+ hosts.add(host);
+ }
+ }
+ while (hosts.size() >= MAX_NUMBER_OF_HOSTS_PER_PACKAGE) {
+ deleteHostLocked(hosts.remove(0));
+ }
+ }
+
private void deleteHostLocked(Host host) {
if (DEBUG) {
Slog.i(TAG, "deleteHostLocked() " + host);
@@ -2377,6 +2404,11 @@
}
@Override
+ public void onNullBinding(ComponentName name) {
+ mContext.unbindService(this);
+ }
+
+ @Override
public void onServiceDisconnected(ComponentName name) {
// Do nothing
}
@@ -2524,6 +2556,11 @@
}
@Override
+ public void onNullBinding(ComponentName name) {
+ mContext.unbindService(this);
+ }
+
+ @Override
public void onServiceDisconnected(android.content.ComponentName name) {
// Do nothing
}
@@ -3582,12 +3619,33 @@
if (DEBUG) {
Slog.i(TAG, "addWidgetLocked() " + widget);
}
+ ensureWidgetCountBeforeAddLocked(widget);
mWidgets.add(widget);
onWidgetProviderAddedOrChangedLocked(widget);
}
/**
+ * Ensures that the widget count for the widget's host is not greater than the maximum
+ * number of widgets per host. If the count is greater than the maximum, removes oldest widgets
+ * from the host until the count is less than or equal to the maximum.
+ */
+ private void ensureWidgetCountBeforeAddLocked(@NonNull final Widget widget) {
+ if (widget.host == null || widget.host.id == null) {
+ return;
+ }
+ final List<Widget> widgetsInSameHost = new ArrayList<>();
+ for (Widget w : mWidgets) {
+ if (w.host != null && widget.host.id.equals(w.host.id)) {
+ widgetsInSameHost.add(w);
+ }
+ }
+ while (widgetsInSameHost.size() >= MAX_NUMBER_OF_WIDGETS_PER_HOST) {
+ removeWidgetLocked(widgetsInSameHost.remove(0));
+ }
+ }
+
+ /**
* Checks if the provider is assigned and updates the mWidgetPackages to track packages
* that have bound widgets.
*/
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index d4f729c..3666524 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1516,9 +1516,8 @@
serviceName, FrameworkStatsLog.SERVICE_STATE_CHANGED__STATE__START);
mAm.mBatteryStatsService.noteServiceStartRunning(uid, packageName, serviceName);
final ProcessRecord hostApp = r.app;
- final boolean wasStopped = hostApp == null ? wasStopped(r) : false;
- final boolean firstLaunch =
- hostApp == null ? !mAm.wasPackageEverLaunched(r.packageName, r.userId) : false;
+ final boolean wasStopped = hostApp == null ? r.appInfo.isStopped() : false;
+ final boolean firstLaunch = hostApp == null ? r.appInfo.isNotLaunched() : false;
String error = bringUpServiceLocked(r, service.getFlags(), callerFg,
false /* whileRestarting */,
@@ -4308,9 +4307,8 @@
true, UNKNOWN_ADJ);
}
- final boolean wasStopped = hostApp == null ? wasStopped(s) : false;
- final boolean firstLaunch =
- hostApp == null ? !mAm.wasPackageEverLaunched(s.packageName, s.userId) : false;
+ final boolean wasStopped = hostApp == null ? s.appInfo.isStopped() : false;
+ final boolean firstLaunch = hostApp == null ? s.appInfo.isNotLaunched() : false;
boolean needOomAdj = false;
if (c.hasFlag(Context.BIND_AUTO_CREATE)) {
@@ -9350,8 +9348,4 @@
return mCachedDeviceProvisioningPackage != null
&& mCachedDeviceProvisioningPackage.equals(packageName);
}
-
- private boolean wasStopped(ServiceRecord serviceRecord) {
- return (serviceRecord.appInfo.flags & ApplicationInfo.FLAG_STOPPED) != 0;
- }
}
diff --git a/services/core/java/com/android/server/am/AppStartInfoTracker.java b/services/core/java/com/android/server/am/AppStartInfoTracker.java
index 1b00cec..6aadcdc 100644
--- a/services/core/java/com/android/server/am/AppStartInfoTracker.java
+++ b/services/core/java/com/android/server/am/AppStartInfoTracker.java
@@ -33,6 +33,7 @@
import android.content.pm.PackageManager;
import android.icu.text.SimpleDateFormat;
import android.os.Binder;
+import android.os.Debug;
import android.os.FileUtils;
import android.os.Handler;
import android.os.IBinder.DeathRecipient;
@@ -495,6 +496,10 @@
private void addBaseFieldsFromProcessRecord(ApplicationStartInfo start, ProcessRecord app) {
if (app == null) {
+ if (DEBUG) {
+ Slog.w(TAG,
+ "app is null in addBaseFieldsFromProcessRecord: " + Debug.getCallers(4));
+ }
return;
}
final int definingUid = app.getHostingRecord() != null
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index 8fe33d1..9e4666c 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -1005,13 +1005,10 @@
final ApplicationInfo info = ((ResolveInfo) receiver).activityInfo.applicationInfo;
final ComponentName component = ((ResolveInfo) receiver).activityInfo.getComponentName();
- if ((info.flags & ApplicationInfo.FLAG_STOPPED) != 0) {
- queue.setActiveWasStopped(true);
- }
- final int intentFlags = r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND;
- final boolean firstLaunch = !mService.wasPackageEverLaunched(info.packageName, r.userId);
- queue.setActiveFirstLaunch(firstLaunch);
+ queue.setActiveWasStopped(info.isStopped());
+ queue.setActiveFirstLaunch(info.isNotLaunched());
+ final int intentFlags = r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND;
final HostingRecord hostingRecord = new HostingRecord(HostingRecord.HOSTING_TYPE_BROADCAST,
component, r.intent.getAction(), r.getHostingRecordTriggerType());
final boolean isActivityCapable = (r.options != null
diff --git a/services/core/java/com/android/server/am/ContentProviderHelper.java b/services/core/java/com/android/server/am/ContentProviderHelper.java
index 1314521..da40826 100644
--- a/services/core/java/com/android/server/am/ContentProviderHelper.java
+++ b/services/core/java/com/android/server/am/ContentProviderHelper.java
@@ -554,13 +554,11 @@
callingProcessState, proc.mState.getCurProcState(),
false, 0L);
} else {
- final boolean stopped =
- (cpr.appInfo.flags & ApplicationInfo.FLAG_STOPPED) != 0;
+ final boolean stopped = cpr.appInfo.isStopped();
final int packageState = stopped
? PROVIDER_ACQUISITION_EVENT_REPORTED__PACKAGE_STOPPED_STATE__PACKAGE_STATE_STOPPED
: PROVIDER_ACQUISITION_EVENT_REPORTED__PACKAGE_STOPPED_STATE__PACKAGE_STATE_NORMAL;
- final boolean firstLaunch = !mService.wasPackageEverLaunched(
- cpi.packageName, userId);
+ final boolean firstLaunch = cpr.appInfo.isNotLaunched();
checkTime(startTime, "getContentProviderImpl: before start process");
proc = mService.startProcessLocked(
cpi.processName, cpr.appInfo, false, 0,
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index bb0c24b..5b4e573 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -3394,24 +3394,33 @@
hostingRecord.getDefiningUid(), hostingRecord.getDefiningProcessName());
final ProcessStateRecord state = r.mState;
- final boolean wasStopped = (info.flags & ApplicationInfo.FLAG_STOPPED) != 0;
+ final boolean wasStopped = info.isStopped();
// Check if we should mark the processrecord for first launch after force-stopping
if (wasStopped) {
+ boolean wasEverLaunched;
+ if (android.app.Flags.useAppInfoNotLaunched()) {
+ wasEverLaunched = !info.isNotLaunched();
+ } else {
+ wasEverLaunched = mService.getPackageManagerInternal()
+ .wasPackageEverLaunched(r.getApplicationInfo().packageName, r.userId);
+ }
// Check if the hosting record is for an activity or not. Since the stopped
// state tracking is handled differently to avoid WM calling back into AM,
// store the state in the correct record
if (hostingRecord.isTypeActivity()) {
- final boolean wasPackageEverLaunched = mService
- .wasPackageEverLaunched(r.getApplicationInfo().packageName, r.userId);
// If the package was launched in the past but is currently stopped, only then
// should it be considered as force-stopped.
- @WindowProcessController.StoppedState int stoppedState = wasPackageEverLaunched
+ @WindowProcessController.StoppedState int stoppedState = wasEverLaunched
? STOPPED_STATE_FORCE_STOPPED
: STOPPED_STATE_FIRST_LAUNCH;
r.getWindowProcessController().setStoppedState(stoppedState);
} else {
- r.setWasForceStopped(true);
- // first launch is computed just before logging, for non-activity types
+ if (android.app.Flags.useAppInfoNotLaunched()) {
+ // If it was launched before, then it must be a force-stop
+ r.setWasForceStopped(wasEverLaunched);
+ } else {
+ r.setWasForceStopped(true);
+ }
}
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 4e24cf3..780eda6 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -285,7 +285,6 @@
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.CancellationException;
-import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
@@ -411,6 +410,9 @@
/** The controller for the volume UI. */
private final VolumeController mVolumeController = new VolumeController();
+ /** Used only for testing to enable/disable the long press timeout volume actions. */
+ private final AtomicBoolean mVolumeControllerLongPressEnabled = new AtomicBoolean(true);
+
// sendMsg() flags
/** If the msg is already queued, replace it with this one. */
private static final int SENDMSG_REPLACE = 0;
@@ -12554,6 +12556,15 @@
if (DEBUG_VOL) Log.d(TAG, "Volume controller visible: " + visible);
}
+ /** @see AudioManager#setVolumeControllerLongPressTimeoutEnabled(boolean) */
+ @Override
+ @android.annotation.EnforcePermission(MODIFY_AUDIO_SETTINGS_PRIVILEGED)
+ public void setVolumeControllerLongPressTimeoutEnabled(boolean enable) {
+ super.setVolumeControllerLongPressTimeoutEnabled_enforcePermission();
+ mVolumeControllerLongPressEnabled.set(enable);
+ Log.i(TAG, "Volume controller long press timeout enabled: " + enable);
+ }
+
@Override
public void setVolumePolicy(VolumePolicy policy) {
enforceVolumeController("set volume policy");
@@ -12632,7 +12643,9 @@
if ((flags & AudioManager.FLAG_SHOW_UI) != 0 && !mVisible) {
// UI is not visible yet, adjustment is ignored
if (mNextLongPress < now) {
- mNextLongPress = now + mLongPressTimeout;
+ mNextLongPress =
+ now + (mVolumeControllerLongPressEnabled.get() ? mLongPressTimeout
+ : 0);
}
suppress = true;
} else if (mNextLongPress > 0) { // in a long-press
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 907e7c6..86015ac 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -938,7 +938,7 @@
setAmbientLux(mFastAmbientLux);
if (mLoggingEnabled) {
Slog.d(TAG, "updateAmbientLux: "
- + ((mFastAmbientLux > mAmbientLux) ? "Brightened" : "Darkened") + ": "
+ + ((mFastAmbientLux > mPreThresholdLux) ? "Brightened" : "Darkened") + ": "
+ "mAmbientBrighteningThreshold=" + mAmbientBrighteningThreshold + ", "
+ "mAmbientDarkeningThreshold=" + mAmbientDarkeningThreshold + ", "
+ "mAmbientLightRingBuffer=" + mAmbientLightRingBuffer + ", "
diff --git a/services/core/java/com/android/server/display/DisplayBrightnessState.java b/services/core/java/com/android/server/display/DisplayBrightnessState.java
index 334dda0..01bbd2f 100644
--- a/services/core/java/com/android/server/display/DisplayBrightnessState.java
+++ b/services/core/java/com/android/server/display/DisplayBrightnessState.java
@@ -17,6 +17,7 @@
package com.android.server.display;
import android.hardware.display.BrightnessInfo;
+import android.os.PowerManager;
import android.text.TextUtils;
import com.android.server.display.brightness.BrightnessEvent;
@@ -255,7 +256,7 @@
private String mDisplayBrightnessStrategyName;
private boolean mShouldUseAutoBrightness;
private boolean mIsSlowChange;
- private float mMaxBrightness;
+ private float mMaxBrightness = PowerManager.BRIGHTNESS_MAX;
private float mMinBrightness;
private float mCustomAnimationRate = CUSTOM_ANIMATION_RATE_NOT_SET;
private boolean mShouldUpdateScreenBrightnessSetting;
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 2b732ea..ed16b14 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -134,6 +134,7 @@
import android.util.EventLog;
import android.util.IndentingPrintWriter;
import android.util.IntArray;
+import android.util.MathUtils;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
@@ -1275,6 +1276,9 @@
|| isUidPresentOnDisplayInternal(callingUid, displayId)) {
return info;
}
+ } else if (displayId == Display.DEFAULT_DISPLAY) {
+ Slog.e(TAG, "Default display is null for info request from uid "
+ + callingUid);
}
return null;
}
@@ -2223,10 +2227,11 @@
if (display.isValidLocked()) {
applyDisplayChangedLocked(display);
}
- return;
+ } else {
+ releaseDisplayAndEmitEvent(display, DisplayManagerGlobal.EVENT_DISPLAY_REMOVED);
}
- releaseDisplayAndEmitEvent(display, DisplayManagerGlobal.EVENT_DISPLAY_REMOVED);
+ Slog.i(TAG, "Logical display removed: " + display.getDisplayIdLocked());
}
private void releaseDisplayAndEmitEvent(LogicalDisplay display, int event) {
@@ -3098,6 +3103,7 @@
/**
* Get internal or external viewport. Create it if does not currently exist.
+ *
* @param viewportType - either INTERNAL or EXTERNAL
* @return the viewport with the requested type
*/
@@ -4413,7 +4419,6 @@
}
-
@Override // Binder call
public BrightnessConfiguration getBrightnessConfigurationForUser(int userId) {
final String uniqueId;
@@ -4492,10 +4497,12 @@
@Override // Binder call
public void setBrightness(int displayId, float brightness) {
setBrightness_enforcePermission();
- if (!isValidBrightness(brightness)) {
- Slog.w(TAG, "Attempted to set invalid brightness" + brightness);
+ if (Float.isNaN(brightness)) {
+ Slog.w(TAG, "Attempted to set invalid brightness: " + brightness);
return;
}
+ MathUtils.constrain(brightness, PowerManager.BRIGHTNESS_MIN,
+ PowerManager.BRIGHTNESS_MAX);
final long token = Binder.clearCallingIdentity();
try {
synchronized (mSyncRoot) {
@@ -4791,12 +4798,6 @@
}
}
- private static boolean isValidBrightness(float brightness) {
- return !Float.isNaN(brightness)
- && (brightness >= PowerManager.BRIGHTNESS_MIN)
- && (brightness <= PowerManager.BRIGHTNESS_MAX);
- }
-
@VisibleForTesting
void overrideSensorManager(SensorManager sensorManager) {
synchronized (mSyncRoot) {
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index bb2bed7..7c591e3 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -2222,6 +2222,7 @@
unblockScreenOn();
}
mWindowManagerPolicy.screenTurningOn(mDisplayId, mPendingScreenOnUnblocker);
+ Slog.i(TAG, "Window Manager Policy screenTurningOn complete");
}
// Return true if the screen isn't blocked.
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java
index 40e9198..cf44ac0 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamper.java
@@ -73,7 +73,6 @@
abstract void stop();
protected enum Type {
- THERMAL,
POWER,
WEAR_BEDTIME_MODE,
}
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
index d3be33f..9404034 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java
@@ -72,6 +72,8 @@
private final List<DisplayDeviceDataListener> mDisplayDeviceDataListeners = new ArrayList<>();
private final List<StatefulModifier> mStatefulModifiers = new ArrayList<>();
private final List<UserSwitchListener> mUserSwitchListeners = new ArrayList<>();
+ private final List<DeviceConfigListener> mDeviceConfigListeners = new ArrayList<>();
+
private ModifiersAggregatedState mModifiersAggregatedState = new ModifiersAggregatedState();
private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener;
@@ -144,9 +146,14 @@
if (m instanceof UserSwitchListener l) {
mUserSwitchListeners.add(l);
}
+ if (m instanceof DeviceConfigListener l) {
+ mDeviceConfigListeners.add(l);
+ }
});
- mOnPropertiesChangedListener =
- properties -> mClampers.forEach(BrightnessClamper::onDeviceConfigChanged);
+ mOnPropertiesChangedListener = properties -> {
+ mClampers.forEach(BrightnessClamper::onDeviceConfigChanged);
+ mDeviceConfigListeners.forEach(DeviceConfigListener::onDeviceConfigChanged);
+ };
mLightSensorController.configure(data.getAmbientLightSensor(), data.getDisplayId());
start();
}
@@ -209,8 +216,6 @@
private int getBrightnessMaxReason() {
if (mClamperType == null) {
return BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE;
- } else if (mClamperType == Type.THERMAL) {
- return BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL;
} else if (mClamperType == Type.POWER) {
return BrightnessInfo.BRIGHTNESS_MAX_REASON_POWER_IC;
} else if (mClamperType == Type.WEAR_BEDTIME_MODE) {
@@ -225,7 +230,7 @@
* Called when the user switches.
*/
public void onUserSwitch() {
- mUserSwitchListeners.forEach(listener -> listener.onSwitchUser());
+ mUserSwitchListeners.forEach(UserSwitchListener::onSwitchUser);
}
/**
@@ -294,11 +299,14 @@
state2.mMaxDesiredHdrRatio)
|| !BrightnessSynchronizer.floatEquals(state1.mMaxHdrBrightness,
state2.mMaxHdrBrightness)
- || state1.mSdrHdrRatioSpline != state2.mSdrHdrRatioSpline;
+ || state1.mSdrHdrRatioSpline != state2.mSdrHdrRatioSpline
+ || state1.mMaxBrightnessReason != state2.mMaxBrightnessReason
+ || !BrightnessSynchronizer.floatEquals(state1.mMaxBrightness,
+ state2.mMaxBrightness);
}
private void start() {
- if (!mClampers.isEmpty()) {
+ if (!mClampers.isEmpty() || !mDeviceConfigListeners.isEmpty()) {
mDeviceConfigParameterProvider.addOnPropertiesChangedListener(
mExecutor, mOnPropertiesChangedListener);
}
@@ -333,8 +341,7 @@
ClamperChangeListener clamperChangeListener, DisplayDeviceData data,
DisplayManagerFlags flags, Context context, float currentBrightness) {
List<BrightnessClamper<? super DisplayDeviceData>> clampers = new ArrayList<>();
- clampers.add(
- new BrightnessThermalClamper(handler, clamperChangeListener, data));
+
if (flags.isPowerThrottlingClamperEnabled()) {
// Check if power-throttling config is present.
PowerThrottlingConfigData configData = data.getPowerThrottlingConfigData();
@@ -354,6 +361,8 @@
Handler handler, ClamperChangeListener listener,
DisplayDeviceData data) {
List<BrightnessStateModifier> modifiers = new ArrayList<>();
+ modifiers.add(new BrightnessThermalModifier(handler, listener, data));
+
modifiers.add(new DisplayDimModifier(context));
modifiers.add(new BrightnessLowPowerModeModifier());
if (flags.isEvenDimmerEnabled() && data.mDisplayDeviceConfig.isEvenDimmerAvailable()) {
@@ -384,7 +393,7 @@
/**
* Config Data for clampers/modifiers
*/
- public static class DisplayDeviceData implements BrightnessThermalClamper.ThermalData,
+ public static class DisplayDeviceData implements BrightnessThermalModifier.ThermalData,
BrightnessPowerClamper.PowerData,
BrightnessWearBedtimeModeClamper.WearBedtimeModeData {
@NonNull
@@ -498,13 +507,23 @@
}
/**
+ * Modifier should implement this interface in order to receive device config updates
+ */
+ interface DeviceConfigListener {
+ void onDeviceConfigChanged();
+ }
+
+ /**
* StatefulModifiers contribute to AggregatedState, that is used to decide if brightness
- * adjustement is needed
+ * adjustment is needed
*/
public static class ModifiersAggregatedState {
float mMaxDesiredHdrRatio = HdrBrightnessModifier.DEFAULT_MAX_HDR_SDR_RATIO;
float mMaxHdrBrightness = PowerManager.BRIGHTNESS_MAX;
@Nullable
Spline mSdrHdrRatioSpline = null;
+ @BrightnessInfo.BrightnessMaxReason
+ int mMaxBrightnessReason = BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE;
+ float mMaxBrightness = PowerManager.BRIGHTNESS_MAX;
}
}
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessThermalClamper.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessThermalModifier.java
similarity index 77%
rename from services/core/java/com/android/server/display/brightness/clamper/BrightnessThermalClamper.java
rename to services/core/java/com/android/server/display/brightness/clamper/BrightnessThermalModifier.java
index 4498258..21ef309 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessThermalClamper.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessThermalModifier.java
@@ -22,6 +22,8 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
+import android.hardware.display.BrightnessInfo;
+import android.hardware.display.DisplayManagerInternal;
import android.os.Handler;
import android.os.IThermalEventListener;
import android.os.IThermalService;
@@ -33,8 +35,10 @@
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.display.DisplayBrightnessState;
import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData;
import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData.ThrottlingLevel;
+import com.android.server.display.brightness.BrightnessReason;
import com.android.server.display.config.SensorData;
import com.android.server.display.feature.DeviceConfigParameterProvider;
import com.android.server.display.utils.DeviceConfigParsingUtils;
@@ -43,12 +47,15 @@
import java.io.PrintWriter;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.function.BiFunction;
import java.util.function.Function;
-class BrightnessThermalClamper extends
- BrightnessClamper<BrightnessThermalClamper.ThermalData> {
+class BrightnessThermalModifier implements BrightnessStateModifier,
+ BrightnessClamperController.DisplayDeviceDataListener,
+ BrightnessClamperController.StatefulModifier,
+ BrightnessClamperController.DeviceConfigListener {
private static final String TAG = "BrightnessThermalClamper";
@NonNull
@@ -58,6 +65,11 @@
// data from DeviceConfig, for all displays, for all dataSets
// mapOf(uniqueDisplayId to mapOf(dataSetId to ThermalBrightnessThrottlingData))
@NonNull
+ protected final Handler mHandler;
+ @NonNull
+ protected final BrightnessClamperController.ClamperChangeListener mChangeListener;
+
+ @NonNull
private Map<String, Map<String, ThermalBrightnessThrottlingData>>
mThermalThrottlingDataOverride = Map.of();
// data from DisplayDeviceConfig, for particular display+dataSet
@@ -73,6 +85,8 @@
private String mDataId = null;
@Temperature.ThrottlingStatus
private int mThrottlingStatus = Temperature.THROTTLING_NONE;
+ private float mBrightnessCap = PowerManager.BRIGHTNESS_MAX;
+ private boolean mApplied = false;
private final BiFunction<String, String, ThrottlingLevel> mDataPointMapper = (key, value) -> {
try {
@@ -88,53 +102,51 @@
mDataSetMapper = ThermalBrightnessThrottlingData::create;
- BrightnessThermalClamper(Handler handler, ClamperChangeListener listener,
- ThermalData thermalData) {
- this(new Injector(), handler, listener, thermalData);
+ BrightnessThermalModifier(Handler handler, ClamperChangeListener listener,
+ BrightnessClamperController.DisplayDeviceData data) {
+ this(new Injector(), handler, listener, data);
}
@VisibleForTesting
- BrightnessThermalClamper(Injector injector, Handler handler,
- ClamperChangeListener listener, ThermalData thermalData) {
- super(handler, listener);
+ BrightnessThermalModifier(Injector injector, @NonNull Handler handler,
+ @NonNull ClamperChangeListener listener,
+ @NonNull BrightnessClamperController.DisplayDeviceData data) {
+ mHandler = handler;
+ mChangeListener = listener;
mConfigParameterProvider = injector.getDeviceConfigParameterProvider();
mThermalStatusObserver = new ThermalStatusObserver(injector, handler);
mHandler.post(() -> {
- setDisplayData(thermalData);
- loadOverrideData();
- });
-
- }
-
- @Override
- @NonNull
- Type getType() {
- return Type.THERMAL;
- }
-
- @Override
- void onDeviceConfigChanged() {
- mHandler.post(() -> {
- loadOverrideData();
- recalculateActiveData();
- });
- }
-
- @Override
- void onDisplayChanged(ThermalData data) {
- mHandler.post(() -> {
setDisplayData(data);
- recalculateActiveData();
+ loadOverrideData();
});
}
+ //region BrightnessStateModifier
+ @Override
+ public void apply(DisplayManagerInternal.DisplayPowerRequest request,
+ DisplayBrightnessState.Builder stateBuilder) {
+ if (stateBuilder.getMaxBrightness() > mBrightnessCap) {
+ stateBuilder.setMaxBrightness(mBrightnessCap);
+ stateBuilder.setBrightness(Math.min(stateBuilder.getBrightness(), mBrightnessCap));
+ stateBuilder.setBrightnessMaxReason(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL);
+ stateBuilder.getBrightnessReason().addModifier(BrightnessReason.MODIFIER_THROTTLED);
+ // set fast change only when modifier is activated.
+ // this will allow auto brightness to apply slow change even when modifier is active
+ if (!mApplied) {
+ stateBuilder.setIsSlowChange(false);
+ }
+ mApplied = true;
+ } else {
+ mApplied = false;
+ }
+ }
@Override
- void stop() {
+ public void stop() {
mThermalStatusObserver.stopObserving();
}
@Override
- void dump(PrintWriter writer) {
+ public void dump(PrintWriter writer) {
writer.println("BrightnessThermalClamper:");
writer.println(" mThrottlingStatus: " + mThrottlingStatus);
writer.println(" mUniqueDisplayId: " + mUniqueDisplayId);
@@ -142,10 +154,53 @@
writer.println(" mDataOverride: " + mThermalThrottlingDataOverride);
writer.println(" mDataFromDeviceConfig: " + mThermalThrottlingDataFromDeviceConfig);
writer.println(" mDataActive: " + mThermalThrottlingDataActive);
+ writer.println(" mBrightnessCap:" + mBrightnessCap);
+ writer.println(" mApplied:" + mApplied);
mThermalStatusObserver.dump(writer);
- super.dump(writer);
}
+ @Override
+ public boolean shouldListenToLightSensor() {
+ return false;
+ }
+
+ @Override
+ public void setAmbientLux(float lux) {
+ // noop
+ }
+ //endregion
+
+ //region DisplayDeviceDataListener
+ @Override
+ public void onDisplayChanged(BrightnessClamperController.DisplayDeviceData data) {
+ mHandler.post(() -> {
+ setDisplayData(data);
+ recalculateActiveData();
+ });
+ }
+ //endregion
+
+ //region StatefulModifier
+ @Override
+ public void applyStateChange(
+ BrightnessClamperController.ModifiersAggregatedState aggregatedState) {
+ if (aggregatedState.mMaxBrightness > mBrightnessCap) {
+ aggregatedState.mMaxBrightness = mBrightnessCap;
+ aggregatedState.mMaxBrightnessReason = BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL;
+ }
+ }
+ //endregion
+
+ //region DeviceConfigListener
+ @Override
+ public void onDeviceConfigChanged() {
+ mHandler.post(() -> {
+ loadOverrideData();
+ recalculateActiveData();
+ });
+ }
+ //endregion
+
private void recalculateActiveData() {
if (mUniqueDisplayId == null || mDataId == null) {
return;
@@ -176,14 +231,11 @@
private void recalculateBrightnessCap() {
float brightnessCap = PowerManager.BRIGHTNESS_MAX;
- boolean isActive = false;
-
if (mThermalThrottlingDataActive != null) {
// Throttling levels are sorted by increasing severity
for (ThrottlingLevel level : mThermalThrottlingDataActive.throttlingLevels) {
if (level.thermalStatus <= mThrottlingStatus) {
brightnessCap = level.brightness;
- isActive = true;
} else {
// Throttling levels that are greater than the current status are irrelevant
break;
@@ -191,9 +243,8 @@
}
}
- if (brightnessCap != mBrightnessCap || mIsActive != isActive) {
+ if (brightnessCap != mBrightnessCap) {
mBrightnessCap = brightnessCap;
- mIsActive = isActive;
mChangeListener.onChanged();
}
}
@@ -205,7 +256,6 @@
}
}
-
private final class ThermalStatusObserver extends IThermalEventListener.Stub {
private final Injector mInjector;
private final Handler mHandler;
@@ -228,7 +278,7 @@
String curType = mObserverTempSensor.type;
mObserverTempSensor = tempSensor;
- if (curType.equals(tempSensor.type)) {
+ if (Objects.equals(curType, tempSensor.type)) {
Slog.d(TAG, "Thermal status observer already started");
return;
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java b/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java
index 234d6d3..236333e 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecFeatureAction.java
@@ -53,6 +53,10 @@
// Default state used in common by all the feature actions.
protected static final int STATE_NONE = 0;
+ // Delay to query avr's audio status, some avrs could report the old volume first. It could
+ // show inverse mute state on TV.
+ protected static final long DELAY_GIVE_AUDIO_STATUS = 500;
+
// Internal state indicating the progress of action.
protected int mState = STATE_NONE;
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 3c7b9d3..271836a 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -1638,6 +1638,10 @@
mHandler.post(new WorkSourceUidPreservingRunnable(runnable));
}
+ void runOnServiceThreadDelayed(Runnable runnable, long delay) {
+ mHandler.postDelayed(new WorkSourceUidPreservingRunnable(runnable), delay);
+ }
+
private void assertRunOnServiceThread() {
if (Looper.myLooper() != mHandler.getLooper()) {
throw new IllegalStateException("Should run on service thread.");
diff --git a/services/core/java/com/android/server/hdmi/SendKeyAction.java b/services/core/java/com/android/server/hdmi/SendKeyAction.java
index 7e18d84..11682f6 100644
--- a/services/core/java/com/android/server/hdmi/SendKeyAction.java
+++ b/services/core/java/com/android/server/hdmi/SendKeyAction.java
@@ -180,15 +180,22 @@
&& localDevice().getService().isAbsoluteVolumeBehaviorEnabled()) {
sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(getSourceAddress(),
mTargetAddress),
- __ -> sendCommand(HdmiCecMessageBuilder.buildGiveAudioStatus(
- getSourceAddress(),
- localDevice().findAudioReceiverAddress())));
+ __ -> queryAvrAudioStatus());
} else {
sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(getSourceAddress(),
mTargetAddress));
}
}
+ private void queryAvrAudioStatus() {
+ localDevice().mService.runOnServiceThreadDelayed(
+ () -> sendCommand(HdmiCecMessageBuilder.buildGiveAudioStatus(
+ getSourceAddress(),
+ localDevice().findAudioReceiverAddress())),
+ DELAY_GIVE_AUDIO_STATUS);
+
+ }
+
@Override
public boolean processCommand(HdmiCecMessage cmd) {
// Send key action doesn't need any incoming CEC command, hence does not consume it.
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index af0ccf9..f747879 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -4678,6 +4678,7 @@
}
}
+ // TODO(b/356239178): Make dump proto multi-user aware.
private void dumpDebug(ProtoOutputStream proto, long fieldId) {
synchronized (ImfLock.class) {
final int userId = mCurrentImeUserId;
@@ -6103,17 +6104,40 @@
@BinderThread
private void dumpAsStringNoCheck(FileDescriptor fd, PrintWriter pw, String[] args,
boolean isCritical) {
+ final int argUserId = parseUserIdFromDumpArgs(args);
+ final Printer p = new PrintWriterPrinter(pw);
+ p.println("Current Input Method Manager state:");
+ p.println(" concurrentMultiUserModeEnabled=" + mConcurrentMultiUserModeEnabled);
+ if (mConcurrentMultiUserModeEnabled && argUserId == UserHandle.USER_NULL) {
+ mUserDataRepository.forAllUserData(
+ u -> dumpAsStringNoCheckForUser(u, fd, pw, args, isCritical));
+ } else {
+ final int userId = argUserId != UserHandle.USER_NULL ? argUserId : mCurrentImeUserId;
+ final var userData = getUserData(userId);
+ dumpAsStringNoCheckForUser(userData, fd, pw, args, isCritical);
+ }
+ }
+
+ @UserIdInt
+ private static int parseUserIdFromDumpArgs(String[] args) {
+ final int userIdx = Arrays.binarySearch(args, "--user");
+ if (userIdx == -1 || userIdx == args.length - 1) {
+ return UserHandle.USER_NULL;
+ }
+ return Integer.parseInt(args[userIdx + 1]);
+ }
+
+ // TODO(b/356239178): Update dump format output to better group per-user info.
+ @BinderThread
+ private void dumpAsStringNoCheckForUser(UserData userData, FileDescriptor fd, PrintWriter pw,
+ String[] args, boolean isCritical) {
+ final Printer p = new PrintWriterPrinter(pw);
IInputMethodInvoker method;
ClientState client;
-
- final Printer p = new PrintWriterPrinter(pw);
-
+ p.println(" UserId=" + userData.mUserId);
synchronized (ImfLock.class) {
- final int userId = mCurrentImeUserId;
- final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
- final var userData = getUserData(userId);
- p.println("Current Input Method Manager state:");
- p.println(" concurrentMultiUserModeEnabled" + mConcurrentMultiUserModeEnabled);
+ final InputMethodSettings settings = InputMethodSettingsRepository.get(
+ userData.mUserId);
final List<InputMethodInfo> methodList = settings.getMethodList();
int numImes = methodList.size();
p.println(" Input Methods:");
@@ -6133,7 +6157,7 @@
p.println(" sessionRequested="
+ c.mSessionRequested);
p.println(" sessionRequestedForAccessibility="
- + c.mSessionRequestedForAccessibility);
+ + c.mSessionRequestedForAccessibility);
p.println(" curSession=" + c.mCurSession);
p.println(" selfReportedDisplayId=" + c.mSelfReportedDisplayId);
p.println(" uid=" + c.mUid);
@@ -6141,7 +6165,6 @@
};
mClientController.forAllClients(clientControllerDump);
final var bindingController = userData.mBindingController;
- p.println(" mCurrentImeUserId=" + userData.mUserId);
p.println(" mCurMethodId=" + bindingController.getSelectedMethodId());
client = userData.mCurClient;
p.println(" mCurClient=" + client + " mCurSeq="
@@ -6234,8 +6257,6 @@
p.println("No input method client.");
}
synchronized (ImfLock.class) {
- final int userId = mCurrentImeUserId;
- final var userData = getUserData(userId);
if (userData.mImeBindingState.mFocusedWindowClient != null
&& client != userData.mImeBindingState.mFocusedWindowClient) {
p.println(" ");
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 0cc50e6..3523a33 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -103,7 +103,7 @@
* - A remote interface definition (aidl) provided by the service used for communication.
*/
abstract public class ManagedServices {
- protected final String TAG = getClass().getSimpleName();
+ protected final String TAG = getClass().getSimpleName().replace('$', '.');
protected final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final int ON_BINDING_DIED_REBIND_DELAY_MS = 10000;
diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
index 1c70af0..5ca5fda 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -397,6 +397,8 @@
ai.privateFlags |= flag(state.isInstantApp(), ApplicationInfo.PRIVATE_FLAG_INSTANT)
| flag(state.isVirtualPreload(), ApplicationInfo.PRIVATE_FLAG_VIRTUAL_PRELOAD)
| flag(state.isHidden(), ApplicationInfo.PRIVATE_FLAG_HIDDEN);
+ ai.privateFlagsExt |=
+ flag(state.isNotLaunched(), ApplicationInfo.PRIVATE_FLAG_EXT_NOT_LAUNCHED);
if (state.getEnabledState() == PackageManager.COMPONENT_ENABLED_STATE_ENABLED) {
ai.enabled = true;
} else if (state.getEnabledState()
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 749025b..1fcd7f1 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -810,7 +810,7 @@
event.recycle();
break;
case MSG_HANDLE_ALL_APPS:
- launchAllAppsAction();
+ launchAllAppsAction((KeyEvent) msg.obj);
break;
case MSG_RINGER_TOGGLE_CHORD:
handleRingerChordGesture();
@@ -1879,26 +1879,31 @@
}
}
- private void launchAllAppsAction() {
- Intent intent = new Intent(Intent.ACTION_ALL_APPS);
- if (mHasFeatureLeanback) {
- Intent intentLauncher = new Intent(Intent.ACTION_MAIN);
- intentLauncher.addCategory(Intent.CATEGORY_HOME);
- ResolveInfo resolveInfo = mPackageManager.resolveActivityAsUser(intentLauncher,
- PackageManager.MATCH_SYSTEM_ONLY,
- mCurrentUserId);
- if (resolveInfo != null) {
- intent.setPackage(resolveInfo.activityInfo.packageName);
+ private void launchAllAppsAction(KeyEvent event) {
+ if (mHasFeatureLeanback || mHasFeatureWatch) {
+ // TV and watch support the all apps intent
+ notifyKeyGestureCompleted(event,
+ KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS);
+ Intent intent = new Intent(Intent.ACTION_ALL_APPS);
+ if (mHasFeatureLeanback) {
+ Intent intentLauncher = new Intent(Intent.ACTION_MAIN);
+ intentLauncher.addCategory(Intent.CATEGORY_HOME);
+ ResolveInfo resolveInfo = mPackageManager.resolveActivityAsUser(intentLauncher,
+ PackageManager.MATCH_SYSTEM_ONLY,
+ mCurrentUserId);
+ if (resolveInfo != null) {
+ intent.setPackage(resolveInfo.activityInfo.packageName);
+ }
}
- }
- startActivityAsUser(intent, UserHandle.CURRENT);
- }
-
- private void launchAllAppsViaA11y() {
- AccessibilityManagerInternal accessibilityManager = getAccessibilityManagerInternal();
- if (accessibilityManager != null) {
- accessibilityManager.performSystemAction(
- AccessibilityService.GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS);
+ startActivityAsUser(intent, UserHandle.CURRENT);
+ } else {
+ notifyKeyGestureCompleted(event,
+ KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS);
+ AccessibilityManagerInternal accessibilityManager = getAccessibilityManagerInternal();
+ if (accessibilityManager != null) {
+ accessibilityManager.performSystemAction(
+ AccessibilityService.GLOBAL_ACTION_ACCESSIBILITY_ALL_APPS);
+ }
}
dismissKeyboardShortcutsMenu();
}
@@ -2076,15 +2081,7 @@
performHapticFeedback(HapticFeedbackConstants.LONG_PRESS, "Home - Long Press");
switch (mLongPressOnHomeBehavior) {
case LONG_PRESS_HOME_ALL_APPS:
- if (mHasFeatureLeanback) {
- launchAllAppsAction();
- notifyKeyGestureCompleted(event,
- KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS);
- } else {
- launchAllAppsViaA11y();
- notifyKeyGestureCompleted(event,
- KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS);
- }
+ launchAllAppsAction(event);
break;
case LONG_PRESS_HOME_ASSIST:
notifyKeyGestureCompleted(event,
@@ -3695,18 +3692,11 @@
break;
case KeyEvent.KEYCODE_ALL_APPS:
if (firstDown) {
- if (mHasFeatureLeanback) {
- mHandler.removeMessages(MSG_HANDLE_ALL_APPS);
- Message msg = mHandler.obtainMessage(MSG_HANDLE_ALL_APPS);
- msg.setAsynchronous(true);
- msg.sendToTarget();
- notifyKeyGestureCompleted(event,
- KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS);
- } else {
- launchAllAppsViaA11y();
- notifyKeyGestureCompleted(event,
- KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS);
- }
+ mHandler.removeMessages(MSG_HANDLE_ALL_APPS);
+
+ Message msg = mHandler.obtainMessage(MSG_HANDLE_ALL_APPS, new KeyEvent(event));
+ msg.setAsynchronous(true);
+ msg.sendToTarget();
}
return true;
case KeyEvent.KEYCODE_NOTIFICATION:
@@ -3759,9 +3749,7 @@
KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK);
} else if (mPendingMetaAction) {
if (!canceled) {
- launchAllAppsViaA11y();
- notifyKeyGestureCompleted(event,
- KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS);
+ launchAllAppsAction(event);
}
mPendingMetaAction = false;
}
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 12e7fd0..71cb882 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -3668,7 +3668,7 @@
mBrightWhenDozingConfig);
int wakefulness = powerGroup.getWakefulnessLocked();
if (DEBUG_SPEW) {
- Slog.d(TAG, "updateDisplayPowerStateLocked: displayReady=" + ready
+ Slog.d(TAG, "updatePowerGroupsLocked: displayReady=" + ready
+ ", groupId=" + groupId
+ ", policy=" + policyToString(powerGroup.getPolicyLocked())
+ ", mWakefulness="
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 60b5e65..91a17a9 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -599,13 +599,6 @@
ComponentName component = it.next();
ServiceState serviceState = userState.serviceStateMap.get(component);
if (serviceState != null && serviceState.sessionTokens.isEmpty()) {
- if (serviceState.callback != null) {
- try {
- serviceState.service.unregisterCallback(serviceState.callback);
- } catch (RemoteException e) {
- Slog.e(TAG, "error in unregisterCallback", e);
- }
- }
unbindService(serviceState);
it.remove();
}
@@ -667,13 +660,6 @@
// Unregister all callbacks and unbind all services.
for (ServiceState serviceState : userState.serviceStateMap.values()) {
if (serviceState.service != null) {
- if (serviceState.callback != null) {
- try {
- serviceState.service.unregisterCallback(serviceState.callback);
- } catch (RemoteException e) {
- Slog.e(TAG, "error in unregisterCallback", e);
- }
- }
unbindService(serviceState);
}
}
@@ -3571,12 +3557,19 @@
@GuardedBy("mLock")
private void unbindService(ServiceState serviceState) {
- if (!serviceState.bound) {
+ if (serviceState == null || !serviceState.bound) {
return;
}
if (DEBUG) {
Slog.d(TAG, "unbindService(service=" + serviceState.component + ")");
}
+ if (serviceState.callback != null) {
+ try {
+ serviceState.service.unregisterCallback(serviceState.callback);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "error in unregisterCallback", e);
+ }
+ }
mContext.unbindService(serviceState.connection);
serviceState.bound = false;
serviceState.service = null;
@@ -3794,9 +3787,9 @@
if (serviceState.hardwareInputMap.containsKey(inputInfo.getId())) {
return;
}
- Slog.d("ServiceCallback",
- "addHardwareInput: device id " + deviceId + ", "
- + inputInfo.toString());
+ Slog.d(TAG, "ServiceCallback: addHardwareInput, deviceId: " + deviceId +
+ ", inputInfo: " + inputInfo.toString() + " by " + mComponent +
+ ", userId: " + mUserId);
mTvInputHardwareManager.addHardwareInput(deviceId, inputInfo);
addHardwareInputLocked(inputInfo, mComponent, mUserId);
}
@@ -3815,6 +3808,9 @@
if (serviceState.hardwareInputMap.containsKey(inputInfo.getId())) {
return;
}
+ Slog.d(TAG, "ServiceCallback: addHdmiInput, id: " + id +
+ ", inputInfo: "+ inputInfo.toString() + " by " + mComponent +
+ ", userId: " + mUserId);
mTvInputHardwareManager.addHdmiInput(id, inputInfo);
addHardwareInputLocked(inputInfo, mComponent, mUserId);
if (mOnScreenInputId != null && mOnScreenSessionState != null) {
@@ -3845,8 +3841,8 @@
final long identity = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- Slog.d("ServiceCallback",
- "removeHardwareInput " + inputId + " by " + mComponent);
+ Slog.d(TAG, "ServiceCallback: removeHardwareInput, inputId: " + inputId +
+ " by " + mComponent + ", userId: " + mUserId);
removeHardwareInputLocked(inputId, mUserId);
}
} finally {
diff --git a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
index ecd140e..96a25da 100644
--- a/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
+++ b/services/core/java/com/android/server/vcn/TelephonySubscriptionTracker.java
@@ -44,7 +44,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;
-import com.android.internal.telephony.flags.Flags;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper;
@@ -324,11 +323,8 @@
if (SubscriptionManager.isValidSubscriptionId(subId)) {
// Get only configs as needed to save memory.
final PersistableBundle carrierConfig =
- Flags.fixCrashOnGettingConfigWhenPhoneIsGone()
- ? CarrierConfigManager.getCarrierConfigSubset(mContext, subId,
- VcnManager.VCN_RELATED_CARRIER_CONFIG_KEYS)
- : mCarrierConfigManager.getConfigForSubId(subId,
- VcnManager.VCN_RELATED_CARRIER_CONFIG_KEYS);
+ CarrierConfigManager.getCarrierConfigSubset(mContext, subId,
+ VcnManager.VCN_RELATED_CARRIER_CONFIG_KEYS);
if (mDeps.isConfigForIdentifiedCarrier(carrierConfig)) {
mReadySubIdsBySlotId.put(slotId, subId);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 85e24c1..b6e45fc8 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -14799,7 +14799,8 @@
}
@Override
- public void setSecondaryLockscreenEnabled(ComponentName who, boolean enabled) {
+ public void setSecondaryLockscreenEnabled(ComponentName who, boolean enabled,
+ PersistableBundle options) {
Objects.requireNonNull(who, "ComponentName is null");
// Check can set secondary lockscreen enabled
@@ -18644,11 +18645,9 @@
toggleBackupServiceActive(caller.getUserId(), enabled);
- if (Flags.backupServiceSecurityLogEventEnabled()) {
- if (SecurityLog.isLoggingEnabled()) {
- SecurityLog.writeEvent(SecurityLog.TAG_BACKUP_SERVICE_TOGGLED,
- caller.getPackageName(), caller.getUserId(), enabled ? 1 : 0);
- }
+ if (SecurityLog.isLoggingEnabled()) {
+ SecurityLog.writeEvent(SecurityLog.TAG_BACKUP_SERVICE_TOGGLED,
+ caller.getPackageName(), caller.getUserId(), enabled ? 1 : 0);
}
}
diff --git a/services/tests/appfunctions/Android.bp b/services/tests/appfunctions/Android.bp
new file mode 100644
index 0000000..b5cf986
--- /dev/null
+++ b/services/tests/appfunctions/Android.bp
@@ -0,0 +1,59 @@
+// Copyright (C) 2024 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+android_test {
+ name: "FrameworksAppFunctionsTests",
+ team: "trendy_team_machine_learning",
+ defaults: [
+ "modules-utils-testable-device-config-defaults",
+ ],
+
+ srcs: [
+ "src/**/*.kt",
+ ],
+
+ static_libs: [
+ "androidx.test.core",
+ "androidx.test.runner",
+ "androidx.test.ext.truth",
+ "platform-test-annotations",
+ "services.appfunctions",
+ "servicestests-core-utils",
+ "truth",
+ "frameworks-base-testutils",
+ "androidx.test.rules",
+ ],
+
+ libs: [
+ "android.test.base",
+ "android.test.runner",
+ ],
+
+ certificate: "platform",
+ platform_apis: true,
+ test_suites: ["device-tests"],
+
+ optimize: {
+ enabled: false,
+ },
+}
diff --git a/services/tests/appfunctions/AndroidManifest.xml b/services/tests/appfunctions/AndroidManifest.xml
new file mode 100644
index 0000000..1d42b17
--- /dev/null
+++ b/services/tests/appfunctions/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT 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.frameworks.appfunctionstests">
+
+ <application android:testOnly="true"
+ android:debuggable="true">
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.frameworks.appfunctionstests"
+ android:label="Frameworks AppFunctions Services Tests" />
+
+</manifest>
\ No newline at end of file
diff --git a/services/tests/appfunctions/AndroidTest.xml b/services/tests/appfunctions/AndroidTest.xml
new file mode 100644
index 0000000..0650120
--- /dev/null
+++ b/services/tests/appfunctions/AndroidTest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2024 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Runs Frameworks AppFunctions Services Tests.">
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="install-arg" value="-t" />
+ <option name="test-file-name" value="FrameworksAppFunctionsTests.apk" />
+ </target_preparer>
+
+ <option name="test-tag" value="FrameworksAppFunctionsTests" />
+
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.frameworks.appfunctionstests" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <option name="hidden-api-checks" value="false"/>
+ </test>
+</configuration>
\ No newline at end of file
diff --git a/services/tests/appfunctions/src/android/app/appfunctions/AppFunctionStaticMetadataHelperTest.kt b/services/tests/appfunctions/src/android/app/appfunctions/AppFunctionStaticMetadataHelperTest.kt
new file mode 100644
index 0000000..d8ce393
--- /dev/null
+++ b/services/tests/appfunctions/src/android/app/appfunctions/AppFunctionStaticMetadataHelperTest.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.appfunctions
+
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class AppFunctionStaticMetadataHelperTest {
+
+ @Test
+ fun getStaticSchemaNameForPackage() {
+ val actualSchemaName =
+ AppFunctionStaticMetadataHelper.getStaticSchemaNameForPackage("com.example.app")
+
+ assertThat(actualSchemaName).isEqualTo("AppFunctionStaticMetadata-com.example.app")
+ }
+
+ @Test
+ fun getDocumentIdForAppFunction() {
+ val packageName = "com.example.app"
+ val functionId = "someFunction"
+
+ val actualDocumentId =
+ AppFunctionStaticMetadataHelper.getDocumentIdForAppFunction(packageName, functionId)
+
+ assertThat(actualDocumentId).isEqualTo("com.example.app/someFunction")
+ }
+
+ @Test
+ fun getStaticMetadataQualifiedId() {
+ val packageName = "com.example.app"
+ val functionId = "someFunction"
+
+ val actualQualifiedId =
+ AppFunctionStaticMetadataHelper.getStaticMetadataQualifiedId(packageName, functionId)
+
+ assertThat(actualQualifiedId)
+ .isEqualTo("android\$apps-db/app_functions#com.example.app/someFunction")
+ }
+}
diff --git a/services/tests/appfunctions/src/com/android/server/appfunctions/FutureAppSearchSessionTest.kt b/services/tests/appfunctions/src/com/android/server/appfunctions/FutureAppSearchSessionTest.kt
new file mode 100644
index 0000000..5233f19
--- /dev/null
+++ b/services/tests/appfunctions/src/com/android/server/appfunctions/FutureAppSearchSessionTest.kt
@@ -0,0 +1,131 @@
+/*
+ * 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.appfunctions
+
+import android.app.appfunctions.AppFunctionRuntimeMetadata
+import android.app.appfunctions.AppFunctionRuntimeMetadata.createAppFunctionRuntimeSchema
+import android.app.appfunctions.AppFunctionRuntimeMetadata.createParentAppFunctionRuntimeSchema
+import android.app.appsearch.AppSearchManager
+import android.app.appsearch.PutDocumentsRequest
+import android.app.appsearch.SearchSpec
+import android.app.appsearch.SetSchemaRequest
+import androidx.test.platform.app.InstrumentationRegistry
+import com.google.common.truth.Truth.assertThat
+import com.google.common.util.concurrent.MoreExecutors
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(JUnit4::class)
+class FutureAppSearchSessionTest {
+ private val context = InstrumentationRegistry.getInstrumentation().targetContext
+ private val appSearchManager = context.getSystemService(AppSearchManager::class.java)
+ private val testExecutor = MoreExecutors.directExecutor()
+
+ @Before
+ @After
+ fun clearData() {
+ val searchContext = AppSearchManager.SearchContext.Builder(TEST_DB).build()
+ FutureAppSearchSession(appSearchManager, testExecutor, searchContext).use {
+ val setSchemaRequest = SetSchemaRequest.Builder().build()
+ it.setSchema(setSchemaRequest)
+ }
+ }
+
+ @Test
+ fun setSchema() {
+ val searchContext = AppSearchManager.SearchContext.Builder(TEST_DB).build()
+ FutureAppSearchSession(appSearchManager, testExecutor, searchContext).use { session ->
+ val setSchemaRequest =
+ SetSchemaRequest.Builder()
+ .addSchemas(
+ createParentAppFunctionRuntimeSchema(),
+ createAppFunctionRuntimeSchema(TEST_PACKAGE_NAME)
+ )
+ .build()
+
+ val schema = session.setSchema(setSchemaRequest)
+
+ assertThat(schema.get()).isNotNull()
+ }
+ }
+
+ @Test
+ fun put() {
+ val searchContext = AppSearchManager.SearchContext.Builder(TEST_DB).build()
+ FutureAppSearchSession(appSearchManager, testExecutor, searchContext).use { session ->
+ val setSchemaRequest =
+ SetSchemaRequest.Builder()
+ .addSchemas(
+ createParentAppFunctionRuntimeSchema(),
+ createAppFunctionRuntimeSchema(TEST_PACKAGE_NAME)
+ )
+ .build()
+ val schema = session.setSchema(setSchemaRequest)
+ assertThat(schema.get()).isNotNull()
+ val appFunctionRuntimeMetadata =
+ AppFunctionRuntimeMetadata.Builder(TEST_PACKAGE_NAME, TEST_FUNCTION_ID, "").build()
+ val putDocumentsRequest: PutDocumentsRequest =
+ PutDocumentsRequest.Builder()
+ .addGenericDocuments(appFunctionRuntimeMetadata)
+ .build()
+
+ val putResult = session.put(putDocumentsRequest)
+
+ assertThat(putResult.get().isSuccess).isTrue()
+ }
+ }
+
+ @Test
+ fun search() {
+ val searchContext = AppSearchManager.SearchContext.Builder(TEST_DB).build()
+ FutureAppSearchSession(appSearchManager, testExecutor, searchContext).use { session ->
+ val setSchemaRequest =
+ SetSchemaRequest.Builder()
+ .addSchemas(
+ createParentAppFunctionRuntimeSchema(),
+ createAppFunctionRuntimeSchema(TEST_PACKAGE_NAME)
+ )
+ .build()
+ val schema = session.setSchema(setSchemaRequest)
+ assertThat(schema.get()).isNotNull()
+ val appFunctionRuntimeMetadata =
+ AppFunctionRuntimeMetadata.Builder(TEST_PACKAGE_NAME, TEST_FUNCTION_ID, "").build()
+ val putDocumentsRequest: PutDocumentsRequest =
+ PutDocumentsRequest.Builder()
+ .addGenericDocuments(appFunctionRuntimeMetadata)
+ .build()
+ val putResult = session.put(putDocumentsRequest)
+ assertThat(putResult.get().isSuccess).isTrue()
+
+ val searchResult = session.search("", SearchSpec.Builder().build())
+
+ val genericDocs =
+ searchResult.get().nextPage.get().stream().map { it.genericDocument }.toList()
+ assertThat(genericDocs).hasSize(1)
+ val foundAppFunctionRuntimeMetadata = AppFunctionRuntimeMetadata(genericDocs[0])
+ assertThat(foundAppFunctionRuntimeMetadata.functionId).isEqualTo(TEST_FUNCTION_ID)
+ }
+ }
+
+ private companion object {
+ const val TEST_DB: String = "test_db"
+ const val TEST_PACKAGE_NAME: String = "test_pkg"
+ const val TEST_FUNCTION_ID: String = "print"
+ }
+}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
index 5bb8ded..52f1cbd 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -107,6 +107,7 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.MessageQueue;
+import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemProperties;
@@ -3053,6 +3054,74 @@
}
@Test
+ public void testBrightnessUpdates() {
+ DisplayManagerService displayManager =
+ new DisplayManagerService(mContext, mShortMockedInjector);
+ DisplayManagerInternal localService = displayManager.new LocalService();
+ DisplayManagerService.BinderService displayManagerBinderService =
+ displayManager.new BinderService();
+ registerDefaultDisplays(displayManager);
+ initDisplayPowerController(localService);
+
+ final float invalidBrightness = -0.3f;
+ final float brightnessOff = -1.0f;
+ final float minimumBrightness = 0.0f;
+ final float validBrightness = 0.5f;
+
+ Settings.System.putInt(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_MODE,
+ Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL);
+
+ // set and check valid brightness
+ waitForIdleHandler(mPowerHandler);
+ displayManagerBinderService.setBrightness(Display.DEFAULT_DISPLAY, validBrightness);
+ waitForIdleHandler(mPowerHandler);
+ assertEquals(validBrightness,
+ displayManagerBinderService.getBrightness(Display.DEFAULT_DISPLAY),
+ FLOAT_TOLERANCE);
+
+ // set and check invalid brightness
+ waitForIdleHandler(mPowerHandler);
+ displayManagerBinderService.setBrightness(Display.DEFAULT_DISPLAY, invalidBrightness);
+ waitForIdleHandler(mPowerHandler);
+ assertEquals(PowerManager.BRIGHTNESS_MIN,
+ displayManagerBinderService.getBrightness(Display.DEFAULT_DISPLAY),
+ FLOAT_TOLERANCE);
+
+ // reset and check valid brightness
+ waitForIdleHandler(mPowerHandler);
+ displayManagerBinderService.setBrightness(Display.DEFAULT_DISPLAY, validBrightness);
+ waitForIdleHandler(mPowerHandler);
+ assertEquals(validBrightness,
+ displayManagerBinderService.getBrightness(Display.DEFAULT_DISPLAY),
+ FLOAT_TOLERANCE);
+
+ // set and check brightness off
+ waitForIdleHandler(mPowerHandler);
+ displayManagerBinderService.setBrightness(Display.DEFAULT_DISPLAY, brightnessOff);
+ waitForIdleHandler(mPowerHandler);
+ assertEquals(PowerManager.BRIGHTNESS_MIN,
+ displayManagerBinderService.getBrightness(Display.DEFAULT_DISPLAY),
+ FLOAT_TOLERANCE);
+
+ // reset and check valid brightness
+ waitForIdleHandler(mPowerHandler);
+ displayManagerBinderService.setBrightness(Display.DEFAULT_DISPLAY, validBrightness);
+ waitForIdleHandler(mPowerHandler);
+ assertEquals(validBrightness,
+ displayManagerBinderService.getBrightness(Display.DEFAULT_DISPLAY),
+ FLOAT_TOLERANCE);
+
+ // set and check minimum brightness
+ waitForIdleHandler(mPowerHandler);
+ displayManagerBinderService.setBrightness(Display.DEFAULT_DISPLAY, minimumBrightness);
+ waitForIdleHandler(mPowerHandler);
+ assertEquals(PowerManager.BRIGHTNESS_MIN,
+ displayManagerBinderService.getBrightness(Display.DEFAULT_DISPLAY),
+ FLOAT_TOLERANCE);
+ }
+
+ @Test
public void testResolutionChangeGetsBackedUp() throws Exception {
when(mMockFlags.isResolutionBackupRestoreEnabled()).thenReturn(true);
DisplayManagerService displayManager =
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java
index f9dc122..da79f30 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java
@@ -226,7 +226,7 @@
float clampedBrightness = 0.6f;
float customAnimationRate = 0.01f;
when(mMockClamper.getBrightnessCap()).thenReturn(clampedBrightness);
- when(mMockClamper.getType()).thenReturn(BrightnessClamper.Type.THERMAL);
+ when(mMockClamper.getType()).thenReturn(BrightnessClamper.Type.POWER);
when(mMockClamper.getCustomAnimationRate()).thenReturn(customAnimationRate);
when(mMockClamper.isActive()).thenReturn(false);
mTestInjector.mCapturedChangeListener.onChanged();
@@ -250,7 +250,7 @@
float clampedBrightness = 0.6f;
float customAnimationRate = 0.01f;
when(mMockClamper.getBrightnessCap()).thenReturn(clampedBrightness);
- when(mMockClamper.getType()).thenReturn(BrightnessClamper.Type.THERMAL);
+ when(mMockClamper.getType()).thenReturn(BrightnessClamper.Type.POWER);
when(mMockClamper.getCustomAnimationRate()).thenReturn(customAnimationRate);
when(mMockClamper.isActive()).thenReturn(true);
mTestInjector.mCapturedChangeListener.onChanged();
@@ -274,7 +274,7 @@
float clampedBrightness = 0.8f;
float customAnimationRate = 0.01f;
when(mMockClamper.getBrightnessCap()).thenReturn(clampedBrightness);
- when(mMockClamper.getType()).thenReturn(BrightnessClamper.Type.THERMAL);
+ when(mMockClamper.getType()).thenReturn(BrightnessClamper.Type.POWER);
when(mMockClamper.getCustomAnimationRate()).thenReturn(customAnimationRate);
when(mMockClamper.isActive()).thenReturn(true);
mTestInjector.mCapturedChangeListener.onChanged();
@@ -298,7 +298,7 @@
float clampedBrightness = 0.6f;
float customAnimationRate = 0.01f;
when(mMockClamper.getBrightnessCap()).thenReturn(clampedBrightness);
- when(mMockClamper.getType()).thenReturn(BrightnessClamper.Type.THERMAL);
+ when(mMockClamper.getType()).thenReturn(BrightnessClamper.Type.POWER);
when(mMockClamper.getCustomAnimationRate()).thenReturn(customAnimationRate);
when(mMockClamper.isActive()).thenReturn(true);
mTestInjector.mCapturedChangeListener.onChanged();
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessThermalClamperTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessThermalClamperTest.java
deleted file mode 100644
index 9d16594..0000000
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessThermalClamperTest.java
+++ /dev/null
@@ -1,312 +0,0 @@
-/*
- * 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.display.brightness.clamper;
-
-import static com.android.server.display.config.DisplayDeviceConfigTestUtilsKt.createSensorData;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.verify;
-
-import android.hardware.display.DisplayManager;
-import android.os.IThermalEventListener;
-import android.os.IThermalService;
-import android.os.PowerManager;
-import android.os.RemoteException;
-import android.os.Temperature;
-import android.provider.DeviceConfig;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import com.android.internal.annotations.Keep;
-import com.android.server.display.DisplayDeviceConfig;
-import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData;
-import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData.ThrottlingLevel;
-import com.android.server.display.config.SensorData;
-import com.android.server.display.feature.DeviceConfigParameterProvider;
-import com.android.server.testutils.FakeDeviceConfigInterface;
-import com.android.server.testutils.TestHandler;
-
-import junitparams.JUnitParamsRunner;
-import junitparams.Parameters;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.List;
-
-@RunWith(JUnitParamsRunner.class)
-public class BrightnessThermalClamperTest {
-
- private static final float FLOAT_TOLERANCE = 0.001f;
-
- private static final String DISPLAY_ID = "displayId";
- @Mock
- private IThermalService mMockThermalService;
- @Mock
- private BrightnessClamperController.ClamperChangeListener mMockClamperChangeListener;
-
- private final FakeDeviceConfigInterface mFakeDeviceConfigInterface =
- new FakeDeviceConfigInterface();
- private final TestHandler mTestHandler = new TestHandler(null);
- private BrightnessThermalClamper mClamper;
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- mClamper = new BrightnessThermalClamper(new TestInjector(), mTestHandler,
- mMockClamperChangeListener, new TestThermalData());
- mTestHandler.flush();
- }
-
- @Test
- public void testTypeIsThermal() {
- assertEquals(BrightnessClamper.Type.THERMAL, mClamper.getType());
- }
-
- @Test
- public void testNoThrottlingData() {
- assertFalse(mClamper.isActive());
- assertEquals(PowerManager.BRIGHTNESS_MAX, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
- }
-
- @Keep
- private static Object[][] testThrottlingData() {
- // throttlingLevels, throttlingStatus, expectedActive, expectedBrightness
- return new Object[][] {
- // no throttling data
- {List.of(), Temperature.THROTTLING_LIGHT, false, PowerManager.BRIGHTNESS_MAX},
- // throttlingStatus < min throttling data
- {List.of(
- new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE, 0.5f),
- new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.1f)),
- Temperature.THROTTLING_LIGHT, false, PowerManager.BRIGHTNESS_MAX},
- // throttlingStatus = min throttling data
- {List.of(
- new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE, 0.5f),
- new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.1f)),
- Temperature.THROTTLING_MODERATE, true, 0.5f},
- // throttlingStatus between min and max throttling data
- {List.of(
- new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE, 0.5f),
- new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.1f)),
- Temperature.THROTTLING_SEVERE, true, 0.5f},
- // throttlingStatus = max throttling data
- {List.of(
- new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE, 0.5f),
- new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.1f)),
- Temperature.THROTTLING_CRITICAL, true, 0.1f},
- // throttlingStatus > max throttling data
- {List.of(
- new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE, 0.5f),
- new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.1f)),
- Temperature.THROTTLING_EMERGENCY, true, 0.1f},
- };
- }
- @Test
- @Parameters(method = "testThrottlingData")
- public void testNotifyThrottlingAfterOnDisplayChange(List<ThrottlingLevel> throttlingLevels,
- @Temperature.ThrottlingStatus int throttlingStatus,
- boolean expectedActive, float expectedBrightness) throws RemoteException {
- IThermalEventListener thermalEventListener = captureSkinThermalEventListener();
- mClamper.onDisplayChanged(new TestThermalData(throttlingLevels));
- mTestHandler.flush();
- assertFalse(mClamper.isActive());
- assertEquals(PowerManager.BRIGHTNESS_MAX, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
-
- thermalEventListener.notifyThrottling(createSkinTemperature(throttlingStatus));
- mTestHandler.flush();
- assertEquals(expectedActive, mClamper.isActive());
- assertEquals(expectedBrightness, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
- }
-
- @Test
- @Parameters(method = "testThrottlingData")
- public void testOnDisplayChangeAfterNotifyThrottling(List<ThrottlingLevel> throttlingLevels,
- @Temperature.ThrottlingStatus int throttlingStatus,
- boolean expectedActive, float expectedBrightness) throws RemoteException {
- IThermalEventListener thermalEventListener = captureSkinThermalEventListener();
- thermalEventListener.notifyThrottling(createSkinTemperature(throttlingStatus));
- mTestHandler.flush();
- assertFalse(mClamper.isActive());
- assertEquals(PowerManager.BRIGHTNESS_MAX, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
-
- mClamper.onDisplayChanged(new TestThermalData(throttlingLevels));
- mTestHandler.flush();
- assertEquals(expectedActive, mClamper.isActive());
- assertEquals(expectedBrightness, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
- }
-
- @Test
- public void testOverrideData() throws RemoteException {
- IThermalEventListener thermalEventListener = captureSkinThermalEventListener();
- thermalEventListener.notifyThrottling(createSkinTemperature(Temperature.THROTTLING_SEVERE));
- mTestHandler.flush();
- assertFalse(mClamper.isActive());
- assertEquals(PowerManager.BRIGHTNESS_MAX, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
-
- mClamper.onDisplayChanged(new TestThermalData(
- List.of(new ThrottlingLevel(PowerManager.THERMAL_STATUS_SEVERE, 0.5f))));
- mTestHandler.flush();
- assertTrue(mClamper.isActive());
- assertEquals(0.5f, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
-
- overrideThrottlingData("displayId,1,emergency,0.4");
- mClamper.onDeviceConfigChanged();
- mTestHandler.flush();
-
- assertFalse(mClamper.isActive());
- assertEquals(PowerManager.BRIGHTNESS_MAX, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
-
- overrideThrottlingData("displayId,1,moderate,0.4");
- mClamper.onDeviceConfigChanged();
- mTestHandler.flush();
-
- assertTrue(mClamper.isActive());
- assertEquals(0.4f, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
- }
-
- @Test
- public void testDisplaySensorBasedThrottling() throws RemoteException {
- final int severity = PowerManager.THERMAL_STATUS_SEVERE;
- IThermalEventListener thermalEventListener = captureSkinThermalEventListener();
- // Update config to listen to display type sensor.
- final SensorData tempSensor = createSensorData("DISPLAY", "VIRTUAL-SKIN-DISPLAY");
- final TestThermalData thermalData =
- new TestThermalData(
- DISPLAY_ID,
- DisplayDeviceConfig.DEFAULT_ID,
- List.of(new ThrottlingLevel(severity, 0.5f)),
- tempSensor);
- mClamper.onDisplayChanged(thermalData);
- mTestHandler.flush();
- verify(mMockThermalService).unregisterThermalEventListener(thermalEventListener);
- thermalEventListener = captureThermalEventListener(Temperature.TYPE_DISPLAY);
- assertFalse(mClamper.isActive());
-
- // Verify no throttling triggered when any other sensor notification received.
- thermalEventListener.notifyThrottling(createSkinTemperature(severity));
- mTestHandler.flush();
- assertFalse(mClamper.isActive());
-
- thermalEventListener.notifyThrottling(createDisplayTemperature("OTHER-SENSOR", severity));
- mTestHandler.flush();
- assertFalse(mClamper.isActive());
-
- assertEquals(PowerManager.BRIGHTNESS_MAX, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
-
- // Verify throttling triggered when display sensor of given name throttled.
- thermalEventListener.notifyThrottling(createDisplayTemperature(tempSensor.name, severity));
- mTestHandler.flush();
- assertTrue(mClamper.isActive());
- assertEquals(0.5f, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
- }
-
- private IThermalEventListener captureSkinThermalEventListener() throws RemoteException {
- return captureThermalEventListener(Temperature.TYPE_SKIN);
- }
-
- private IThermalEventListener captureThermalEventListener(int type) throws RemoteException {
- ArgumentCaptor<IThermalEventListener> captor = ArgumentCaptor.forClass(
- IThermalEventListener.class);
- verify(mMockThermalService).registerThermalEventListenerWithType(captor.capture(), eq(
- type));
- return captor.getValue();
- }
-
- private Temperature createDisplayTemperature(
- @NonNull String sensorName, @Temperature.ThrottlingStatus int status) {
- return new Temperature(100, Temperature.TYPE_DISPLAY, sensorName, status);
- }
-
- private Temperature createSkinTemperature(@Temperature.ThrottlingStatus int status) {
- return new Temperature(100, Temperature.TYPE_SKIN, "test_temperature", status);
- }
-
- private void overrideThrottlingData(String data) {
- mFakeDeviceConfigInterface.putProperty(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
- DisplayManager.DeviceConfig.KEY_BRIGHTNESS_THROTTLING_DATA, data);
- }
-
- private class TestInjector extends BrightnessThermalClamper.Injector {
- @Override
- IThermalService getThermalService() {
- return mMockThermalService;
- }
-
- @Override
- DeviceConfigParameterProvider getDeviceConfigParameterProvider() {
- return new DeviceConfigParameterProvider(mFakeDeviceConfigInterface);
- }
- }
-
- private static class TestThermalData implements BrightnessThermalClamper.ThermalData {
-
- private final String mUniqueDisplayId;
- private final String mDataId;
- private final ThermalBrightnessThrottlingData mData;
- private final SensorData mTempSensor;
-
- private TestThermalData() {
- this(DISPLAY_ID, DisplayDeviceConfig.DEFAULT_ID, null,
- SensorData.loadTempSensorUnspecifiedConfig());
- }
-
- private TestThermalData(List<ThrottlingLevel> data) {
- this(DISPLAY_ID, DisplayDeviceConfig.DEFAULT_ID, data,
- SensorData.loadTempSensorUnspecifiedConfig());
- }
-
- private TestThermalData(String uniqueDisplayId, String dataId, List<ThrottlingLevel> data,
- SensorData tempSensor) {
- mUniqueDisplayId = uniqueDisplayId;
- mDataId = dataId;
- mData = ThermalBrightnessThrottlingData.create(data);
- mTempSensor = tempSensor;
- }
-
- @NonNull
- @Override
- public String getUniqueDisplayId() {
- return mUniqueDisplayId;
- }
-
- @NonNull
- @Override
- public String getThermalThrottlingDataId() {
- return mDataId;
- }
-
- @Nullable
- @Override
- public ThermalBrightnessThrottlingData getThermalBrightnessThrottlingData() {
- return mData;
- }
-
- @NonNull
- @Override
- public SensorData getTempSensor() {
- return mTempSensor;
- }
- }
-}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessThermalModifierTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessThermalModifierTest.java
new file mode 100644
index 0000000..35d384b
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessThermalModifierTest.java
@@ -0,0 +1,378 @@
+/*
+ * 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.display.brightness.clamper;
+
+import static com.android.server.display.config.DisplayDeviceConfigTestUtilsKt.createSensorData;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.hardware.display.BrightnessInfo;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManagerInternal;
+import android.os.IBinder;
+import android.os.IThermalEventListener;
+import android.os.IThermalService;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.Temperature;
+import android.provider.DeviceConfig;
+
+import androidx.annotation.NonNull;
+
+import com.android.internal.annotations.Keep;
+import com.android.server.display.DisplayBrightnessState;
+import com.android.server.display.DisplayDeviceConfig;
+import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData;
+import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData.ThrottlingLevel;
+import com.android.server.display.brightness.BrightnessReason;
+import com.android.server.display.brightness.clamper.BrightnessClamperController.ModifiersAggregatedState;
+import com.android.server.display.config.SensorData;
+import com.android.server.display.feature.DeviceConfigParameterProvider;
+import com.android.server.testutils.FakeDeviceConfigInterface;
+import com.android.server.testutils.TestHandler;
+
+import junitparams.JUnitParamsRunner;
+import junitparams.Parameters;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@RunWith(JUnitParamsRunner.class)
+public class BrightnessThermalModifierTest {
+ private static final int NO_MODIFIER = 0;
+
+ private static final float FLOAT_TOLERANCE = 0.001f;
+
+ private static final String DISPLAY_ID = "displayId";
+ @Mock
+ private IThermalService mMockThermalService;
+ @Mock
+ private BrightnessClamperController.ClamperChangeListener mMockClamperChangeListener;
+ @Mock
+ private DisplayManagerInternal.DisplayPowerRequest mMockRequest;
+ @Mock
+ private DisplayDeviceConfig mMockDisplayDeviceConfig;
+ @Mock
+ private IBinder mMockBinder;
+
+ private final FakeDeviceConfigInterface mFakeDeviceConfigInterface =
+ new FakeDeviceConfigInterface();
+ private final TestHandler mTestHandler = new TestHandler(null);
+ private BrightnessThermalModifier mModifier;
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ when(mMockDisplayDeviceConfig.getTempSensor())
+ .thenReturn(SensorData.loadTempSensorUnspecifiedConfig());
+ mModifier = new BrightnessThermalModifier(new TestInjector(), mTestHandler,
+ mMockClamperChangeListener,
+ ClamperTestUtilsKt.createDisplayDeviceData(mMockDisplayDeviceConfig, mMockBinder));
+ mTestHandler.flush();
+ }
+
+
+ @Test
+ public void testNoThrottlingData() {
+ assertModifierState(
+ 0.3f, true,
+ PowerManager.BRIGHTNESS_MAX, 0.3f,
+ false, true);
+ }
+
+ @Keep
+ private static Object[][] testThrottlingData() {
+ // throttlingLevels, throttlingStatus, expectedActive, expectedBrightness
+ return new Object[][] {
+ // no throttling data
+ {List.of(), Temperature.THROTTLING_LIGHT, false, PowerManager.BRIGHTNESS_MAX},
+ // throttlingStatus < min throttling data
+ {List.of(
+ new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE, 0.5f),
+ new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.1f)),
+ Temperature.THROTTLING_LIGHT, false, PowerManager.BRIGHTNESS_MAX},
+ // throttlingStatus = min throttling data
+ {List.of(
+ new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE, 0.5f),
+ new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.1f)),
+ Temperature.THROTTLING_MODERATE, true, 0.5f},
+ // throttlingStatus between min and max throttling data
+ {List.of(
+ new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE, 0.5f),
+ new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.1f)),
+ Temperature.THROTTLING_SEVERE, true, 0.5f},
+ // throttlingStatus = max throttling data
+ {List.of(
+ new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE, 0.5f),
+ new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.1f)),
+ Temperature.THROTTLING_CRITICAL, true, 0.1f},
+ // throttlingStatus > max throttling data
+ {List.of(
+ new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE, 0.5f),
+ new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.1f)),
+ Temperature.THROTTLING_EMERGENCY, true, 0.1f},
+ };
+ }
+ @Test
+ @Parameters(method = "testThrottlingData")
+ public void testNotifyThrottlingAfterOnDisplayChange(List<ThrottlingLevel> throttlingLevels,
+ @Temperature.ThrottlingStatus int throttlingStatus,
+ boolean expectedActive, float expectedBrightness) throws RemoteException {
+ IThermalEventListener thermalEventListener = captureSkinThermalEventListener();
+ onDisplayChange(throttlingLevels);
+ mTestHandler.flush();
+ assertModifierState(
+ PowerManager.BRIGHTNESS_MAX, true,
+ PowerManager.BRIGHTNESS_MAX, PowerManager.BRIGHTNESS_MAX,
+ false, true);
+
+ thermalEventListener.notifyThrottling(createSkinTemperature(throttlingStatus));
+ mTestHandler.flush();
+ assertModifierState(
+ PowerManager.BRIGHTNESS_MAX, true,
+ expectedBrightness, expectedBrightness,
+ expectedActive, !expectedActive);
+ }
+
+ @Test
+ @Parameters(method = "testThrottlingData")
+ public void testOnDisplayChangeAfterNotifyThrottling(List<ThrottlingLevel> throttlingLevels,
+ @Temperature.ThrottlingStatus int throttlingStatus,
+ boolean expectedActive, float expectedBrightness) throws RemoteException {
+ IThermalEventListener thermalEventListener = captureSkinThermalEventListener();
+ thermalEventListener.notifyThrottling(createSkinTemperature(throttlingStatus));
+ mTestHandler.flush();
+
+ assertModifierState(
+ PowerManager.BRIGHTNESS_MAX, true,
+ PowerManager.BRIGHTNESS_MAX, PowerManager.BRIGHTNESS_MAX,
+ false, true);
+
+ onDisplayChange(throttlingLevels);
+ mTestHandler.flush();
+ assertModifierState(
+ PowerManager.BRIGHTNESS_MAX, true,
+ expectedBrightness, expectedBrightness,
+ expectedActive, !expectedActive);
+ }
+
+ @Test
+ public void testAppliesFastChangeOnlyOnActivation() throws RemoteException {
+ IThermalEventListener thermalEventListener = captureSkinThermalEventListener();
+ onDisplayChange(List.of(new ThrottlingLevel(PowerManager.THERMAL_STATUS_SEVERE, 0.5f)));
+ mTestHandler.flush();
+
+ thermalEventListener.notifyThrottling(createSkinTemperature(Temperature.THROTTLING_SEVERE));
+ mTestHandler.flush();
+
+ // expectedSlowChange = false
+ assertModifierState(
+ PowerManager.BRIGHTNESS_MAX, true,
+ 0.5f, 0.5f,
+ true, false);
+
+ // slowChange is unchanged
+ assertModifierState(
+ PowerManager.BRIGHTNESS_MAX, true,
+ 0.5f, 0.5f,
+ true, true);
+ }
+
+ @Test
+ public void testCapsMaxBrightnessOnly_currentBrightnessIsLowAndFastChange()
+ throws RemoteException {
+ IThermalEventListener thermalEventListener = captureSkinThermalEventListener();
+ onDisplayChange(List.of(new ThrottlingLevel(PowerManager.THERMAL_STATUS_SEVERE, 0.5f)));
+ mTestHandler.flush();
+
+ thermalEventListener.notifyThrottling(createSkinTemperature(Temperature.THROTTLING_SEVERE));
+ mTestHandler.flush();
+
+ assertModifierState(
+ 0.1f, false,
+ 0.5f, 0.1f,
+ true, false);
+ }
+
+ @Test
+ public void testOverrideData() throws RemoteException {
+ IThermalEventListener thermalEventListener = captureSkinThermalEventListener();
+ thermalEventListener.notifyThrottling(createSkinTemperature(Temperature.THROTTLING_SEVERE));
+ mTestHandler.flush();
+
+ assertModifierState(
+ PowerManager.BRIGHTNESS_MAX, true,
+ PowerManager.BRIGHTNESS_MAX, PowerManager.BRIGHTNESS_MAX,
+ false, true);
+
+ onDisplayChange(List.of(new ThrottlingLevel(PowerManager.THERMAL_STATUS_SEVERE, 0.5f)));
+ mTestHandler.flush();
+
+ assertModifierState(
+ PowerManager.BRIGHTNESS_MAX, true,
+ 0.5f, 0.5f,
+ true, false);
+
+ overrideThrottlingData("displayId,1,emergency,0.4");
+ mModifier.onDeviceConfigChanged();
+ mTestHandler.flush();
+
+ assertModifierState(
+ PowerManager.BRIGHTNESS_MAX, true,
+ PowerManager.BRIGHTNESS_MAX, PowerManager.BRIGHTNESS_MAX,
+ false, true);
+
+ overrideThrottlingData("displayId,1,moderate,0.4");
+ mModifier.onDeviceConfigChanged();
+ mTestHandler.flush();
+
+ assertModifierState(
+ PowerManager.BRIGHTNESS_MAX, true,
+ 0.4f, 0.4f,
+ true, false);
+ }
+
+ @Test
+ public void testDisplaySensorBasedThrottling() throws RemoteException {
+ final int severity = PowerManager.THERMAL_STATUS_SEVERE;
+ IThermalEventListener thermalEventListener = captureSkinThermalEventListener();
+ // Update config to listen to display type sensor.
+ SensorData tempSensor = createSensorData("DISPLAY", "VIRTUAL-SKIN-DISPLAY");
+
+ when(mMockDisplayDeviceConfig.getTempSensor()).thenReturn(tempSensor);
+ onDisplayChange(List.of(new ThrottlingLevel(severity, 0.5f)));
+ mTestHandler.flush();
+
+ verify(mMockThermalService).unregisterThermalEventListener(thermalEventListener);
+ thermalEventListener = captureThermalEventListener(Temperature.TYPE_DISPLAY);
+ assertModifierState(
+ PowerManager.BRIGHTNESS_MAX, true,
+ PowerManager.BRIGHTNESS_MAX, PowerManager.BRIGHTNESS_MAX,
+ false, true);
+
+ // Verify no throttling triggered when any other sensor notification received.
+ thermalEventListener.notifyThrottling(createSkinTemperature(severity));
+ mTestHandler.flush();
+ assertModifierState(
+ PowerManager.BRIGHTNESS_MAX, true,
+ PowerManager.BRIGHTNESS_MAX, PowerManager.BRIGHTNESS_MAX,
+ false, true);
+
+ thermalEventListener.notifyThrottling(createDisplayTemperature("OTHER-SENSOR", severity));
+ mTestHandler.flush();
+ assertModifierState(
+ PowerManager.BRIGHTNESS_MAX, true,
+ PowerManager.BRIGHTNESS_MAX, PowerManager.BRIGHTNESS_MAX,
+ false, true);
+
+ // Verify throttling triggered when display sensor of given name throttled.
+ thermalEventListener.notifyThrottling(createDisplayTemperature(tempSensor.name, severity));
+ mTestHandler.flush();
+ assertModifierState(
+ PowerManager.BRIGHTNESS_MAX, true,
+ 0.5f, 0.5f,
+ true, false);
+ }
+
+ private IThermalEventListener captureSkinThermalEventListener() throws RemoteException {
+ return captureThermalEventListener(Temperature.TYPE_SKIN);
+ }
+
+ private IThermalEventListener captureThermalEventListener(int type) throws RemoteException {
+ ArgumentCaptor<IThermalEventListener> captor = ArgumentCaptor.forClass(
+ IThermalEventListener.class);
+ verify(mMockThermalService).registerThermalEventListenerWithType(captor.capture(), eq(
+ type));
+ return captor.getValue();
+ }
+
+ private Temperature createDisplayTemperature(
+ @NonNull String sensorName, @Temperature.ThrottlingStatus int status) {
+ return new Temperature(100, Temperature.TYPE_DISPLAY, sensorName, status);
+ }
+
+ private Temperature createSkinTemperature(@Temperature.ThrottlingStatus int status) {
+ return new Temperature(100, Temperature.TYPE_SKIN, "test_temperature", status);
+ }
+
+ private void overrideThrottlingData(String data) {
+ mFakeDeviceConfigInterface.putProperty(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ DisplayManager.DeviceConfig.KEY_BRIGHTNESS_THROTTLING_DATA, data);
+ }
+
+ private void onDisplayChange(List<ThrottlingLevel> throttlingLevels) {
+ Map<String, ThermalBrightnessThrottlingData> throttlingLevelsMap = new HashMap<>();
+ throttlingLevelsMap.put(DisplayDeviceConfig.DEFAULT_ID,
+ ThermalBrightnessThrottlingData.create(throttlingLevels));
+ when(mMockDisplayDeviceConfig.getThermalBrightnessThrottlingDataMapByThrottlingId())
+ .thenReturn(throttlingLevelsMap);
+ mModifier.onDisplayChanged(ClamperTestUtilsKt.createDisplayDeviceData(
+ mMockDisplayDeviceConfig, mMockBinder, DISPLAY_ID, DisplayDeviceConfig.DEFAULT_ID));
+ }
+
+ private void assertModifierState(
+ float currentBrightness,
+ boolean currentSlowChange,
+ float maxBrightness, float brightness,
+ boolean isActive,
+ boolean isSlowChange) {
+ ModifiersAggregatedState modifierState = new ModifiersAggregatedState();
+ DisplayBrightnessState.Builder stateBuilder = DisplayBrightnessState.builder();
+ stateBuilder.setBrightness(currentBrightness);
+ stateBuilder.setIsSlowChange(currentSlowChange);
+
+ int maxBrightnessReason = isActive ? BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL
+ : BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE;
+ int modifier = isActive ? BrightnessReason.MODIFIER_THROTTLED : NO_MODIFIER;
+
+ mModifier.applyStateChange(modifierState);
+ assertThat(modifierState.mMaxBrightness).isEqualTo(maxBrightness);
+ assertThat(modifierState.mMaxBrightnessReason).isEqualTo(maxBrightnessReason);
+
+ mModifier.apply(mMockRequest, stateBuilder);
+
+ assertThat(stateBuilder.getMaxBrightness()).isWithin(FLOAT_TOLERANCE).of(maxBrightness);
+ assertThat(stateBuilder.getBrightness()).isWithin(FLOAT_TOLERANCE).of(brightness);
+ assertThat(stateBuilder.getBrightnessMaxReason()).isEqualTo(maxBrightnessReason);
+ assertThat(stateBuilder.getBrightnessReason().getModifier()).isEqualTo(modifier);
+ assertThat(stateBuilder.isSlowChange()).isEqualTo(isSlowChange);
+ }
+
+
+ private class TestInjector extends BrightnessThermalModifier.Injector {
+ @Override
+ IThermalService getThermalService() {
+ return mMockThermalService;
+ }
+
+ @Override
+ DeviceConfigParameterProvider getDeviceConfigParameterProvider() {
+ return new DeviceConfigParameterProvider(mFakeDeviceConfigInterface);
+ }
+ }
+}
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/ClamperTestUtils.kt b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/ClamperTestUtils.kt
index 5fd848f..f21749e 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/ClamperTestUtils.kt
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/ClamperTestUtils.kt
@@ -21,6 +21,7 @@
import com.android.server.display.DisplayDeviceConfig
import com.android.server.display.brightness.clamper.BrightnessClamperController.DisplayDeviceData
+@JvmOverloads
fun createDisplayDeviceData(
displayDeviceConfig: DisplayDeviceConfig,
displayToken: IBinder,
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
index 1cd61e9..e5005d1 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/gestures/TouchExplorerTest.java
@@ -44,6 +44,8 @@
import android.graphics.PointF;
import android.os.Looper;
import android.os.SystemClock;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.testing.DexmakerShareClassLoaderRule;
import android.view.InputDevice;
import android.view.MotionEvent;
@@ -56,6 +58,7 @@
import com.android.server.accessibility.AccessibilityManagerService;
import com.android.server.accessibility.AccessibilityTraceManager;
import com.android.server.accessibility.EventStreamTransformation;
+import com.android.server.accessibility.Flags;
import com.android.server.accessibility.utils.GestureLogParser;
import com.android.server.testutils.OffsettableClock;
@@ -119,6 +122,9 @@
public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
new DexmakerShareClassLoaderRule();
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
/**
* {@link TouchExplorer#sendDownForAllNotInjectedPointers} injecting events with the same object
* is resulting {@link ArgumentCaptor} to capture events with last state. Before implementation
@@ -154,18 +160,43 @@
mHandler = new TestHandler();
mTouchExplorer = new TouchExplorer(mContext, mMockAms, null, mHandler);
mTouchExplorer.setNext(mCaptor);
+ // Start TouchExplorer in the state where it has already reset InputDispatcher so that
+ // all tests do not start with an irrelevant ACTION_CANCEL.
+ mTouchExplorer.setHasResetInputDispatcherState(true);
}
@Test
public void testOneFingerMove_shouldInjectHoverEvents() {
- goFromStateClearTo(STATE_TOUCH_EXPLORING_1FINGER);
- // Wait for transiting to touch exploring state.
+ triggerTouchExplorationWithOneFingerDownMoveUp();
+ assertCapturedEvents(ACTION_HOVER_ENTER, ACTION_HOVER_MOVE, ACTION_HOVER_EXIT);
+ assertState(STATE_TOUCH_EXPLORING);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_RESET_INPUT_DISPATCHER_BEFORE_FIRST_TOUCH_EXPLORATION)
+ public void testStartTouchExploration_shouldResetInputDispatcherStateWithActionCancel() {
+ // Start TouchExplorer in the state where it has *not yet* reset InputDispatcher.
+ mTouchExplorer.setHasResetInputDispatcherState(false);
+ // Trigger touch exploration twice, with a handler fast-forward in between so TouchExplorer
+ // treats these as two separate interactions.
+ triggerTouchExplorationWithOneFingerDownMoveUp();
+ mHandler.fastForward(2 * USER_INTENT_TIMEOUT);
+ triggerTouchExplorationWithOneFingerDownMoveUp();
+
+ assertCapturedEvents(
+ ACTION_CANCEL, // Only one ACTION_CANCEL before the first touch exploration
+ ACTION_HOVER_ENTER, ACTION_HOVER_MOVE, ACTION_HOVER_EXIT,
+ ACTION_HOVER_ENTER, ACTION_HOVER_MOVE, ACTION_HOVER_EXIT);
+ assertState(STATE_TOUCH_EXPLORING);
+ }
+
+ private void triggerTouchExplorationWithOneFingerDownMoveUp() {
+ send(downEvent());
+ // Fast forward so that TouchExplorer's timeouts transition us to the touch exploring state.
mHandler.fastForward(2 * USER_INTENT_TIMEOUT);
moveEachPointers(mLastEvent, p(10, 10));
send(mLastEvent);
- goToStateClearFrom(STATE_TOUCH_EXPLORING_1FINGER);
- assertCapturedEvents(ACTION_HOVER_ENTER, ACTION_HOVER_MOVE, ACTION_HOVER_EXIT);
- assertState(STATE_TOUCH_EXPLORING);
+ send(upEvent());
}
/**
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java b/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java
index 6ace9f1..fca0cfb 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/BaseAbsoluteVolumeBehaviorTest.java
@@ -20,6 +20,7 @@
import static com.android.server.hdmi.HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP;
import static com.android.server.hdmi.HdmiControlService.INITIATED_BY_BOOT_UP;
+import static com.android.server.hdmi.HdmiCecFeatureAction.DELAY_GIVE_AUDIO_STATUS;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.TruthJUnit.assume;
@@ -582,6 +583,9 @@
);
mTestLooper.dispatchAll();
+ mTestLooper.moveTimeForward(DELAY_GIVE_AUDIO_STATUS);
+ mTestLooper.dispatchAll();
+
assertThat(mNativeWrapper.getResultMessages()).contains(
HdmiCecMessageBuilder.buildUserControlPressed(getLogicalAddress(),
getSystemAudioDeviceLogicalAddress(), CEC_KEYCODE_VOLUME_UP));
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/BaseTvToAudioSystemAvbTest.java b/services/tests/servicestests/src/com/android/server/hdmi/BaseTvToAudioSystemAvbTest.java
index 6731403..ec44a91 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/BaseTvToAudioSystemAvbTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/BaseTvToAudioSystemAvbTest.java
@@ -17,6 +17,7 @@
package com.android.server.hdmi;
import static com.android.server.hdmi.HdmiCecKeycode.CEC_KEYCODE_VOLUME_UP;
+import static com.android.server.hdmi.HdmiCecFeatureAction.DELAY_GIVE_AUDIO_STATUS;
import static com.google.common.truth.Truth.assertThat;
@@ -223,6 +224,9 @@
);
mTestLooper.dispatchAll();
+ mTestLooper.moveTimeForward(DELAY_GIVE_AUDIO_STATUS);
+ mTestLooper.dispatchAll();
+
assertThat(mNativeWrapper.getResultMessages()).contains(
HdmiCecMessageBuilder.buildUserControlPressed(getLogicalAddress(),
getSystemAudioDeviceLogicalAddress(), CEC_KEYCODE_VOLUME_UP));
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index f0850af..51e0c33 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -1456,21 +1456,21 @@
public static final int SERVICE_CAPABILITY_MAX = SERVICE_CAPABILITY_DATA;
/**
- * Bitmask for {@code SERVICE_CAPABILITY_VOICE}.
+ * Bitmask for {@link #SERVICE_CAPABILITY_VOICE}.
* @hide
*/
public static final int SERVICE_CAPABILITY_VOICE_BITMASK =
serviceCapabilityToBitmask(SERVICE_CAPABILITY_VOICE);
/**
- * Bitmask for {@code SERVICE_CAPABILITY_SMS}.
+ * Bitmask for {@link #SERVICE_CAPABILITY_SMS}.
* @hide
*/
public static final int SERVICE_CAPABILITY_SMS_BITMASK =
serviceCapabilityToBitmask(SERVICE_CAPABILITY_SMS);
/**
- * Bitmask for {@code SERVICE_CAPABILITY_DATA}.
+ * Bitmask for {@link #SERVICE_CAPABILITY_DATA}.
* @hide
*/
public static final int SERVICE_CAPABILITY_DATA_BITMASK =
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 3ff1e2c..3e226cc 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -6907,7 +6907,6 @@
return false;
}
- // TODO(b/316183370): replace all @code with @link in javadoc after feature is released
/**
* @return true if the current device is "voice capable".
* <p>
@@ -6921,10 +6920,10 @@
* PackageManager.FEATURE_TELEPHONY system feature, which is available
* on any device with a telephony radio, even if the device is
* data-only.
- * @deprecated Replaced by {@code #isDeviceVoiceCapable()}. Starting from Android 15, voice
+ * @deprecated Replaced by {@link #isDeviceVoiceCapable()}. Starting from Android 15, voice
* capability may also be overridden by carriers for a given subscription. For voice capable
- * device (when {@code #isDeviceVoiceCapable} return {@code true}), caller should check for
- * subscription-level voice capability as well. See {@code #isDeviceVoiceCapable} for details.
+ * device (when {@link #isDeviceVoiceCapable} return {@code true}), caller should check for
+ * subscription-level voice capability as well. See {@link #isDeviceVoiceCapable} for details.
*/
@Deprecated
public boolean isVoiceCapable() {
@@ -6946,8 +6945,8 @@
* <p>
* Starting from Android 15, voice capability may also be overridden by carrier for a given
* subscription on a voice capable device. To check if a subscription is "voice capable",
- * call method {@code SubscriptionInfo#getServiceCapabilities()} and check if
- * {@code SubscriptionManager#SERVICE_CAPABILITY_VOICE} is included.
+ * call method {@link SubscriptionInfo#getServiceCapabilities()} and check if
+ * {@link SubscriptionManager#SERVICE_CAPABILITY_VOICE} is included.
*
* @see SubscriptionInfo#getServiceCapabilities()
*/
@@ -6964,10 +6963,10 @@
* <p>
* Note: Voicemail waiting sms, cell broadcasting sms, and MMS are
* disabled when device doesn't support sms.
- * @deprecated Replaced by {@code #isDeviceSmsCapable()}. Starting from Android 15, SMS
+ * @deprecated Replaced by {@link #isDeviceSmsCapable()}. Starting from Android 15, SMS
* capability may also be overridden by carriers for a given subscription. For SMS capable
- * device (when {@code #isDeviceSmsCapable} return {@code true}), caller should check for
- * subscription-level SMS capability as well. See {@code #isDeviceSmsCapable} for details.
+ * device (when {@link #isDeviceSmsCapable} return {@code true}), caller should check for
+ * subscription-level SMS capability as well. See {@link #isDeviceSmsCapable} for details.
*/
@Deprecated
public boolean isSmsCapable() {
@@ -6986,8 +6985,8 @@
* <p>
* Starting from Android 15, SMS capability may also be overridden by carriers for a given
* subscription on an SMS capable device. To check if a subscription is "SMS capable",
- * call method {@code SubscriptionInfo#getServiceCapabilities()} and check if
- * {@code SubscriptionManager#SERVICE_CAPABILITY_SMS} is included.
+ * call method {@link SubscriptionInfo#getServiceCapabilities()} and check if
+ * {@link SubscriptionManager#SERVICE_CAPABILITY_SMS} is included.
*
* @see SubscriptionInfo#getServiceCapabilities()
*/
diff --git a/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java b/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java
index 27cc923..189de6b 100644
--- a/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java
+++ b/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java
@@ -16,8 +16,6 @@
package com.android.internal.protolog;
-import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
@@ -30,7 +28,6 @@
import static java.io.File.createTempFile;
-import android.content.Context;
import android.os.SystemClock;
import android.platform.test.annotations.Presubmit;
import android.tools.ScenarioBuilder;
@@ -45,6 +42,7 @@
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.internal.protolog.ProtoLogConfigurationService.ViewerConfigFileTracer;
import com.android.internal.protolog.common.IProtoLogGroup;
import com.android.internal.protolog.common.LogDataType;
import com.android.internal.protolog.common.LogLevel;
@@ -53,11 +51,11 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
import perfetto.protos.Protolog;
import perfetto.protos.ProtologCommon;
@@ -76,6 +74,7 @@
@RunWith(JUnit4.class)
public class PerfettoProtoLogImplTest {
private static final String TEST_PROTOLOG_DATASOURCE_NAME = "test.android.protolog";
+ private static final String MOCK_VIEWER_CONFIG_FILE = "my/mock/viewer/config/file.pb";
private final File mTracingDirectory = InstrumentationRegistry.getInstrumentation()
.getTargetContext().getFilesDir();
@@ -92,29 +91,19 @@
new TraceConfig(false, true, false)
);
- private ProtoLogConfigurationService mProtoLogConfigurationService;
- private PerfettoProtoLogImpl mProtoLog;
- private Protolog.ProtoLogViewerConfig.Builder mViewerConfigBuilder;
- private File mFile;
- private Runnable mCacheUpdater;
+ private static ProtoLogConfigurationService sProtoLogConfigurationService;
+ private static PerfettoProtoLogImpl sProtoLog;
+ private static Protolog.ProtoLogViewerConfig.Builder sViewerConfigBuilder;
+ private static Runnable sCacheUpdater;
- private ProtoLogViewerConfigReader mReader;
+ private static ProtoLogViewerConfigReader sReader;
public PerfettoProtoLogImplTest() throws IOException {
}
- @Before
- public void setUp() throws Exception {
- MockitoAnnotations.initMocks(this);
- final Context testContext = getInstrumentation().getContext();
- mFile = testContext.getFileStreamPath("tracing_test.dat");
- //noinspection ResultOfMethodCallIgnored
- mFile.delete();
-
- TestProtoLogGroup.TEST_GROUP.setLogToLogcat(false);
- TestProtoLogGroup.TEST_GROUP.setLogToProto(false);
-
- mViewerConfigBuilder = Protolog.ProtoLogViewerConfig.newBuilder()
+ @BeforeClass
+ public static void setUp() throws Exception {
+ sViewerConfigBuilder = Protolog.ProtoLogViewerConfig.newBuilder()
.addGroups(
Protolog.ProtoLogViewerConfig.Group.newBuilder()
.setId(1)
@@ -160,33 +149,52 @@
ViewerConfigInputStreamProvider viewerConfigInputStreamProvider = Mockito.mock(
ViewerConfigInputStreamProvider.class);
Mockito.when(viewerConfigInputStreamProvider.getInputStream())
- .thenAnswer(it -> new ProtoInputStream(mViewerConfigBuilder.build().toByteArray()));
+ .thenAnswer(it -> new ProtoInputStream(sViewerConfigBuilder.build().toByteArray()));
- mCacheUpdater = () -> {};
- mReader = Mockito.spy(new ProtoLogViewerConfigReader(viewerConfigInputStreamProvider));
+ sCacheUpdater = () -> {};
+ sReader = Mockito.spy(new ProtoLogViewerConfigReader(viewerConfigInputStreamProvider));
final ProtoLogDataSourceBuilder dataSourceBuilder =
(onStart, onFlush, onStop) -> new ProtoLogDataSource(
onStart, onFlush, onStop, TEST_PROTOLOG_DATASOURCE_NAME);
- mProtoLogConfigurationService =
- new ProtoLogConfigurationService(dataSourceBuilder);
- mProtoLog = new PerfettoProtoLogImpl(
- viewerConfigInputStreamProvider, mReader, () -> mCacheUpdater.run(),
- TestProtoLogGroup.values(), dataSourceBuilder, mProtoLogConfigurationService);
+ final ViewerConfigFileTracer tracer = (dataSource, viewerConfigFilePath) -> {
+ Utils.dumpViewerConfig(dataSource, () -> {
+ if (!viewerConfigFilePath.equals(MOCK_VIEWER_CONFIG_FILE)) {
+ throw new RuntimeException(
+ "Unexpected viewer config file path provided");
+ }
+ return new ProtoInputStream(sViewerConfigBuilder.build().toByteArray());
+ });
+ };
+ sProtoLogConfigurationService = new ProtoLogConfigurationService(dataSourceBuilder, tracer);
+
+ if (android.tracing.Flags.clientSideProtoLogging()) {
+ sProtoLog = new PerfettoProtoLogImpl(
+ MOCK_VIEWER_CONFIG_FILE, sReader, () -> sCacheUpdater.run(),
+ TestProtoLogGroup.values(), dataSourceBuilder, sProtoLogConfigurationService);
+ } else {
+ sProtoLog = new PerfettoProtoLogImpl(
+ viewerConfigInputStreamProvider, sReader, () -> sCacheUpdater.run(),
+ TestProtoLogGroup.values(), dataSourceBuilder, sProtoLogConfigurationService);
+ }
+ }
+
+ @Before
+ public void before() {
+ Mockito.reset(sReader);
+
+ TestProtoLogGroup.TEST_GROUP.setLogToLogcat(false);
+ TestProtoLogGroup.TEST_GROUP.setLogToProto(false);
}
@After
public void tearDown() {
- if (mFile != null) {
- //noinspection ResultOfMethodCallIgnored
- mFile.delete();
- }
ProtoLogImpl.setSingleInstance(null);
}
@Test
public void isEnabled_returnsFalseByDefault() {
- assertFalse(mProtoLog.isProtoEnabled());
+ assertFalse(sProtoLog.isProtoEnabled());
}
@Test
@@ -196,7 +204,7 @@
.build();
try {
traceMonitor.start();
- assertTrue(mProtoLog.isProtoEnabled());
+ assertTrue(sProtoLog.isProtoEnabled());
} finally {
traceMonitor.stop(mWriter);
}
@@ -209,12 +217,12 @@
.build();
try {
traceMonitor.start();
- assertTrue(mProtoLog.isProtoEnabled());
+ assertTrue(sProtoLog.isProtoEnabled());
} finally {
traceMonitor.stop(mWriter);
}
- assertFalse(mProtoLog.isProtoEnabled());
+ assertFalse(sProtoLog.isProtoEnabled());
}
@Test
@@ -226,15 +234,15 @@
traceMonitor.start();
// Shouldn't be logging anything except WTF unless explicitly requested in the group
// override.
- mProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1,
+ sProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1,
LogDataType.BOOLEAN, new Object[]{true});
- mProtoLog.log(LogLevel.VERBOSE, TestProtoLogGroup.TEST_GROUP, 2,
+ sProtoLog.log(LogLevel.VERBOSE, TestProtoLogGroup.TEST_GROUP, 2,
LogDataType.BOOLEAN, new Object[]{true});
- mProtoLog.log(LogLevel.WARN, TestProtoLogGroup.TEST_GROUP, 3,
+ sProtoLog.log(LogLevel.WARN, TestProtoLogGroup.TEST_GROUP, 3,
LogDataType.BOOLEAN, new Object[]{true});
- mProtoLog.log(LogLevel.ERROR, TestProtoLogGroup.TEST_GROUP, 4,
+ sProtoLog.log(LogLevel.ERROR, TestProtoLogGroup.TEST_GROUP, 4,
LogDataType.BOOLEAN, new Object[]{true});
- mProtoLog.log(LogLevel.WTF, TestProtoLogGroup.TEST_GROUP, 5,
+ sProtoLog.log(LogLevel.WTF, TestProtoLogGroup.TEST_GROUP, 5,
LogDataType.BOOLEAN, new Object[]{true});
} finally {
traceMonitor.stop(mWriter);
@@ -258,15 +266,15 @@
).build();
try {
traceMonitor.start();
- mProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1,
+ sProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1,
LogDataType.BOOLEAN, new Object[]{true});
- mProtoLog.log(LogLevel.VERBOSE, TestProtoLogGroup.TEST_GROUP, 2,
+ sProtoLog.log(LogLevel.VERBOSE, TestProtoLogGroup.TEST_GROUP, 2,
LogDataType.BOOLEAN, new Object[]{true});
- mProtoLog.log(LogLevel.WARN, TestProtoLogGroup.TEST_GROUP, 3,
+ sProtoLog.log(LogLevel.WARN, TestProtoLogGroup.TEST_GROUP, 3,
LogDataType.BOOLEAN, new Object[]{true});
- mProtoLog.log(LogLevel.ERROR, TestProtoLogGroup.TEST_GROUP, 4,
+ sProtoLog.log(LogLevel.ERROR, TestProtoLogGroup.TEST_GROUP, 4,
LogDataType.BOOLEAN, new Object[]{true});
- mProtoLog.log(LogLevel.WTF, TestProtoLogGroup.TEST_GROUP, 5,
+ sProtoLog.log(LogLevel.WTF, TestProtoLogGroup.TEST_GROUP, 5,
LogDataType.BOOLEAN, new Object[]{true});
} finally {
traceMonitor.stop(mWriter);
@@ -294,15 +302,15 @@
).build();
try {
traceMonitor.start();
- mProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1,
+ sProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1,
LogDataType.BOOLEAN, new Object[]{true});
- mProtoLog.log(LogLevel.VERBOSE, TestProtoLogGroup.TEST_GROUP, 2,
+ sProtoLog.log(LogLevel.VERBOSE, TestProtoLogGroup.TEST_GROUP, 2,
LogDataType.BOOLEAN, new Object[]{true});
- mProtoLog.log(LogLevel.WARN, TestProtoLogGroup.TEST_GROUP, 3,
+ sProtoLog.log(LogLevel.WARN, TestProtoLogGroup.TEST_GROUP, 3,
LogDataType.BOOLEAN, new Object[]{true});
- mProtoLog.log(LogLevel.ERROR, TestProtoLogGroup.TEST_GROUP, 4,
+ sProtoLog.log(LogLevel.ERROR, TestProtoLogGroup.TEST_GROUP, 4,
LogDataType.BOOLEAN, new Object[]{true});
- mProtoLog.log(LogLevel.WTF, TestProtoLogGroup.TEST_GROUP, 5,
+ sProtoLog.log(LogLevel.WTF, TestProtoLogGroup.TEST_GROUP, 5,
LogDataType.BOOLEAN, new Object[]{true});
} finally {
traceMonitor.stop(mWriter);
@@ -324,15 +332,15 @@
.build();
try {
traceMonitor.start();
- mProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1,
+ sProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1,
LogDataType.BOOLEAN, new Object[]{true});
- mProtoLog.log(LogLevel.VERBOSE, TestProtoLogGroup.TEST_GROUP, 2,
+ sProtoLog.log(LogLevel.VERBOSE, TestProtoLogGroup.TEST_GROUP, 2,
LogDataType.BOOLEAN, new Object[]{true});
- mProtoLog.log(LogLevel.WARN, TestProtoLogGroup.TEST_GROUP, 3,
+ sProtoLog.log(LogLevel.WARN, TestProtoLogGroup.TEST_GROUP, 3,
LogDataType.BOOLEAN, new Object[]{true});
- mProtoLog.log(LogLevel.ERROR, TestProtoLogGroup.TEST_GROUP, 4,
+ sProtoLog.log(LogLevel.ERROR, TestProtoLogGroup.TEST_GROUP, 4,
LogDataType.BOOLEAN, new Object[]{true});
- mProtoLog.log(LogLevel.WTF, TestProtoLogGroup.TEST_GROUP, 5,
+ sProtoLog.log(LogLevel.WTF, TestProtoLogGroup.TEST_GROUP, 5,
LogDataType.BOOLEAN, new Object[]{true});
} finally {
traceMonitor.stop(mWriter);
@@ -351,8 +359,8 @@
@Test
public void log_logcatEnabled() {
- when(mReader.getViewerString(anyLong())).thenReturn("test %b %d %% 0x%x %s %f");
- PerfettoProtoLogImpl implSpy = Mockito.spy(mProtoLog);
+ when(sReader.getViewerString(anyLong())).thenReturn("test %b %d %% 0x%x %s %f");
+ PerfettoProtoLogImpl implSpy = Mockito.spy(sProtoLog);
TestProtoLogGroup.TEST_GROUP.setLogToLogcat(true);
TestProtoLogGroup.TEST_GROUP.setLogToProto(false);
@@ -363,13 +371,13 @@
verify(implSpy).passToLogcat(eq(TestProtoLogGroup.TEST_GROUP.getTag()), eq(
LogLevel.INFO),
eq("test true 10000 % 0x7530 test 3.0E-6"));
- verify(mReader).getViewerString(eq(1234L));
+ verify(sReader).getViewerString(eq(1234L));
}
@Test
public void log_logcatEnabledInvalidMessage() {
- when(mReader.getViewerString(anyLong())).thenReturn("test %b %d %% %x %s %f");
- PerfettoProtoLogImpl implSpy = Mockito.spy(mProtoLog);
+ when(sReader.getViewerString(anyLong())).thenReturn("test %b %d %% %x %s %f");
+ PerfettoProtoLogImpl implSpy = Mockito.spy(sProtoLog);
TestProtoLogGroup.TEST_GROUP.setLogToLogcat(true);
TestProtoLogGroup.TEST_GROUP.setLogToProto(false);
@@ -381,29 +389,32 @@
LogLevel.INFO),
eq("FORMAT_ERROR \"test %b %d %% %x %s %f\", "
+ "args=(true, 10000, 1.0E-4, 2.0E-5, test)"));
- verify(mReader).getViewerString(eq(1234L));
+ verify(sReader).getViewerString(eq(1234L));
}
@Test
public void log_logcatEnabledNoMessage() {
- when(mReader.getViewerString(anyLong())).thenReturn(null);
- PerfettoProtoLogImpl implSpy = Mockito.spy(mProtoLog);
+ when(sReader.getViewerString(anyLong())).thenReturn(null);
+ PerfettoProtoLogImpl implSpy = Mockito.spy(sProtoLog);
TestProtoLogGroup.TEST_GROUP.setLogToLogcat(true);
TestProtoLogGroup.TEST_GROUP.setLogToProto(false);
- implSpy.log(
+ var assertion = assertThrows(RuntimeException.class, () -> implSpy.log(
LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, 4321,
- new Object[]{5});
+ new Object[]{5}));
- verify(implSpy).passToLogcat(eq(TestProtoLogGroup.TEST_GROUP.getTag()), eq(
- LogLevel.INFO), eq("UNKNOWN MESSAGE args = (5)"));
- verify(mReader).getViewerString(eq(1234L));
+ verify(implSpy, never()).passToLogcat(eq(TestProtoLogGroup.TEST_GROUP.getTag()), eq(
+ LogLevel.INFO), any());
+ verify(sReader).getViewerString(eq(1234L));
+
+ Truth.assertThat(assertion).hasMessageThat()
+ .contains("Failed to get log message with hash 1234 and args (5)");
}
@Test
public void log_logcatDisabled() {
- when(mReader.getViewerString(anyLong())).thenReturn("test %d");
- PerfettoProtoLogImpl implSpy = Mockito.spy(mProtoLog);
+ when(sReader.getViewerString(anyLong())).thenReturn("test %d");
+ PerfettoProtoLogImpl implSpy = Mockito.spy(sProtoLog);
TestProtoLogGroup.TEST_GROUP.setLogToLogcat(false);
implSpy.log(
@@ -411,7 +422,7 @@
new Object[]{5});
verify(implSpy, never()).passToLogcat(any(), any(), any());
- verify(mReader, never()).getViewerString(anyLong());
+ verify(sReader, never()).getViewerString(anyLong());
}
@Test
@@ -426,11 +437,12 @@
long before;
long after;
try {
+ assertFalse(sProtoLog.isProtoEnabled());
traceMonitor.start();
- assertTrue(mProtoLog.isProtoEnabled());
+ assertTrue(sProtoLog.isProtoEnabled());
before = SystemClock.elapsedRealtimeNanos();
- mProtoLog.log(
+ sProtoLog.log(
LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, messageHash,
0b1110101001010100,
new Object[]{"test", 1, 2, 3, 0.4, 0.5, 0.6, true});
@@ -448,7 +460,8 @@
Truth.assertThat(protolog.messages.getFirst().getTimestamp().getElapsedNanos())
.isAtMost(after);
Truth.assertThat(protolog.messages.getFirst().getMessage())
- .isEqualTo("My test message :: test, 2, 4, 6, 0.400000, 5.000000e-01, 0.6, true");
+ .isEqualTo(
+ "My test message :: test, 1, 2, 3, 0.400000, 5.000000e-01, 0.6, true");
}
@Test
@@ -460,10 +473,10 @@
long after;
try {
traceMonitor.start();
- assertTrue(mProtoLog.isProtoEnabled());
+ assertTrue(sProtoLog.isProtoEnabled());
before = SystemClock.elapsedRealtimeNanos();
- mProtoLog.log(
+ sProtoLog.log(
LogLevel.INFO, TestProtoLogGroup.TEST_GROUP,
"My test message :: %s, %d, %x, %f, %b",
"test", 1, 3, 0.4, true);
@@ -481,7 +494,7 @@
Truth.assertThat(protolog.messages.getFirst().getTimestamp().getElapsedNanos())
.isAtMost(after);
Truth.assertThat(protolog.messages.getFirst().getMessage())
- .isEqualTo("My test message :: test, 2, 6, 0.400000, true");
+ .isEqualTo("My test message :: test, 1, 3, 0.400000, true");
}
@Test
@@ -491,7 +504,7 @@
.build();
try {
traceMonitor.start();
- mProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1,
+ sProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1,
LogDataType.BOOLEAN, new Object[]{true});
} finally {
traceMonitor.stop(mWriter);
@@ -507,7 +520,7 @@
private long addMessageToConfig(ProtologCommon.ProtoLogLevel logLevel, String message) {
final long messageId = new Random().nextLong();
- mViewerConfigBuilder.addMessages(Protolog.ProtoLogViewerConfig.MessageData.newBuilder()
+ sViewerConfigBuilder.addMessages(Protolog.ProtoLogViewerConfig.MessageData.newBuilder()
.setMessageId(messageId)
.setMessage(message)
.setLevel(logLevel)
@@ -530,7 +543,7 @@
try {
traceMonitor.start();
before = SystemClock.elapsedRealtimeNanos();
- mProtoLog.log(
+ sProtoLog.log(
LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, messageHash,
0b01100100,
new Object[]{"test", 1, 0.1, true});
@@ -550,7 +563,7 @@
.build();
try {
traceMonitor.start();
- mProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1,
+ sProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1,
0b11, new Object[]{true});
} finally {
traceMonitor.stop(mWriter);
@@ -575,7 +588,7 @@
try {
traceMonitor.start();
- ProtoLogImpl.setSingleInstance(mProtoLog);
+ ProtoLogImpl.setSingleInstance(sProtoLog);
ProtoLogImpl.d(TestProtoLogGroup.TEST_GROUP, 1,
0b11, true);
} finally {
@@ -599,7 +612,7 @@
@Test
public void cacheIsUpdatedWhenTracesStartAndStop() {
final AtomicInteger cacheUpdateCallCount = new AtomicInteger(0);
- mCacheUpdater = cacheUpdateCallCount::incrementAndGet;
+ sCacheUpdater = cacheUpdateCallCount::incrementAndGet;
PerfettoTraceMonitor traceMonitor1 = PerfettoTraceMonitor.newBuilder()
.enableProtoLog(true,
@@ -641,17 +654,17 @@
@Test
public void isEnabledUpdatesBasedOnRunningTraces() {
- Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.DEBUG))
+ Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.DEBUG))
.isFalse();
- Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.VERBOSE))
+ Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.VERBOSE))
.isFalse();
- Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.INFO))
+ Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.INFO))
.isFalse();
- Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WARN))
+ Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WARN))
.isFalse();
- Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.ERROR))
+ Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.ERROR))
.isFalse();
- Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WTF)).isFalse();
+ Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WTF)).isFalse();
PerfettoTraceMonitor traceMonitor1 =
PerfettoTraceMonitor.newBuilder().enableProtoLog(true,
@@ -670,65 +683,65 @@
try {
traceMonitor1.start();
- Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.DEBUG))
+ Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.DEBUG))
.isFalse();
- Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.VERBOSE))
+ Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.VERBOSE))
.isFalse();
- Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.INFO))
+ Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.INFO))
.isFalse();
- Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WARN))
+ Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WARN))
.isTrue();
- Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.ERROR))
+ Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.ERROR))
.isTrue();
- Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WTF))
+ Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WTF))
.isTrue();
try {
traceMonitor2.start();
- Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.DEBUG))
+ Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.DEBUG))
.isTrue();
- Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP,
+ Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP,
LogLevel.VERBOSE)).isTrue();
- Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.INFO))
+ Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.INFO))
.isTrue();
- Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WARN))
+ Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WARN))
.isTrue();
- Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.ERROR))
+ Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.ERROR))
.isTrue();
- Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WTF))
+ Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WTF))
.isTrue();
} finally {
traceMonitor2.stop(mWriter);
}
- Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.DEBUG))
+ Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.DEBUG))
.isFalse();
- Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.VERBOSE))
+ Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.VERBOSE))
.isFalse();
- Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.INFO))
+ Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.INFO))
.isFalse();
- Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WARN))
+ Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WARN))
.isTrue();
- Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.ERROR))
+ Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.ERROR))
.isTrue();
- Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WTF))
+ Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WTF))
.isTrue();
} finally {
traceMonitor1.stop(mWriter);
}
- Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.DEBUG))
+ Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.DEBUG))
.isFalse();
- Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.VERBOSE))
+ Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.VERBOSE))
.isFalse();
- Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.INFO))
+ Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.INFO))
.isFalse();
- Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WARN))
+ Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WARN))
.isFalse();
- Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.ERROR))
+ Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.ERROR))
.isFalse();
- Truth.assertThat(mProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WTF))
+ Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WTF))
.isFalse();
}
@@ -741,7 +754,7 @@
try {
traceMonitor.start();
- mProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP,
+ sProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP,
"My test null string: %s", (Object) null);
} finally {
traceMonitor.stop(mWriter);
@@ -764,7 +777,7 @@
try {
traceMonitor.start();
- mProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP,
+ sProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP,
"My null args: %d, %f, %b", null, null, null);
} finally {
traceMonitor.stop(mWriter);
@@ -775,7 +788,7 @@
Truth.assertThat(protolog.messages).hasSize(1);
Truth.assertThat(protolog.messages.get(0).getMessage())
- .isEqualTo("My null args: 0, 0, false");
+ .isEqualTo("My null args: 0, 0.000000, false");
}
@Test
@@ -798,7 +811,7 @@
traceMonitor1.start();
traceMonitor2.start();
- mProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1,
+ sProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1,
LogDataType.BOOLEAN, new Object[]{true});
} finally {
traceMonitor1.stop(mWriter);
@@ -827,12 +840,12 @@
.build();
try {
traceMonitor.start();
- mProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP,
- "This message should not be logged");
- mProtoLog.log(LogLevel.WARN, TestProtoLogGroup.TEST_GROUP,
- "This message should logged %d", 123);
- mProtoLog.log(LogLevel.ERROR, TestProtoLogGroup.TEST_GROUP,
- "This message should also be logged %d", 567);
+ sProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP,
+ "This message should not be logged");
+ sProtoLog.log(LogLevel.WARN, TestProtoLogGroup.TEST_GROUP,
+ "This message should be logged %d", 123);
+ sProtoLog.log(LogLevel.ERROR, TestProtoLogGroup.TEST_GROUP,
+ "This message should also be logged %d", 567);
} finally {
traceMonitor.stop(mWriter);
}
@@ -845,7 +858,7 @@
Truth.assertThat(protolog.messages.get(0).getLevel())
.isEqualTo(LogLevel.WARN);
Truth.assertThat(protolog.messages.get(0).getMessage())
- .isEqualTo("This message should logged 123");
+ .isEqualTo("This message should be logged 123");
Truth.assertThat(protolog.messages.get(1).getLevel())
.isEqualTo(LogLevel.ERROR);
@@ -853,6 +866,19 @@
.isEqualTo("This message should also be logged 567");
}
+ @Test
+ public void throwsOnLogToLogcatForProcessedMessageMissingLoadedDefinition() {
+ TestProtoLogGroup.TEST_GROUP.setLogToLogcat(true);
+ var protolog = new PerfettoProtoLogImpl(TestProtoLogGroup.values());
+
+ var exception = assertThrows(RuntimeException.class, () -> {
+ protolog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 123, 0, new Object[0]);
+ });
+
+ Truth.assertThat(exception).hasMessageThat()
+ .contains("Failed to get log message with hash 123");
+ }
+
private enum TestProtoLogGroup implements IProtoLogGroup {
TEST_GROUP(true, true, false, "TEST_TAG");
diff --git a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
index 887630b..b5cc553 100644
--- a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
+++ b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java
@@ -59,7 +59,6 @@
import android.os.ParcelUuid;
import android.os.PersistableBundle;
import android.os.test.TestLooper;
-import android.platform.test.flag.junit.SetFlagsRule;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
@@ -73,10 +72,7 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.internal.telephony.flags.Flags;
-
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -133,8 +129,6 @@
TEST_SUBID_TO_CARRIER_CONFIG_MAP = Collections.unmodifiableMap(subIdToCarrierConfigMap);
}
- @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
-
@NonNull private final Context mContext;
@NonNull private final TestLooper mTestLooper;
@@ -193,7 +187,6 @@
@Before
public void setUp() throws Exception {
- mSetFlagsRule.enableFlags(Flags.FLAG_FIX_CRASH_ON_GETTING_CONFIG_WHEN_PHONE_IS_GONE);
doReturn(2).when(mTelephonyManager).getActiveModemCount();
mCallback = mock(TelephonySubscriptionTrackerCallback.class);
diff --git a/tools/protologtool/src/com/android/protolog/tool/ViewerConfigProtoBuilder.kt b/tools/protologtool/src/com/android/protolog/tool/ViewerConfigProtoBuilder.kt
index 0115339..245e802 100644
--- a/tools/protologtool/src/com/android/protolog/tool/ViewerConfigProtoBuilder.kt
+++ b/tools/protologtool/src/com/android/protolog/tool/ViewerConfigProtoBuilder.kt
@@ -53,7 +53,7 @@
.setMessageId(key)
.setMessage(log.messageString)
.setLevel(
- ProtoLogLevel.forNumber(log.logLevel.ordinal + 1))
+ ProtoLogLevel.forNumber(log.logLevel.id))
.setGroupId(groupId)
.setLocation(log.position)
)