Merge "Updated BackgroundInstallControlService to use historical sessions to retrieve install start time." into main
diff --git a/AconfigFlags.bp b/AconfigFlags.bp
index 7fee810..a5e695a 100644
--- a/AconfigFlags.bp
+++ b/AconfigFlags.bp
@@ -22,6 +22,7 @@
"aconfig_mediacodec_flags_java_lib",
"android.adaptiveauth.flags-aconfig-java",
"android.app.flags-aconfig-java",
+ "android.app.ondeviceintelligence-aconfig-java",
"android.app.smartspace.flags-aconfig-java",
"android.app.usage.flags-aconfig-java",
"android.app.wearable.flags-aconfig-java",
@@ -37,6 +38,7 @@
"android.credentials.flags-aconfig-java",
"android.database.sqlite-aconfig-java",
"android.hardware.biometrics.flags-aconfig-java",
+ "android.hardware.devicestate.feature.flags-aconfig-java",
"android.hardware.flags-aconfig-java",
"android.hardware.radio.flags-aconfig-java",
"android.hardware.usb.flags-aconfig-java",
@@ -163,6 +165,19 @@
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+// DeviceStateManager
+aconfig_declarations {
+ name: "android.hardware.devicestate.feature.flags-aconfig",
+ package: "android.hardware.devicestate.feature.flags",
+ srcs: ["core/java/android/hardware/devicestate/feature/*.aconfig"],
+}
+
+java_aconfig_library {
+ name: "android.hardware.devicestate.feature.flags-aconfig-java",
+ aconfig_declarations: "android.hardware.devicestate.feature.flags-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
// Input
aconfig_declarations {
name: "com.android.hardware.input.input-aconfig",
@@ -531,6 +546,19 @@
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+// OnDeviceIntelligence
+aconfig_declarations {
+ name: "android.app.ondeviceintelligence-aconfig",
+ package: "android.app.ondeviceintelligence.flags",
+ srcs: ["core/java/android/app/ondeviceintelligence/flags/ondevice_intelligence.aconfig"],
+}
+
+java_aconfig_library {
+ name: "android.app.ondeviceintelligence-aconfig-java",
+ aconfig_declarations: "android.app.ondeviceintelligence-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+}
+
// Permissions
aconfig_declarations {
name: "android.permission.flags-aconfig",
@@ -882,6 +910,8 @@
aconfig_declarations {
name: "android.service.notification.flags-aconfig",
package: "android.service.notification",
+ exportable: true,
+ container: "system",
srcs: ["core/java/android/service/notification/flags.aconfig"],
}
@@ -891,6 +921,18 @@
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+java_aconfig_library {
+ name: "android.service.notification.flags-aconfig-export-java",
+ aconfig_declarations: "android.service.notification.flags-aconfig",
+ defaults: ["framework-minus-apex-aconfig-java-defaults"],
+ mode: "exported",
+ min_sdk_version: "30",
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.extservices",
+ ],
+}
+
// Smartspace
aconfig_declarations {
name: "android.app.smartspace.flags-aconfig",
@@ -957,6 +999,11 @@
defaults: ["framework-minus-apex-aconfig-java-defaults"],
}
+cc_aconfig_library {
+ name: "android.tracing.flags_c_lib",
+ aconfig_declarations: "android.tracing.flags-aconfig",
+}
+
// App Widgets
aconfig_declarations {
name: "android.appwidget.flags-aconfig",
diff --git a/Android.bp b/Android.bp
index 870df5a..019bf6508 100644
--- a/Android.bp
+++ b/Android.bp
@@ -508,7 +508,6 @@
lint: {
baseline_filename: "lint-baseline.xml",
},
- // For jarjar repackaging
jarjar_prefix: "com.android.internal.hidden_from_bootclasspath",
}
diff --git a/apct-tests/perftests/autofill/Android.bp b/apct-tests/perftests/autofill/Android.bp
index 84145be..2ff8b4e 100644
--- a/apct-tests/perftests/autofill/Android.bp
+++ b/apct-tests/perftests/autofill/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_input_framework",
// 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"
diff --git a/apct-tests/perftests/blobstore/Android.bp b/apct-tests/perftests/blobstore/Android.bp
index 2590fe3..e9353fe 100644
--- a/apct-tests/perftests/blobstore/Android.bp
+++ b/apct-tests/perftests/blobstore/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_input_framework",
// 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"
diff --git a/apct-tests/perftests/contentcapture/Android.bp b/apct-tests/perftests/contentcapture/Android.bp
index 638403d..5e559d7 100644
--- a/apct-tests/perftests/contentcapture/Android.bp
+++ b/apct-tests/perftests/contentcapture/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_input_framework",
// 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"
diff --git a/apct-tests/perftests/core/Android.bp b/apct-tests/perftests/core/Android.bp
index e1b3241..e092499 100644
--- a/apct-tests/perftests/core/Android.bp
+++ b/apct-tests/perftests/core/Android.bp
@@ -15,6 +15,7 @@
//
package {
+ default_team: "trendy_team_framework_accessibility",
// 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"
diff --git a/apct-tests/perftests/core/apps/overlay/Android.bp b/apct-tests/perftests/core/apps/overlay/Android.bp
index 6465307..2170fa7 100644
--- a/apct-tests/perftests/core/apps/overlay/Android.bp
+++ b/apct-tests/perftests/core/apps/overlay/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_framework_accessibility",
// 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"
@@ -25,149 +26,150 @@
name: "Overlay0",
aaptflags: [
"--rename-manifest-package com.android.perftests.overlay0",
- ]
+ ],
}
android_test_helper_app {
name: "Overlay1",
aaptflags: [
"--rename-manifest-package com.android.perftests.overlay1",
- ]
+ ],
}
android_test_helper_app {
name: "Overlay2",
aaptflags: [
"--rename-manifest-package com.android.perftests.overlay2",
- ]
+ ],
}
android_test_helper_app {
name: "Overlay3",
aaptflags: [
"--rename-manifest-package com.android.perftests.overlay3",
- ]
+ ],
}
android_test_helper_app {
name: "Overlay4",
aaptflags: [
"--rename-manifest-package com.android.perftests.overlay4",
- ]
+ ],
}
android_test_helper_app {
name: "Overlay5",
aaptflags: [
"--rename-manifest-package com.android.perftests.overlay5",
- ]
+ ],
}
android_test_helper_app {
name: "Overlay6",
aaptflags: [
"--rename-manifest-package com.android.perftests.overlay6",
- ]
+ ],
}
android_test_helper_app {
name: "Overlay7",
aaptflags: [
"--rename-manifest-package com.android.perftests.overlay7",
- ]
+ ],
}
+
android_test_helper_app {
name: "Overlay8",
aaptflags: [
"--rename-manifest-package com.android.perftests.overlay8",
- ]
+ ],
}
android_test_helper_app {
name: "Overlay9",
aaptflags: [
"--rename-manifest-package com.android.perftests.overlay9",
- ]
+ ],
}
android_test_helper_app {
name: "LargeOverlay0",
- resource_dirs : [ "res_large" ],
+ resource_dirs: ["res_large"],
aaptflags: [
"--rename-manifest-package com.android.perftests.overlay.large0",
- ]
+ ],
}
android_test_helper_app {
name: "LargeOverlay1",
- resource_dirs : [ "res_large" ],
+ resource_dirs: ["res_large"],
aaptflags: [
"--rename-manifest-package com.android.perftests.overlay.large1",
- ]
+ ],
}
android_test_helper_app {
name: "LargeOverlay2",
- resource_dirs : [ "res_large" ],
+ resource_dirs: ["res_large"],
aaptflags: [
"--rename-manifest-package com.android.perftests.overlay.large2",
- ]
+ ],
}
android_test_helper_app {
name: "LargeOverlay3",
- resource_dirs : [ "res_large" ],
+ resource_dirs: ["res_large"],
aaptflags: [
"--rename-manifest-package com.android.perftests.overlay.large3",
- ]
+ ],
}
android_test_helper_app {
name: "LargeOverlay4",
- resource_dirs : [ "res_large" ],
+ resource_dirs: ["res_large"],
aaptflags: [
"--rename-manifest-package com.android.perftests.overlay.large4",
- ]
+ ],
}
android_test_helper_app {
name: "LargeOverlay5",
- resource_dirs : [ "res_large" ],
+ resource_dirs: ["res_large"],
aaptflags: [
"--rename-manifest-package com.android.perftests.overlay.large5",
- ]
+ ],
}
android_test_helper_app {
name: "LargeOverlay6",
- resource_dirs : [ "res_large" ],
+ resource_dirs: ["res_large"],
aaptflags: [
"--rename-manifest-package com.android.perftests.overlay.large6",
- ]
+ ],
}
android_test_helper_app {
name: "LargeOverlay7",
- resource_dirs : [ "res_large" ],
+ resource_dirs: ["res_large"],
aaptflags: [
"--rename-manifest-package com.android.perftests.overlay.large7",
- ]
+ ],
}
android_test_helper_app {
name: "LargeOverlay8",
- resource_dirs : [ "res_large" ],
+ resource_dirs: ["res_large"],
aaptflags: [
"--rename-manifest-package com.android.perftests.overlay.large8",
- ]
+ ],
}
android_test_helper_app {
name: "LargeOverlay9",
- resource_dirs : [ "res_large" ],
+ resource_dirs: ["res_large"],
aaptflags: [
"--rename-manifest-package com.android.perftests.overlay.large9",
- ]
+ ],
}
java_library {
diff --git a/apct-tests/perftests/core/apps/reources_manager/Android.bp b/apct-tests/perftests/core/apps/reources_manager/Android.bp
index 766b8c4..96b9d6a 100644
--- a/apct-tests/perftests/core/apps/reources_manager/Android.bp
+++ b/apct-tests/perftests/core/apps/reources_manager/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_framework_accessibility",
// 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"
diff --git a/apct-tests/perftests/core/jni/Android.bp b/apct-tests/perftests/core/jni/Android.bp
index b92b13b..a9c9526 100644
--- a/apct-tests/perftests/core/jni/Android.bp
+++ b/apct-tests/perftests/core/jni/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_framework_accessibility",
// 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"
diff --git a/apct-tests/perftests/healthconnect/Android.bp b/apct-tests/perftests/healthconnect/Android.bp
index c2d0a6f..c38a24e 100644
--- a/apct-tests/perftests/healthconnect/Android.bp
+++ b/apct-tests/perftests/healthconnect/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_input_framework",
// 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"
diff --git a/apct-tests/perftests/inputmethod/Android.bp b/apct-tests/perftests/inputmethod/Android.bp
index f2f1f75..7fcfdea 100644
--- a/apct-tests/perftests/inputmethod/Android.bp
+++ b/apct-tests/perftests/inputmethod/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_input_method_framework",
// 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"
diff --git a/apct-tests/perftests/multiuser/Android.bp b/apct-tests/perftests/multiuser/Android.bp
index 45c6b8c..1653edc 100644
--- a/apct-tests/perftests/multiuser/Android.bp
+++ b/apct-tests/perftests/multiuser/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_input_framework",
// 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"
diff --git a/apct-tests/perftests/multiuser/apps/dummyapp/Android.bp b/apct-tests/perftests/multiuser/apps/dummyapp/Android.bp
index 892c140..022655d 100644
--- a/apct-tests/perftests/multiuser/apps/dummyapp/Android.bp
+++ b/apct-tests/perftests/multiuser/apps/dummyapp/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_input_framework",
// 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"
diff --git a/apct-tests/perftests/packagemanager/Android.bp b/apct-tests/perftests/packagemanager/Android.bp
index b6ea54d..02fc12c 100644
--- a/apct-tests/perftests/packagemanager/Android.bp
+++ b/apct-tests/perftests/packagemanager/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_input_framework",
// 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"
diff --git a/apct-tests/perftests/packagemanager/apps/query-all/Android.bp b/apct-tests/perftests/packagemanager/apps/query-all/Android.bp
index b2339d5..6984936 100644
--- a/apct-tests/perftests/packagemanager/apps/query-all/Android.bp
+++ b/apct-tests/perftests/packagemanager/apps/query-all/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_input_framework",
// 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"
@@ -25,299 +26,348 @@
name: "QueriesAll0",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration0",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll1",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration1",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll2",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration2",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll3",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration3",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll4",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration4",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll5",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration5",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll6",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration6",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll7",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration7",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll8",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration8",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll9",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration9",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll10",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration10",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll11",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration11",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll12",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration12",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll13",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration13",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll14",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration14",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll15",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration15",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll16",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration16",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll17",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration17",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll18",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration18",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll19",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration19",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll20",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration20",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll21",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration21",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll22",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration22",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll23",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration23",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll24",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration24",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll25",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration25",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll26",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration26",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll27",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration27",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll28",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration28",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll29",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration29",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll30",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration30",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll31",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration31",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll32",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration32",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll33",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration33",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll34",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration34",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll35",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration35",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll36",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration36",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll37",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration37",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll38",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration38",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll39",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration39",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll40",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration40",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll41",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration41",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll42",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration42",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll43",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration43",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll44",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration44",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll45",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration45",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll46",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration46",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll47",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration47",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll48",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration48",
- ]
+ ],
}
+
android_test_helper_app {
name: "QueriesAll49",
aaptflags: [
"--rename-manifest-package com.android.perftests.appenumeration49",
- ]
+ ],
}
diff --git a/apct-tests/perftests/permission/Android.bp b/apct-tests/perftests/permission/Android.bp
index b80a6af..bc8e769 100644
--- a/apct-tests/perftests/permission/Android.bp
+++ b/apct-tests/perftests/permission/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_input_framework",
// 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"
diff --git a/apct-tests/perftests/permission/apps/usepermissionapp/Android.bp b/apct-tests/perftests/permission/apps/usepermissionapp/Android.bp
index 1ad20b6..d503972 100644
--- a/apct-tests/perftests/permission/apps/usepermissionapp/Android.bp
+++ b/apct-tests/perftests/permission/apps/usepermissionapp/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_input_framework",
// 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"
diff --git a/apct-tests/perftests/rubidium/Android.bp b/apct-tests/perftests/rubidium/Android.bp
index ebd228f..4f4fb11 100644
--- a/apct-tests/perftests/rubidium/Android.bp
+++ b/apct-tests/perftests/rubidium/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_input_framework",
// 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"
diff --git a/apct-tests/perftests/settingsprovider/Android.bp b/apct-tests/perftests/settingsprovider/Android.bp
index 43ec0e0..e4aa14c 100644
--- a/apct-tests/perftests/settingsprovider/Android.bp
+++ b/apct-tests/perftests/settingsprovider/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_input_framework",
// 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"
diff --git a/apct-tests/perftests/surfaceflinger/Android.bp b/apct-tests/perftests/surfaceflinger/Android.bp
index 21d0d44..735c725 100644
--- a/apct-tests/perftests/surfaceflinger/Android.bp
+++ b/apct-tests/perftests/surfaceflinger/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_input_framework",
// 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"
diff --git a/apct-tests/perftests/textclassifier/Android.bp b/apct-tests/perftests/textclassifier/Android.bp
index 1011267..cf99771 100644
--- a/apct-tests/perftests/textclassifier/Android.bp
+++ b/apct-tests/perftests/textclassifier/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_input_framework",
// 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"
diff --git a/apct-tests/perftests/utils/Android.bp b/apct-tests/perftests/utils/Android.bp
index 6c46a9b..d2653cd 100644
--- a/apct-tests/perftests/utils/Android.bp
+++ b/apct-tests/perftests/utils/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_input_framework",
// 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"
diff --git a/apct-tests/perftests/windowmanager/Android.bp b/apct-tests/perftests/windowmanager/Android.bp
index 903cf8c..e9357f4 100644
--- a/apct-tests/perftests/windowmanager/Android.bp
+++ b/apct-tests/perftests/windowmanager/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_input_framework",
// 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"
diff --git a/core/api/current.txt b/core/api/current.txt
index 4af2c52..9b89a65 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -2167,11 +2167,6 @@
field public static final int notification_large_icon_width = 17104901; // 0x1050005
field public static final int system_app_widget_background_radius = 17104904; // 0x1050008
field public static final int system_app_widget_inner_radius = 17104905; // 0x1050009
- field public static final int system_corner_radius_large;
- field public static final int system_corner_radius_medium;
- field public static final int system_corner_radius_small;
- field public static final int system_corner_radius_xlarge;
- field public static final int system_corner_radius_xsmall;
field public static final int thumbnail_height = 17104897; // 0x1050001
field public static final int thumbnail_width = 17104898; // 0x1050002
}
@@ -7029,6 +7024,7 @@
method public void deleteNotificationChannelGroup(String);
method public android.service.notification.StatusBarNotification[] getActiveNotifications();
method public android.app.AutomaticZenRule getAutomaticZenRule(String);
+ method @FlaggedApi("android.app.modes_api") public int getAutomaticZenRuleState(@NonNull String);
method public java.util.Map<java.lang.String,android.app.AutomaticZenRule> getAutomaticZenRules();
method public int getBubblePreference();
method @NonNull public android.app.NotificationManager.Policy getConsolidatedNotificationPolicy();
@@ -10737,7 +10733,6 @@
field public static final String DROPBOX_SERVICE = "dropbox";
field public static final String EUICC_SERVICE = "euicc";
field public static final String FILE_INTEGRITY_SERVICE = "file_integrity";
- field public static final String FINGERPRINT_SERVICE = "fingerprint";
field public static final String GAME_SERVICE = "game";
field public static final String GRAMMATICAL_INFLECTION_SERVICE = "grammatical_inflection";
field public static final String HARDWARE_PROPERTIES_SERVICE = "hardware_properties";
@@ -13221,7 +13216,7 @@
field public static final String FEATURE_TELEPHONY_RADIO_ACCESS = "android.hardware.telephony.radio.access";
field public static final String FEATURE_TELEPHONY_SUBSCRIPTION = "android.hardware.telephony.subscription";
field @Deprecated public static final String FEATURE_TELEVISION = "android.hardware.type.television";
- field @FlaggedApi("com.android.net.thread.flags.thread_enabled") public static final String FEATURE_THREAD_NETWORK = "android.hardware.thread_network";
+ field @FlaggedApi("com.android.net.thread.flags.thread_enabled_platform") public static final String FEATURE_THREAD_NETWORK = "android.hardware.thread_network";
field public static final String FEATURE_TOUCHSCREEN = "android.hardware.touchscreen";
field public static final String FEATURE_TOUCHSCREEN_MULTITOUCH = "android.hardware.touchscreen.multitouch";
field public static final String FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT = "android.hardware.touchscreen.multitouch.distinct";
@@ -20367,54 +20362,6 @@
}
-package android.hardware.fingerprint {
-
- @Deprecated public class FingerprintManager {
- method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.USE_BIOMETRIC, android.Manifest.permission.USE_FINGERPRINT}) public void authenticate(@Nullable android.hardware.fingerprint.FingerprintManager.CryptoObject, @Nullable android.os.CancellationSignal, int, @NonNull android.hardware.fingerprint.FingerprintManager.AuthenticationCallback, @Nullable android.os.Handler);
- method @Deprecated @RequiresPermission(android.Manifest.permission.USE_FINGERPRINT) public boolean hasEnrolledFingerprints();
- method @Deprecated @RequiresPermission(android.Manifest.permission.USE_FINGERPRINT) public boolean isHardwareDetected();
- field public static final int FINGERPRINT_ACQUIRED_GOOD = 0; // 0x0
- field public static final int FINGERPRINT_ACQUIRED_IMAGER_DIRTY = 3; // 0x3
- field public static final int FINGERPRINT_ACQUIRED_INSUFFICIENT = 2; // 0x2
- field public static final int FINGERPRINT_ACQUIRED_PARTIAL = 1; // 0x1
- field public static final int FINGERPRINT_ACQUIRED_TOO_FAST = 5; // 0x5
- field public static final int FINGERPRINT_ACQUIRED_TOO_SLOW = 4; // 0x4
- field public static final int FINGERPRINT_ERROR_CANCELED = 5; // 0x5
- field public static final int FINGERPRINT_ERROR_HW_NOT_PRESENT = 12; // 0xc
- field public static final int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1; // 0x1
- field public static final int FINGERPRINT_ERROR_LOCKOUT = 7; // 0x7
- field public static final int FINGERPRINT_ERROR_LOCKOUT_PERMANENT = 9; // 0x9
- field public static final int FINGERPRINT_ERROR_NO_FINGERPRINTS = 11; // 0xb
- field public static final int FINGERPRINT_ERROR_NO_SPACE = 4; // 0x4
- field public static final int FINGERPRINT_ERROR_TIMEOUT = 3; // 0x3
- field public static final int FINGERPRINT_ERROR_UNABLE_TO_PROCESS = 2; // 0x2
- field public static final int FINGERPRINT_ERROR_USER_CANCELED = 10; // 0xa
- field public static final int FINGERPRINT_ERROR_VENDOR = 8; // 0x8
- }
-
- @Deprecated public abstract static class FingerprintManager.AuthenticationCallback {
- ctor @Deprecated public FingerprintManager.AuthenticationCallback();
- method @Deprecated public void onAuthenticationError(int, CharSequence);
- method @Deprecated public void onAuthenticationFailed();
- method @Deprecated public void onAuthenticationHelp(int, CharSequence);
- method @Deprecated public void onAuthenticationSucceeded(android.hardware.fingerprint.FingerprintManager.AuthenticationResult);
- }
-
- @Deprecated public static class FingerprintManager.AuthenticationResult {
- method @Deprecated public android.hardware.fingerprint.FingerprintManager.CryptoObject getCryptoObject();
- }
-
- @Deprecated public static final class FingerprintManager.CryptoObject {
- ctor @Deprecated public FingerprintManager.CryptoObject(@NonNull java.security.Signature);
- ctor @Deprecated public FingerprintManager.CryptoObject(@NonNull javax.crypto.Cipher);
- ctor @Deprecated public FingerprintManager.CryptoObject(@NonNull javax.crypto.Mac);
- method @Deprecated public javax.crypto.Cipher getCipher();
- method @Deprecated public javax.crypto.Mac getMac();
- method @Deprecated public java.security.Signature getSignature();
- }
-
-}
-
package android.hardware.input {
public final class HostUsiVersion implements android.os.Parcelable {
@@ -24567,6 +24514,7 @@
method @FlaggedApi("com.android.media.flags.enable_screen_off_scanning") @NonNull public android.media.MediaRouter2.ScanToken requestScan(@NonNull android.media.MediaRouter2.ScanRequest);
method public void setOnGetControllerHintsListener(@Nullable android.media.MediaRouter2.OnGetControllerHintsListener);
method public void setRouteListingPreference(@Nullable android.media.RouteListingPreference);
+ method @FlaggedApi("com.android.media.flags.enable_privileged_routing_for_media_routing_control") @RequiresPermission(anyOf={android.Manifest.permission.MEDIA_CONTENT_CONTROL, android.Manifest.permission.MEDIA_ROUTING_CONTROL}) public void setRouteVolume(@NonNull android.media.MediaRoute2Info, int);
method public boolean showSystemOutputSwitcher();
method public void stop();
method public void transferTo(@NonNull android.media.MediaRoute2Info);
@@ -43247,6 +43195,7 @@
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PHONE_STATE, android.Manifest.permission.READ_SMS, android.Manifest.permission.READ_PHONE_NUMBERS}, conditional=true) public String getLine1Number(android.telecom.PhoneAccountHandle);
method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_OWN_CALLS) public java.util.List<android.telecom.PhoneAccountHandle> getOwnSelfManagedPhoneAccounts();
method public android.telecom.PhoneAccount getPhoneAccount(android.telecom.PhoneAccountHandle);
+ method @FlaggedApi("com.android.server.telecom.flags.get_registered_phone_accounts") @NonNull public java.util.List<android.telecom.PhoneAccount> getRegisteredPhoneAccounts();
method @NonNull @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public java.util.List<android.telecom.PhoneAccountHandle> getSelfManagedPhoneAccounts();
method public android.telecom.PhoneAccountHandle getSimCallManager();
method @Nullable public android.telecom.PhoneAccountHandle getSimCallManagerForSubscription(int);
@@ -52257,6 +52206,7 @@
method @NonNull public android.view.SurfaceControl.Transaction setCrop(@NonNull android.view.SurfaceControl, @Nullable android.graphics.Rect);
method @NonNull public android.view.SurfaceControl.Transaction setDamageRegion(@NonNull android.view.SurfaceControl, @Nullable android.graphics.Region);
method @NonNull public android.view.SurfaceControl.Transaction setDataSpace(@NonNull android.view.SurfaceControl, int);
+ method @FlaggedApi("com.android.graphics.hwui.flags.limited_hdr") @NonNull public android.view.SurfaceControl.Transaction setDesiredHdrHeadroom(@NonNull android.view.SurfaceControl, @FloatRange(from=0.0f) float);
method @FlaggedApi("com.android.window.flags.sdk_desired_present_time") @NonNull public android.view.SurfaceControl.Transaction setDesiredPresentTimeNanos(long);
method @NonNull public android.view.SurfaceControl.Transaction setExtendedRangeBrightness(@NonNull android.view.SurfaceControl, float, float);
method @NonNull public android.view.SurfaceControl.Transaction setFrameRate(@NonNull android.view.SurfaceControl, @FloatRange(from=0.0) float, int);
@@ -52360,6 +52310,7 @@
method @Nullable public android.os.IBinder getHostToken();
method public android.view.SurfaceControl getSurfaceControl();
method public void setChildSurfacePackage(@NonNull android.view.SurfaceControlViewHost.SurfacePackage);
+ method @FlaggedApi("com.android.graphics.hwui.flags.limited_hdr") public void setDesiredHdrHeadroom(@FloatRange(from=0.0f, to=10000.0) float);
method public void setSecure(boolean);
method public void setSurfaceLifecycle(int);
method public void setZOrderMediaOverlay(boolean);
@@ -60249,6 +60200,7 @@
}
@FlaggedApi("android.appwidget.flags.draw_data_parcel") public static final class RemoteViews.DrawInstructions {
+ method @FlaggedApi("android.appwidget.flags.draw_data_parcel") public static long getSupportedVersion();
}
@FlaggedApi("android.appwidget.flags.draw_data_parcel") public static final class RemoteViews.DrawInstructions.Builder {
diff --git a/core/api/removed.txt b/core/api/removed.txt
index 3c7c0d6..c61f163 100644
--- a/core/api/removed.txt
+++ b/core/api/removed.txt
@@ -35,6 +35,7 @@
method @Deprecated @Nullable public String getFeatureId();
method public abstract android.content.SharedPreferences getSharedPreferences(java.io.File, int);
method public abstract java.io.File getSharedPreferencesPath(String);
+ field public static final String FINGERPRINT_SERVICE = "fingerprint";
}
public class ContextWrapper extends android.content.Context {
@@ -145,6 +146,54 @@
}
+package android.hardware.fingerprint {
+
+ @Deprecated public class FingerprintManager {
+ method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.USE_BIOMETRIC, android.Manifest.permission.USE_FINGERPRINT}) public void authenticate(@Nullable android.hardware.fingerprint.FingerprintManager.CryptoObject, @Nullable android.os.CancellationSignal, int, @NonNull android.hardware.fingerprint.FingerprintManager.AuthenticationCallback, @Nullable android.os.Handler);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.USE_FINGERPRINT) public boolean hasEnrolledFingerprints();
+ method @Deprecated @RequiresPermission(android.Manifest.permission.USE_FINGERPRINT) public boolean isHardwareDetected();
+ field public static final int FINGERPRINT_ACQUIRED_GOOD = 0; // 0x0
+ field public static final int FINGERPRINT_ACQUIRED_IMAGER_DIRTY = 3; // 0x3
+ field public static final int FINGERPRINT_ACQUIRED_INSUFFICIENT = 2; // 0x2
+ field public static final int FINGERPRINT_ACQUIRED_PARTIAL = 1; // 0x1
+ field public static final int FINGERPRINT_ACQUIRED_TOO_FAST = 5; // 0x5
+ field public static final int FINGERPRINT_ACQUIRED_TOO_SLOW = 4; // 0x4
+ field public static final int FINGERPRINT_ERROR_CANCELED = 5; // 0x5
+ field public static final int FINGERPRINT_ERROR_HW_NOT_PRESENT = 12; // 0xc
+ field public static final int FINGERPRINT_ERROR_HW_UNAVAILABLE = 1; // 0x1
+ field public static final int FINGERPRINT_ERROR_LOCKOUT = 7; // 0x7
+ field public static final int FINGERPRINT_ERROR_LOCKOUT_PERMANENT = 9; // 0x9
+ field public static final int FINGERPRINT_ERROR_NO_FINGERPRINTS = 11; // 0xb
+ field public static final int FINGERPRINT_ERROR_NO_SPACE = 4; // 0x4
+ field public static final int FINGERPRINT_ERROR_TIMEOUT = 3; // 0x3
+ field public static final int FINGERPRINT_ERROR_UNABLE_TO_PROCESS = 2; // 0x2
+ field public static final int FINGERPRINT_ERROR_USER_CANCELED = 10; // 0xa
+ field public static final int FINGERPRINT_ERROR_VENDOR = 8; // 0x8
+ }
+
+ @Deprecated public abstract static class FingerprintManager.AuthenticationCallback {
+ ctor public FingerprintManager.AuthenticationCallback();
+ method public void onAuthenticationError(int, CharSequence);
+ method public void onAuthenticationFailed();
+ method public void onAuthenticationHelp(int, CharSequence);
+ method public void onAuthenticationSucceeded(android.hardware.fingerprint.FingerprintManager.AuthenticationResult);
+ }
+
+ @Deprecated public static class FingerprintManager.AuthenticationResult {
+ method public android.hardware.fingerprint.FingerprintManager.CryptoObject getCryptoObject();
+ }
+
+ @Deprecated public static final class FingerprintManager.CryptoObject {
+ ctor public FingerprintManager.CryptoObject(@NonNull java.security.Signature);
+ ctor public FingerprintManager.CryptoObject(@NonNull javax.crypto.Cipher);
+ ctor public FingerprintManager.CryptoObject(@NonNull javax.crypto.Mac);
+ method public javax.crypto.Cipher getCipher();
+ method public javax.crypto.Mac getMac();
+ method public java.security.Signature getSignature();
+ }
+
+}
+
package android.media {
public final class AudioFormat implements android.os.Parcelable {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 77a66ce..627b703 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -69,6 +69,8 @@
field public static final String BIND_MUSIC_RECOGNITION_SERVICE = "android.permission.BIND_MUSIC_RECOGNITION_SERVICE";
field public static final String BIND_NETWORK_RECOMMENDATION_SERVICE = "android.permission.BIND_NETWORK_RECOMMENDATION_SERVICE";
field public static final String BIND_NOTIFICATION_ASSISTANT_SERVICE = "android.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE";
+ field @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public static final String BIND_ON_DEVICE_INTELLIGENCE_SERVICE = "android.permission.BIND_ON_DEVICE_INTELLIGENCE_SERVICE";
+ field @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public static final String BIND_ON_DEVICE_TRUSTED_SERVICE = "android.permission.BIND_ON_DEVICE_TRUSTED_SERVICE";
field public static final String BIND_PHONE_ACCOUNT_SUGGESTION_SERVICE = "android.permission.BIND_PHONE_ACCOUNT_SUGGESTION_SERVICE";
field public static final String BIND_PRINT_RECOMMENDATION_SERVICE = "android.permission.BIND_PRINT_RECOMMENDATION_SERVICE";
field public static final String BIND_REMOTE_LOCKSCREEN_VALIDATION_SERVICE = "android.permission.BIND_REMOTE_LOCKSCREEN_VALIDATION_SERVICE";
@@ -367,7 +369,7 @@
field public static final String SET_VOLUME_KEY_LONG_PRESS_LISTENER = "android.permission.SET_VOLUME_KEY_LONG_PRESS_LISTENER";
field public static final String SET_WALLPAPER_COMPONENT = "android.permission.SET_WALLPAPER_COMPONENT";
field public static final String SET_WALLPAPER_DIM_AMOUNT = "android.permission.SET_WALLPAPER_DIM_AMOUNT";
- field @FlaggedApi("android.service.chooser.support_nfc_resolver") public static final String SHOW_CUSTOMIZED_RESOLVER = "android.permission.SHOW_CUSTOMIZED_RESOLVER";
+ field @FlaggedApi("android.nfc.enable_nfc_mainline") public static final String SHOW_CUSTOMIZED_RESOLVER = "android.permission.SHOW_CUSTOMIZED_RESOLVER";
field public static final String SHOW_KEYGUARD_MESSAGE = "android.permission.SHOW_KEYGUARD_MESSAGE";
field public static final String SHUTDOWN = "android.permission.SHUTDOWN";
field public static final String SIGNAL_REBOOT_READINESS = "android.permission.SIGNAL_REBOOT_READINESS";
@@ -386,7 +388,7 @@
field public static final String SYSTEM_APPLICATION_OVERLAY = "android.permission.SYSTEM_APPLICATION_OVERLAY";
field public static final String SYSTEM_CAMERA = "android.permission.SYSTEM_CAMERA";
field public static final String TETHER_PRIVILEGED = "android.permission.TETHER_PRIVILEGED";
- field @FlaggedApi("com.android.net.thread.flags.thread_enabled") public static final String THREAD_NETWORK_PRIVILEGED = "android.permission.THREAD_NETWORK_PRIVILEGED";
+ field @FlaggedApi("com.android.net.thread.flags.thread_enabled_platform") public static final String THREAD_NETWORK_PRIVILEGED = "android.permission.THREAD_NETWORK_PRIVILEGED";
field public static final String TIS_EXTENSION_INTERFACE = "android.permission.TIS_EXTENSION_INTERFACE";
field public static final String TOGGLE_AUTOMOTIVE_PROJECTION = "android.permission.TOGGLE_AUTOMOTIVE_PROJECTION";
field public static final String TRIGGER_LOST_MODE = "android.permission.TRIGGER_LOST_MODE";
@@ -403,6 +405,7 @@
field public static final String USER_ACTIVITY = "android.permission.USER_ACTIVITY";
field @FlaggedApi("android.hardware.biometrics.face_background_authentication") public static final String USE_BACKGROUND_FACE_AUTHENTICATION = "android.permission.USE_BACKGROUND_FACE_AUTHENTICATION";
field public static final String USE_COLORIZED_NOTIFICATIONS = "android.permission.USE_COLORIZED_NOTIFICATIONS";
+ field @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public static final String USE_ON_DEVICE_INTELLIGENCE = "android.permission.USE_ON_DEVICE_INTELLIGENCE";
field public static final String USE_RESERVED_DISK = "android.permission.USE_RESERVED_DISK";
field public static final String UWB_PRIVILEGED = "android.permission.UWB_PRIVILEGED";
field public static final String WHITELIST_AUTO_REVOKE_PERMISSIONS = "android.permission.WHITELIST_AUTO_REVOKE_PERMISSIONS";
@@ -2190,6 +2193,139 @@
}
+package android.app.ondeviceintelligence {
+
+ @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public final class Content implements android.os.Parcelable {
+ ctor public Content(@NonNull android.os.Bundle);
+ method public int describeContents();
+ method @NonNull public android.os.Bundle getData();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.ondeviceintelligence.Content> CREATOR;
+ }
+
+ @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public interface DownloadCallback {
+ method public void onDownloadCompleted(@NonNull android.os.PersistableBundle);
+ method public void onDownloadFailed(int, @Nullable String, @NonNull android.os.PersistableBundle);
+ method public default void onDownloadProgress(long);
+ method public default void onDownloadStarted(long);
+ field public static final int DOWNLOAD_FAILURE_STATUS_DOWNLOADING = 3; // 0x3
+ field public static final int DOWNLOAD_FAILURE_STATUS_NETWORK_FAILURE = 2; // 0x2
+ field public static final int DOWNLOAD_FAILURE_STATUS_NOT_ENOUGH_DISK_SPACE = 1; // 0x1
+ field public static final int DOWNLOAD_FAILURE_STATUS_UNAVAILABLE = 4; // 0x4
+ field public static final int DOWNLOAD_FAILURE_STATUS_UNKNOWN = 0; // 0x0
+ }
+
+ @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public final class Feature implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public android.os.PersistableBundle getFeatureParams();
+ method public int getId();
+ method @Nullable public String getModelName();
+ method @Nullable public String getName();
+ method public int getType();
+ method public int getVariant();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.ondeviceintelligence.Feature> CREATOR;
+ }
+
+ public static final class Feature.Builder {
+ ctor public Feature.Builder(int, int, int, @NonNull android.os.PersistableBundle);
+ method @NonNull public android.app.ondeviceintelligence.Feature build();
+ method @NonNull public android.app.ondeviceintelligence.Feature.Builder setFeatureParams(@NonNull android.os.PersistableBundle);
+ method @NonNull public android.app.ondeviceintelligence.Feature.Builder setId(int);
+ method @NonNull public android.app.ondeviceintelligence.Feature.Builder setModelName(@NonNull String);
+ method @NonNull public android.app.ondeviceintelligence.Feature.Builder setName(@NonNull String);
+ method @NonNull public android.app.ondeviceintelligence.Feature.Builder setType(int);
+ method @NonNull public android.app.ondeviceintelligence.Feature.Builder setVariant(int);
+ }
+
+ @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public final class FeatureDetails implements android.os.Parcelable {
+ ctor public FeatureDetails(@android.app.ondeviceintelligence.FeatureDetails.Status int, @NonNull android.os.PersistableBundle);
+ ctor public FeatureDetails(@android.app.ondeviceintelligence.FeatureDetails.Status int);
+ method public int describeContents();
+ method @NonNull public android.os.PersistableBundle getFeatureDetailParams();
+ method @android.app.ondeviceintelligence.FeatureDetails.Status public int getStatus();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.ondeviceintelligence.FeatureDetails> CREATOR;
+ field public static final int FEATURE_STATUS_AVAILABLE = 3; // 0x3
+ field public static final int FEATURE_STATUS_DOWNLOADABLE = 1; // 0x1
+ field public static final int FEATURE_STATUS_DOWNLOADING = 2; // 0x2
+ field public static final int FEATURE_STATUS_SERVICE_UNAVAILABLE = 4; // 0x4
+ field public static final int FEATURE_STATUS_UNAVAILABLE = 0; // 0x0
+ }
+
+ @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE_USE, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.FIELD}) public static @interface FeatureDetails.Status {
+ }
+
+ @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public final class FilePart implements android.os.Parcelable {
+ ctor public FilePart(@NonNull String, @NonNull android.os.PersistableBundle, @NonNull String) throws java.io.FileNotFoundException;
+ ctor public FilePart(@NonNull String, @NonNull android.os.PersistableBundle, @NonNull java.io.FileInputStream) throws java.io.IOException;
+ method public int describeContents();
+ method @NonNull public java.io.FileInputStream getFileInputStream();
+ method @NonNull public String getFilePartKey();
+ method @NonNull public android.os.PersistableBundle getFilePartParams();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.ondeviceintelligence.FilePart> CREATOR;
+ }
+
+ @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public class OnDeviceIntelligenceManager {
+ method @RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE) public void getFeature(int, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.app.ondeviceintelligence.Feature,android.app.ondeviceintelligence.OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException>);
+ method @RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE) public void getFeatureDetails(@NonNull android.app.ondeviceintelligence.Feature, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.app.ondeviceintelligence.FeatureDetails,android.app.ondeviceintelligence.OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException>);
+ method @RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE) public void getVersion(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.LongConsumer);
+ method @RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE) public void listFeatures(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.util.List<android.app.ondeviceintelligence.Feature>,android.app.ondeviceintelligence.OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException>);
+ method @RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE) public void processRequest(@NonNull android.app.ondeviceintelligence.Feature, @NonNull android.app.ondeviceintelligence.Content, int, @Nullable android.os.CancellationSignal, @Nullable android.app.ondeviceintelligence.ProcessingSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.app.ondeviceintelligence.Content,android.app.ondeviceintelligence.OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException>);
+ method @RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE) public void processRequestStreaming(@NonNull android.app.ondeviceintelligence.Feature, @NonNull android.app.ondeviceintelligence.Content, int, @Nullable android.os.CancellationSignal, @Nullable android.app.ondeviceintelligence.ProcessingSignal, @NonNull java.util.concurrent.Executor, @NonNull android.app.ondeviceintelligence.StreamingResponseReceiver<android.app.ondeviceintelligence.Content,android.app.ondeviceintelligence.Content,android.app.ondeviceintelligence.OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException>);
+ method @RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE) public void requestFeatureDownload(@NonNull android.app.ondeviceintelligence.Feature, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.app.ondeviceintelligence.DownloadCallback);
+ method @RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE) public void requestTokenCount(@NonNull android.app.ondeviceintelligence.Feature, @NonNull android.app.ondeviceintelligence.Content, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Long,android.app.ondeviceintelligence.OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException>);
+ field public static final String API_VERSION_BUNDLE_KEY = "ApiVersionBundleKey";
+ field public static final int REQUEST_TYPE_EMBEDDINGS = 2; // 0x2
+ field public static final int REQUEST_TYPE_INFERENCE = 0; // 0x0
+ field public static final int REQUEST_TYPE_PREPARE = 1; // 0x1
+ }
+
+ public static class OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException extends java.lang.Exception {
+ ctor public OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException(int, @NonNull String, @NonNull android.os.PersistableBundle);
+ ctor public OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException(int, @NonNull android.os.PersistableBundle);
+ method public int getErrorCode();
+ method @NonNull public android.os.PersistableBundle getErrorParams();
+ field public static final int ON_DEVICE_INTELLIGENCE_SERVICE_UNAVAILABLE = 1000; // 0x3e8
+ }
+
+ public static class OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException extends android.app.ondeviceintelligence.OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException {
+ ctor public OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException(int, @NonNull String, @NonNull android.os.PersistableBundle);
+ ctor public OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException(int, @NonNull android.os.PersistableBundle);
+ field public static final int PROCESSING_ERROR_BAD_DATA = 2; // 0x2
+ field public static final int PROCESSING_ERROR_BAD_REQUEST = 3; // 0x3
+ field public static final int PROCESSING_ERROR_BUSY = 9; // 0x9
+ field public static final int PROCESSING_ERROR_CANCELLED = 7; // 0x7
+ field public static final int PROCESSING_ERROR_COMPUTE_ERROR = 5; // 0x5
+ field public static final int PROCESSING_ERROR_INTERNAL = 14; // 0xe
+ field public static final int PROCESSING_ERROR_IPC_ERROR = 6; // 0x6
+ field public static final int PROCESSING_ERROR_NOT_AVAILABLE = 8; // 0x8
+ field public static final int PROCESSING_ERROR_REQUEST_NOT_SAFE = 4; // 0x4
+ field public static final int PROCESSING_ERROR_REQUEST_TOO_LARGE = 12; // 0xc
+ field public static final int PROCESSING_ERROR_RESPONSE_NOT_SAFE = 11; // 0xb
+ field public static final int PROCESSING_ERROR_SAFETY_ERROR = 10; // 0xa
+ field public static final int PROCESSING_ERROR_SERVICE_UNAVAILABLE = 15; // 0xf
+ field public static final int PROCESSING_ERROR_SUSPENDED = 13; // 0xd
+ field public static final int PROCESSING_ERROR_UNKNOWN = 1; // 0x1
+ }
+
+ @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public final class ProcessingSignal {
+ ctor public ProcessingSignal();
+ method public void sendSignal(@NonNull android.os.PersistableBundle);
+ method public void setOnProcessingSignalCallback(@NonNull java.util.concurrent.Executor, @Nullable android.app.ondeviceintelligence.ProcessingSignal.OnProcessingSignalCallback);
+ }
+
+ public static interface ProcessingSignal.OnProcessingSignalCallback {
+ method public void onSignalReceived(@NonNull android.os.PersistableBundle);
+ }
+
+ @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public interface StreamingResponseReceiver<R, T, E extends java.lang.Throwable> extends android.os.OutcomeReceiver<R,E> {
+ method public void onNewContent(@NonNull T);
+ }
+
+}
+
package android.app.people {
public final class PeopleManager {
@@ -3637,6 +3773,7 @@
field public static final String NETD_SERVICE = "netd";
field @Deprecated public static final String NETWORK_SCORE_SERVICE = "network_score";
field public static final String OEM_LOCK_SERVICE = "oem_lock";
+ field @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public static final String ON_DEVICE_INTELLIGENCE_SERVICE = "on_device_intelligence";
field public static final String PERMISSION_CONTROLLER_SERVICE = "permission_controller";
field public static final String PERMISSION_SERVICE = "permission";
field public static final String PERSISTENT_DATA_BLOCK_SERVICE = "persistent_data_block";
@@ -3651,7 +3788,7 @@
field public static final String SYSTEM_CONFIG_SERVICE = "system_config";
field public static final String SYSTEM_UPDATE_SERVICE = "system_update";
field public static final String TETHERING_SERVICE = "tethering";
- field @FlaggedApi("com.android.net.thread.flags.thread_enabled") public static final String THREAD_NETWORK_SERVICE = "thread_network";
+ field @FlaggedApi("com.android.net.thread.flags.thread_enabled_platform") public static final String THREAD_NETWORK_SERVICE = "thread_network";
field public static final String TIME_MANAGER_SERVICE = "time_manager";
field public static final String TRANSLATION_MANAGER_SERVICE = "translation";
field public static final String UI_TRANSLATION_SERVICE = "ui_translation";
@@ -4827,7 +4964,7 @@
}
@FlaggedApi("com.android.internal.camera.flags.concert_mode") public static interface SessionProcessor.CaptureCallback {
- method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public void onCaptureCompleted(long, int, @NonNull android.hardware.camera2.CaptureResult);
+ method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public void onCaptureCompleted(long, int, @NonNull java.util.Map<android.hardware.camera2.CaptureResult.Key,java.lang.Object>);
method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public void onCaptureFailed(int, int);
method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public void onCaptureProcessStarted(int);
method @FlaggedApi("com.android.internal.camera.flags.concert_mode") public void onCaptureSequenceAborted(int);
@@ -4851,6 +4988,39 @@
}
+package android.hardware.devicestate {
+
+ @FlaggedApi("android.hardware.devicestate.feature.flags.device_state_property_api") public final class DeviceState {
+ method @IntRange(from=0x0) public int getIdentifier();
+ method @NonNull public String getName();
+ method public boolean hasProperties(@NonNull int...);
+ method public boolean hasProperty(int);
+ field public static final int PROPERTY_EMULATED_ONLY = 10; // 0xa
+ field public static final int PROPERTY_EXTENDED_DEVICE_STATE_EXTERNAL_DISPLAY = 15; // 0xf
+ field public static final int PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT = 17; // 0x11
+ field public static final int PROPERTY_FEATURE_REAR_DISPLAY = 16; // 0x10
+ field public static final int PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY = 12; // 0xc
+ field public static final int PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY = 11; // 0xb
+ field public static final int PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED = 1; // 0x1
+ field public static final int PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN = 2; // 0x2
+ field public static final int PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN = 3; // 0x3
+ field public static final int PROPERTY_POWER_CONFIGURATION_TRIGGER_SLEEP = 13; // 0xd
+ field public static final int PROPERTY_POWER_CONFIGURATION_TRIGGER_WAKE = 14; // 0xe
+ }
+
+ @FlaggedApi("android.hardware.devicestate.feature.flags.device_state_property_api") public final class DeviceStateManager {
+ method @NonNull public java.util.List<android.hardware.devicestate.DeviceState> getSupportedDeviceStates();
+ method public void registerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.devicestate.DeviceStateManager.DeviceStateCallback);
+ method public void unregisterCallback(@NonNull android.hardware.devicestate.DeviceStateManager.DeviceStateCallback);
+ }
+
+ public static interface DeviceStateManager.DeviceStateCallback {
+ method public default void onDeviceStateChanged(@NonNull android.hardware.devicestate.DeviceState);
+ method public default void onSupportedStatesChanged(@NonNull java.util.List<android.hardware.devicestate.DeviceState>);
+ }
+
+}
+
package android.hardware.display {
public final class AmbientBrightnessDayStats implements android.os.Parcelable {
@@ -7125,7 +7295,6 @@
public final class MediaRouter2 {
method @NonNull public java.util.List<android.media.MediaRoute2Info> getAllRoutes();
method @Nullable public String getClientPackageName();
- method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void setRouteVolume(@NonNull android.media.MediaRoute2Info, int);
method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void startScan();
method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void stopScan();
method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void transfer(@NonNull android.media.MediaRouter2.RoutingController, @NonNull android.media.MediaRoute2Info);
@@ -12329,14 +12498,6 @@
}
-package android.service.chooser {
-
- @FlaggedApi("android.service.chooser.support_nfc_resolver") public class CustomChoosers {
- method @FlaggedApi("android.service.chooser.support_nfc_resolver") @NonNull public static android.content.Intent createNfcResolverIntent(@NonNull android.content.Intent, @Nullable CharSequence, @NonNull java.util.List<android.content.pm.ResolveInfo>);
- }
-
-}
-
package android.service.cloudsearch {
public abstract class CloudSearchService extends android.app.Service {
@@ -12797,6 +12958,34 @@
}
+package android.service.ondeviceintelligence {
+
+ @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public abstract class OnDeviceIntelligenceService extends android.app.Service {
+ ctor public OnDeviceIntelligenceService();
+ method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
+ method public abstract void onDownloadFeature(@NonNull android.app.ondeviceintelligence.Feature, @Nullable android.os.CancellationSignal, @NonNull android.app.ondeviceintelligence.DownloadCallback);
+ method public abstract void onGetFeature(int, @NonNull android.os.OutcomeReceiver<android.app.ondeviceintelligence.Feature,android.app.ondeviceintelligence.OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException>);
+ method public abstract void onGetFeatureDetails(@NonNull android.app.ondeviceintelligence.Feature, @NonNull android.os.OutcomeReceiver<android.app.ondeviceintelligence.FeatureDetails,android.app.ondeviceintelligence.OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException>);
+ method public abstract void onGetReadOnlyFeatureFileDescriptorMap(@NonNull android.app.ondeviceintelligence.Feature, @NonNull java.util.function.Consumer<java.util.Map<java.lang.String,android.os.ParcelFileDescriptor>>);
+ method public abstract void onGetVersion(@NonNull java.util.function.LongConsumer);
+ method public abstract void onListFeatures(@NonNull android.os.OutcomeReceiver<java.util.List<android.app.ondeviceintelligence.Feature>,android.app.ondeviceintelligence.OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException>);
+ field public static final String SERVICE_INTERFACE = "android.service.ondeviceintelligence.OnDeviceIntelligenceService";
+ }
+
+ @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence") public abstract class OnDeviceTrustedInferenceService extends android.app.Service {
+ ctor public OnDeviceTrustedInferenceService();
+ method public final void fetchFeatureFileInputStreamMap(@NonNull android.app.ondeviceintelligence.Feature, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.util.Map<java.lang.String,java.io.FileInputStream>>);
+ method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
+ method @NonNull public abstract void onCountTokens(@NonNull android.app.ondeviceintelligence.Feature, @NonNull android.app.ondeviceintelligence.Content, @Nullable android.os.CancellationSignal, @NonNull android.os.OutcomeReceiver<java.lang.Long,android.app.ondeviceintelligence.OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException>);
+ method @NonNull public abstract void onProcessRequest(@NonNull android.app.ondeviceintelligence.Feature, @NonNull android.app.ondeviceintelligence.Content, int, @Nullable android.os.CancellationSignal, @Nullable android.app.ondeviceintelligence.ProcessingSignal, @NonNull android.os.OutcomeReceiver<android.app.ondeviceintelligence.Content,android.app.ondeviceintelligence.OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException>);
+ method @NonNull public abstract void onProcessRequestStreaming(@NonNull android.app.ondeviceintelligence.Feature, @NonNull android.app.ondeviceintelligence.Content, int, @Nullable android.os.CancellationSignal, @Nullable android.app.ondeviceintelligence.ProcessingSignal, @NonNull android.app.ondeviceintelligence.StreamingResponseReceiver<android.app.ondeviceintelligence.Content,android.app.ondeviceintelligence.Content,android.app.ondeviceintelligence.OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException>);
+ method public final java.io.FileInputStream openFileInput(@NonNull String) throws java.io.FileNotFoundException;
+ method public final void openFileInputAsync(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.io.FileInputStream>) throws java.io.FileNotFoundException;
+ field public static final String SERVICE_INTERFACE = "android.service.ondeviceintelligence.OnDeviceTrustedInferenceService";
+ }
+
+}
+
package android.service.persistentdata {
@FlaggedApi("android.security.frp_enforcement") public class PersistentDataBlockManager {
@@ -13429,6 +13618,7 @@
@FlaggedApi("android.service.voice.flags.allow_complex_results_egress_from_vqds") public final class VisualQueryDetectedResult implements android.os.Parcelable {
method public int describeContents();
+ method @Nullable public byte[] getAccessibilityDetectionData();
method public static int getMaxSpeakerId();
method @NonNull public String getPartialQuery();
method public int getSpeakerId();
@@ -13439,6 +13629,7 @@
public static final class VisualQueryDetectedResult.Builder {
ctor public VisualQueryDetectedResult.Builder();
method @NonNull public android.service.voice.VisualQueryDetectedResult build();
+ method @NonNull public android.service.voice.VisualQueryDetectedResult.Builder setAccessibilityDetectionData(@NonNull byte...);
method @NonNull public android.service.voice.VisualQueryDetectedResult.Builder setPartialQuery(@NonNull String);
method @NonNull public android.service.voice.VisualQueryDetectedResult.Builder setSpeakerId(int);
}
@@ -13476,7 +13667,10 @@
}
public class VisualQueryDetector {
+ method @FlaggedApi("android.service.voice.flags.allow_complex_results_egress_from_vqds") public void clearAccessibilityDetectionEnabledListener();
method public void destroy();
+ method @FlaggedApi("android.service.voice.flags.allow_complex_results_egress_from_vqds") public boolean isAccessibilityDetectionEnabled();
+ method @FlaggedApi("android.service.voice.flags.allow_complex_results_egress_from_vqds") public void setAccessibilityDetectionEnabledListener(@NonNull java.util.function.Consumer<java.lang.Boolean>);
method @RequiresPermission(allOf={android.Manifest.permission.CAMERA, android.Manifest.permission.RECORD_AUDIO}) public boolean startRecognition();
method @RequiresPermission(allOf={android.Manifest.permission.CAMERA, android.Manifest.permission.RECORD_AUDIO}) public boolean stopRecognition();
method public void updateState(@Nullable android.os.PersistableBundle, @Nullable android.os.SharedMemory);
diff --git a/core/api/system-lint-baseline.txt b/core/api/system-lint-baseline.txt
index ca9fab8..1923641 100644
--- a/core/api/system-lint-baseline.txt
+++ b/core/api/system-lint-baseline.txt
@@ -509,6 +509,12 @@
Methods must not throw generic exceptions (`java.lang.Throwable`)
+InvalidNullabilityOverride: android.service.ondeviceintelligence.OnDeviceIntelligenceService#onBind(android.content.Intent) parameter #0:
+ Invalid nullability on parameter `intent` in method `onBind`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
+InvalidNullabilityOverride: android.service.ondeviceintelligence.OnDeviceTrustedInferenceService#onBind(android.content.Intent) parameter #0:
+ Invalid nullability on parameter `intent` in method `onBind`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
+InvalidNullabilityOverride: android.service.ondeviceintelligence.OnDeviceTrustedInferenceService#openFileInput(String) parameter #0:
+ Invalid nullability on parameter `filename` in method `openFileInput`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
InvalidNullabilityOverride: android.service.textclassifier.TextClassifierService#onUnbind(android.content.Intent) parameter #0:
Invalid nullability on parameter `intent` in method `onUnbind`. Parameters of overrides cannot be NonNull if the super parameter is unannotated.
InvalidNullabilityOverride: android.service.voice.HotwordDetectionService#getSystemService(String) parameter #0:
@@ -565,6 +571,8 @@
Missing nullability on parameter `args` in method `dump`
MissingNullability: android.service.notification.NotificationAssistantService#attachBaseContext(android.content.Context) parameter #0:
Missing nullability on parameter `base` in method `attachBaseContext`
+MissingNullability: android.service.ondeviceintelligence.OnDeviceTrustedInferenceService#openFileInput(String):
+ Missing nullability on method `openFileInput` return
MissingNullability: android.telephony.NetworkService#onUnbind(android.content.Intent) parameter #0:
Missing nullability on parameter `intent` in method `onUnbind`
MissingNullability: android.telephony.data.DataService#onUnbind(android.content.Intent) parameter #0:
@@ -1877,6 +1885,8 @@
Documentation mentions 'TODO'
Todo: android.app.NotificationManager#isNotificationAssistantAccessGranted(android.content.ComponentName):
Documentation mentions 'TODO'
+Todo: android.app.ondeviceintelligence.OnDeviceIntelligenceManager#requestFeatureDownload(android.app.ondeviceintelligence.Feature, android.app.ondeviceintelligence.CancellationSignal, java.util.concurrent.Executor, android.app.ondeviceintelligence.DownloadCallback):
+ Documentation mentions 'TODO'
Todo: android.hardware.camera2.params.StreamConfigurationMap:
Documentation mentions 'TODO'
Todo: android.hardware.location.ContextHubManager#getNanoAppInstanceInfo(int):
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index f6366a2..48dcbe5 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1548,6 +1548,7 @@
public static class BiometricPrompt.Builder {
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.TEST_BIOMETRIC, "android.permission.USE_BIOMETRIC_INTERNAL"}) public android.hardware.biometrics.BiometricPrompt.Builder setAllowBackgroundAuthentication(boolean);
+ method @FlaggedApi("android.multiuser.enable_biometrics_to_unlock_private_space") @NonNull @RequiresPermission(anyOf={android.Manifest.permission.TEST_BIOMETRIC, "android.permission.USE_BIOMETRIC_INTERNAL"}) public android.hardware.biometrics.BiometricPrompt.Builder setAllowBackgroundAuthentication(boolean, boolean);
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.TEST_BIOMETRIC, "android.permission.USE_BIOMETRIC_INTERNAL"}) public android.hardware.biometrics.BiometricPrompt.Builder setAllowedSensorIds(@NonNull java.util.List<java.lang.Integer>);
}
@@ -1620,22 +1621,26 @@
package android.hardware.devicestate {
- public final class DeviceStateManager {
+ @FlaggedApi("android.hardware.devicestate.feature.flags.device_state_property_api") public final class DeviceState {
+ ctor @Deprecated public DeviceState(@IntRange(from=android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE_IDENTIFIER, to=android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE_IDENTIFIER) int, @NonNull String, int);
+ ctor public DeviceState(@IntRange(from=android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE_IDENTIFIER, to=android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE_IDENTIFIER) int, @NonNull String, @NonNull java.util.Set<java.lang.Integer>);
+ field public static final int PROPERTY_POLICY_AVAILABLE_FOR_APP_REQUEST = 8; // 0x8
+ }
+
+ @FlaggedApi("android.hardware.devicestate.feature.flags.device_state_property_api") public final class DeviceStateManager {
method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE) public void cancelBaseStateOverride();
method @RequiresPermission(value=android.Manifest.permission.CONTROL_DEVICE_STATE, conditional=true) public void cancelStateRequest();
- method @NonNull public int[] getSupportedStates();
- method public void registerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.hardware.devicestate.DeviceStateManager.DeviceStateCallback);
+ method @Deprecated @NonNull public int[] getSupportedStates();
method @RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE) public void requestBaseStateOverride(@NonNull android.hardware.devicestate.DeviceStateRequest, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.devicestate.DeviceStateRequest.Callback);
method @RequiresPermission(value=android.Manifest.permission.CONTROL_DEVICE_STATE, conditional=true) public void requestState(@NonNull android.hardware.devicestate.DeviceStateRequest, @Nullable java.util.concurrent.Executor, @Nullable android.hardware.devicestate.DeviceStateRequest.Callback);
- method public void unregisterCallback(@NonNull android.hardware.devicestate.DeviceStateManager.DeviceStateCallback);
- field public static final int MAXIMUM_DEVICE_STATE = 255; // 0xff
- field public static final int MINIMUM_DEVICE_STATE = 0; // 0x0
+ field public static final int MAXIMUM_DEVICE_STATE_IDENTIFIER = 10000; // 0x2710
+ field public static final int MINIMUM_DEVICE_STATE_IDENTIFIER = 0; // 0x0
}
public static interface DeviceStateManager.DeviceStateCallback {
- method public default void onBaseStateChanged(int);
- method public void onStateChanged(int);
- method public default void onSupportedStatesChanged(@NonNull int[]);
+ method @Deprecated public default void onBaseStateChanged(int);
+ method @Deprecated public void onStateChanged(int);
+ method @Deprecated public default void onSupportedStatesChanged(@NonNull int[]);
}
public final class DeviceStateRequest {
@@ -1698,15 +1703,7 @@
field public static final int SWITCHING_TYPE_WITHIN_GROUPS = 1; // 0x1
field public static final int VIRTUAL_DISPLAY_FLAG_OWN_FOCUS = 16384; // 0x4000
field public static final int VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS = 512; // 0x200
- }
-
-}
-
-package android.hardware.fingerprint {
-
- @Deprecated public class FingerprintManager {
- method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public android.hardware.biometrics.BiometricTestSession createTestSession(int);
- method @Deprecated @NonNull @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public java.util.List<android.hardware.biometrics.SensorProperties> getSensorProperties();
+ field public static final int VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH = 64; // 0x40
}
}
@@ -1755,6 +1752,10 @@
field public static final int DEFAULT_POINTER_SPEED = 0; // 0x0
}
+ public class VirtualKeyboard implements java.io.Closeable {
+ method public int getInputDeviceId();
+ }
+
}
package android.hardware.lights {
diff --git a/core/api/test-lint-baseline.txt b/core/api/test-lint-baseline.txt
index c1181f5..27808cb 100644
--- a/core/api/test-lint-baseline.txt
+++ b/core/api/test-lint-baseline.txt
@@ -1931,6 +1931,8 @@
Documentation mentions 'TODO'
Todo: android.app.NotificationManager#isNotificationAssistantAccessGranted(android.content.ComponentName):
Documentation mentions 'TODO'
+Todo: android.app.ondeviceintelligence.OnDeviceIntelligenceManager#requestFeatureDownload(android.app.ondeviceintelligence.Feature, android.app.ondeviceintelligence.CancellationSignal, java.util.concurrent.Executor, android.app.ondeviceintelligence.DownloadCallback):
+ Documentation mentions 'TODO'
Todo: android.hardware.camera2.params.StreamConfigurationMap:
Documentation mentions 'TODO'
Todo: android.hardware.location.ContextHubManager#getNanoAppInstanceInfo(int):
diff --git a/core/api/test-removed.txt b/core/api/test-removed.txt
index d802177..2e44176 100644
--- a/core/api/test-removed.txt
+++ b/core/api/test-removed.txt
@@ -1 +1,10 @@
// Signature format: 2.0
+package android.hardware.fingerprint {
+
+ @Deprecated public class FingerprintManager {
+ method @NonNull @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public android.hardware.biometrics.BiometricTestSession createTestSession(int);
+ method @NonNull @RequiresPermission(android.Manifest.permission.TEST_BIOMETRIC) public java.util.List<android.hardware.biometrics.SensorProperties> getSensorProperties();
+ }
+
+}
+
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index f7d7522..42c3272 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -69,6 +69,7 @@
import android.view.accessibility.AccessibilityWindowInfo;
import android.view.inputmethod.EditorInfo;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.inputmethod.CancellationGroup;
import com.android.internal.inputmethod.IAccessibilityInputMethodSession;
import com.android.internal.inputmethod.IAccessibilityInputMethodSessionCallback;
@@ -1388,7 +1389,9 @@
getFingerprintGestureController().onGesture(gesture);
}
- int getConnectionId() {
+ /** @hide */
+ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ public int getConnectionId() {
return mConnectionId;
}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 237d31c..f358522 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -5967,14 +5967,19 @@
}
/**
- * Used by {@link com.android.systemui.theme.ThemeOverlayController} to notify of color
- * palette readiness.
+ * Used by ThemeOverlayController to notify when color
+ * palette is ready.
+ *
+ * @param userId The ID of the user where ThemeOverlayController is ready.
+ *
+ * @throws RemoteException
+ *
* @hide
*/
@RequiresPermission(Manifest.permission.SET_THEME_OVERLAY_CONTROLLER_READY)
- public void setThemeOverlayReady(boolean readiness) {
+ public void setThemeOverlayReady(@UserIdInt int userId) {
try {
- getService().setThemeOverlayReady(readiness);
+ getService().setThemeOverlayReady(userId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 0ae2e01..062b89e 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -1264,5 +1264,5 @@
* palette readiness.
* @hide
*/
- public abstract boolean getThemeOverlayReadiness();
+ public abstract boolean isThemeOverlayReady(int userId);
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 926e297..e6a2c07 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -237,7 +237,6 @@
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.org.conscrypt.TrustedCertificateStore;
import com.android.server.am.MemInfoDumpProto;
-import com.android.window.flags.Flags;
import dalvik.annotation.optimization.NeverCompile;
import dalvik.system.AppSpecializationHooks;
@@ -3762,11 +3761,7 @@
final ClientTransaction clientTransaction = ClientTransaction.obtain(mAppThread);
final ActivityResultItem activityResultItem = ActivityResultItem.obtain(
activityToken, list);
- if (Flags.bundleClientTransactionFlag()) {
- clientTransaction.addTransactionItem(activityResultItem);
- } else {
- clientTransaction.addCallback(activityResultItem);
- }
+ clientTransaction.addTransactionItem(activityResultItem);
try {
mAppThread.scheduleTransaction(clientTransaction);
} catch (RemoteException e) {
@@ -4553,11 +4548,7 @@
final PauseActivityItem pauseActivityItem = PauseActivityItem.obtain(r.token,
r.activity.isFinishing(), /* userLeaving */ true, r.activity.mConfigChangeFlags,
/* dontReport */ false, /* autoEnteringPip */ false);
- if (Flags.bundleClientTransactionFlag()) {
- transaction.addTransactionItem(pauseActivityItem);
- } else {
- transaction.setLifecycleStateRequest(pauseActivityItem);
- }
+ transaction.addTransactionItem(pauseActivityItem);
executeTransaction(transaction);
}
@@ -4565,11 +4556,7 @@
final ClientTransaction transaction = ClientTransaction.obtain(mAppThread);
final ResumeActivityItem resumeActivityItem = ResumeActivityItem.obtain(r.token,
/* isForward */ false, /* shouldSendCompatFakeFocus */ false);
- if (Flags.bundleClientTransactionFlag()) {
- transaction.addTransactionItem(resumeActivityItem);
- } else {
- transaction.setLifecycleStateRequest(resumeActivityItem);
- }
+ transaction.addTransactionItem(resumeActivityItem);
executeTransaction(transaction);
}
@@ -6189,13 +6176,8 @@
TransactionExecutorHelper.getLifecycleRequestForCurrentState(r);
// Schedule the transaction.
final ClientTransaction transaction = ClientTransaction.obtain(mAppThread);
- if (Flags.bundleClientTransactionFlag()) {
- transaction.addTransactionItem(activityRelaunchItem);
- transaction.addTransactionItem(lifecycleRequest);
- } else {
- transaction.addCallback(activityRelaunchItem);
- transaction.setLifecycleStateRequest(lifecycleRequest);
- }
+ transaction.addTransactionItem(activityRelaunchItem);
+ transaction.addTransactionItem(lifecycleRequest);
executeTransaction(transaction);
}
diff --git a/core/java/android/app/ComponentCaller.java b/core/java/android/app/ComponentCaller.java
index 14bc003..7e6a9ac 100644
--- a/core/java/android/app/ComponentCaller.java
+++ b/core/java/android/app/ComponentCaller.java
@@ -137,16 +137,18 @@
* <li>This is not a real time check, i.e. the permissions have been computed at launch
* time.
* <li>This method will return the correct result for content URIs passed at launch time,
- * specifically the ones from {@link Intent#getData()}, and {@link Intent#getClipData()} in
- * the intent of {@code startActivity(intent)}. For others, it will throw an
- * {@link IllegalArgumentException}.
+ * specifically the ones from {@link Intent#getData()}, {@link Intent#EXTRA_STREAM}, and
+ * {@link Intent#getClipData()} in the intent of {@code startActivity(intent)}. For others,
+ * it will throw an {@link IllegalArgumentException}.
* </ul>
*
* @param uri The content uri that is being checked
* @param modeFlags The access modes to check
* @return {@link PackageManager#PERMISSION_GRANTED} if this activity caller is allowed to
* access that uri, or {@link PackageManager#PERMISSION_DENIED} if it is not
- * @throws IllegalArgumentException if uri is a non-content URI or it wasn't passed at launch
+ * @throws IllegalArgumentException if uri is a non-content URI or it wasn't passed at launch in
+ * {@link Intent#getData()}, {@link Intent#EXTRA_STREAM}, and
+ * {@link Intent#getClipData()}
* @throws SecurityException if you don't have access to uri
*
* @see android.content.Context#checkContentUriPermissionFull(Uri, int, int, int)
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index cc0aafd..7a95720 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -554,11 +554,14 @@
void bootAnimationComplete();
/**
- * Used by {@link com.android.systemui.theme.ThemeOverlayController} to notify of color
- * palette readiness.
+ * Used by {@link com.android.systemui.theme.ThemeOverlayController} to notify when color
+ * palette is ready.
+ *
+ * @param userId The ID of the user where ThemeOverlayController is ready.
+ *
* @throws RemoteException
*/
- void setThemeOverlayReady(boolean readiness);
+ void setThemeOverlayReady(int userId);
@UnsupportedAppUsage
void registerTaskStackListener(in ITaskStackListener listener);
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index b5e3556..8f81ae2 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -225,6 +225,7 @@
boolean removeAutomaticZenRule(String id, boolean fromUser);
boolean removeAutomaticZenRules(String packageName, boolean fromUser);
int getRuleInstanceCount(in ComponentName owner);
+ int getAutomaticZenRuleState(String id);
void setAutomaticZenRuleState(String id, in Condition condition);
byte[] getBackupPayload(int user);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index d6e8ae3..26f85f7 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -80,6 +80,7 @@
import android.os.Parcelable;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
@@ -3023,37 +3024,44 @@
* @hide
*/
public String loadHeaderAppName(Context context) {
- CharSequence name = null;
- // Check if there is a non-empty substitute app name and return that.
- if (extras.containsKey(EXTRA_SUBSTITUTE_APP_NAME)) {
- name = extras.getString(EXTRA_SUBSTITUTE_APP_NAME);
- if (!TextUtils.isEmpty(name)) {
- return name.toString();
- }
- }
- // If not, try getting the app info from extras.
- if (context == null) {
- return null;
- }
- final PackageManager pm = context.getPackageManager();
- if (TextUtils.isEmpty(name)) {
- if (extras.containsKey(EXTRA_BUILDER_APPLICATION_INFO)) {
- final ApplicationInfo info = extras.getParcelable(EXTRA_BUILDER_APPLICATION_INFO,
- ApplicationInfo.class);
- if (info != null) {
- name = pm.getApplicationLabel(info);
+ Trace.beginSection("Notification#loadHeaderAppName");
+
+ try {
+ CharSequence name = null;
+ // Check if there is a non-empty substitute app name and return that.
+ if (extras.containsKey(EXTRA_SUBSTITUTE_APP_NAME)) {
+ name = extras.getString(EXTRA_SUBSTITUTE_APP_NAME);
+ if (!TextUtils.isEmpty(name)) {
+ return name.toString();
}
}
+ // If not, try getting the app info from extras.
+ if (context == null) {
+ return null;
+ }
+ final PackageManager pm = context.getPackageManager();
+ if (TextUtils.isEmpty(name)) {
+ if (extras.containsKey(EXTRA_BUILDER_APPLICATION_INFO)) {
+ final ApplicationInfo info = extras.getParcelable(
+ EXTRA_BUILDER_APPLICATION_INFO,
+ ApplicationInfo.class);
+ if (info != null) {
+ name = pm.getApplicationLabel(info);
+ }
+ }
+ }
+ // If that's still empty, use the one from the context directly.
+ if (TextUtils.isEmpty(name)) {
+ name = pm.getApplicationLabel(context.getApplicationInfo());
+ }
+ // If there's still nothing, ¯\_(ツ)_/¯
+ if (TextUtils.isEmpty(name)) {
+ return null;
+ }
+ return name.toString();
+ } finally {
+ Trace.endSection();
}
- // If that's still empty, use the one from the context directly.
- if (TextUtils.isEmpty(name)) {
- name = pm.getApplicationLabel(context.getApplicationInfo());
- }
- // If there's still nothing, ¯\_(ツ)_/¯
- if (TextUtils.isEmpty(name)) {
- return null;
- }
- return name.toString();
}
/**
@@ -6722,23 +6730,29 @@
*/
@NonNull
public static Notification.Builder recoverBuilder(Context context, Notification n) {
- // Re-create notification context so we can access app resources.
- ApplicationInfo applicationInfo = n.extras.getParcelable(
- EXTRA_BUILDER_APPLICATION_INFO, ApplicationInfo.class);
- Context builderContext;
- if (applicationInfo != null) {
- try {
- builderContext = context.createApplicationContext(applicationInfo,
- Context.CONTEXT_RESTRICTED);
- } catch (NameNotFoundException e) {
- Log.e(TAG, "ApplicationInfo " + applicationInfo + " not found");
- builderContext = context; // try with our context
- }
- } else {
- builderContext = context; // try with given context
- }
+ Trace.beginSection("Notification.Builder#recoverBuilder");
- return new Builder(builderContext, n);
+ try {
+ // Re-create notification context so we can access app resources.
+ ApplicationInfo applicationInfo = n.extras.getParcelable(
+ EXTRA_BUILDER_APPLICATION_INFO, ApplicationInfo.class);
+ Context builderContext;
+ if (applicationInfo != null) {
+ try {
+ builderContext = context.createApplicationContext(applicationInfo,
+ Context.CONTEXT_RESTRICTED);
+ } catch (NameNotFoundException e) {
+ Log.e(TAG, "ApplicationInfo " + applicationInfo + " not found");
+ builderContext = context; // try with our context
+ }
+ } else {
+ builderContext = context; // try with given context
+ }
+
+ return new Builder(builderContext, n);
+ } finally {
+ Trace.endSection();
+ }
}
/**
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 9dfb5b0..d49a254 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -1406,6 +1406,26 @@
}
/**
+ * Returns the current activation state of an {@link AutomaticZenRule}.
+ *
+ * <p>Returns {@link Condition#STATE_UNKNOWN} if the rule does not exist or the calling
+ * package doesn't have access to it.
+ *
+ * @param id The id of the rule
+ * @return the state of the rule.
+ */
+ @FlaggedApi(Flags.FLAG_MODES_API)
+ @Condition.State
+ public int getAutomaticZenRuleState(@NonNull String id) {
+ INotificationManager service = getService();
+ try {
+ return service.getAutomaticZenRuleState(id);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Informs the notification manager that the state of an {@link AutomaticZenRule} has changed.
* Use this method to put the system into Do Not Disturb mode or request that it exits Do Not
* Disturb mode. The calling app must own the provided {@link android.app.AutomaticZenRule}.
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 24a5157..6255260 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -550,7 +550,7 @@
@UnsupportedAppUsage
protected @Nullable AssetManager createAssetManager(@NonNull final ResourcesKey key,
@Nullable ApkAssetsSupplier apkSupplier) {
- final AssetManager.Builder builder = new AssetManager.Builder();
+ final AssetManager.Builder builder = new AssetManager.Builder().setNoInit();
final ArrayList<ApkKey> apkKeys = extractApkKeys(key);
for (int i = 0, n = apkKeys.size(); i < n; i++) {
@@ -1555,7 +1555,7 @@
} else if(overlayPaths == null) {
return ArrayUtils.cloneOrNull(resourceDirs);
} else {
- final ArrayList<String> paths = new ArrayList<>();
+ final var paths = new ArrayList<String>(overlayPaths.length + resourceDirs.length);
for (final String path : overlayPaths) {
paths.add(path);
}
diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java
index a045eae..7903f1c 100644
--- a/core/java/android/app/UiModeManager.java
+++ b/core/java/android/app/UiModeManager.java
@@ -16,7 +16,7 @@
package android.app;
-import static android.app.Flags.enableNightModeCache;
+import static android.app.Flags.enableNightModeBinderCache;
import android.annotation.CallbackExecutor;
import android.annotation.FlaggedApi;
@@ -916,7 +916,7 @@
*
* @hide
*/
- @FlaggedApi(Flags.FLAG_ENABLE_NIGHT_MODE_CACHE)
+ @FlaggedApi(Flags.FLAG_ENABLE_NIGHT_MODE_BINDER_CACHE)
public static void invalidateNightModeCache() {
IpcDataCache.invalidateCache(IpcDataCache.MODULE_SYSTEM,
NIGHT_MODE_API);
@@ -938,7 +938,7 @@
* @see #setNightMode(int)
*/
public @NightMode int getNightMode() {
- if (enableNightModeCache()) {
+ if (enableNightModeBinderCache()) {
return mNightModeCache.query(null);
} else {
return getNightModeFromServer();
diff --git a/core/java/android/app/admin/DeviceAdminInfo.java b/core/java/android/app/admin/DeviceAdminInfo.java
index 7d5d5c1..986205a 100644
--- a/core/java/android/app/admin/DeviceAdminInfo.java
+++ b/core/java/android/app/admin/DeviceAdminInfo.java
@@ -16,10 +16,11 @@
package android.app.admin;
+import static android.app.admin.flags.Flags.FLAG_HEADLESS_DEVICE_OWNER_SINGLE_USER_ENABLED;
+
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
-import android.app.admin.flags.Flags;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
@@ -185,7 +186,7 @@
* <p>This mode only allows a single secondary user on the device blocking the creation of
* additional secondary users.
*/
- @FlaggedApi(Flags.FLAG_HEADLESS_DEVICE_OWNER_SINGLE_USER_ENABLED)
+ @FlaggedApi(FLAG_HEADLESS_DEVICE_OWNER_SINGLE_USER_ENABLED)
public static final int HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER = 2;
@IntDef({HEADLESS_DEVICE_OWNER_MODE_UNSUPPORTED, HEADLESS_DEVICE_OWNER_MODE_AFFILIATED,
diff --git a/core/java/android/app/admin/DevicePolicyIdentifiers.java b/core/java/android/app/admin/DevicePolicyIdentifiers.java
index 3c56aaf..eeaf0b3 100644
--- a/core/java/android/app/admin/DevicePolicyIdentifiers.java
+++ b/core/java/android/app/admin/DevicePolicyIdentifiers.java
@@ -16,13 +16,13 @@
package android.app.admin;
+import static android.app.admin.flags.Flags.FLAG_POLICY_ENGINE_MIGRATION_V2_ENABLED;
import static android.app.admin.flags.Flags.FLAG_SECURITY_LOG_V2_ENABLED;
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.annotation.TestApi;
-import android.app.admin.flags.Flags;
import android.os.UserManager;
import java.util.Objects;
@@ -188,13 +188,13 @@
/**
* String identifier for {@link DevicePolicyManager#setUsbDataSignalingEnabled}.
*/
- @FlaggedApi(Flags.FLAG_POLICY_ENGINE_MIGRATION_V2_ENABLED)
+ @FlaggedApi(FLAG_POLICY_ENGINE_MIGRATION_V2_ENABLED)
public static final String USB_DATA_SIGNALING_POLICY = "usbDataSignaling";
/**
* String identifier for {@link DevicePolicyManager#setRequiredPasswordComplexity}.
*/
- @FlaggedApi(Flags.FLAG_POLICY_ENGINE_MIGRATION_V2_ENABLED)
+ @FlaggedApi(FLAG_POLICY_ENGINE_MIGRATION_V2_ENABLED)
public static final String PASSWORD_COMPLEXITY_POLICY = "passwordComplexity";
/**
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index a6fda9d..083705b 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -53,8 +53,11 @@
import static android.Manifest.permission.REQUEST_PASSWORD_COMPLEXITY;
import static android.Manifest.permission.SET_TIME;
import static android.Manifest.permission.SET_TIME_ZONE;
+import static android.app.admin.flags.Flags.FLAG_DEVICE_THEFT_API_ENABLED;
import static android.app.admin.flags.Flags.FLAG_ESIM_MANAGEMENT_ENABLED;
import static android.app.admin.flags.Flags.FLAG_DEVICE_POLICY_SIZE_TRACKING_ENABLED;
+import static android.app.admin.flags.Flags.FLAG_HEADLESS_DEVICE_OWNER_SINGLE_USER_ENABLED;
+import static android.app.admin.flags.Flags.FLAG_PERMISSION_MIGRATION_FOR_ZERO_TRUST_API_ENABLED;
import static android.app.admin.flags.Flags.FLAG_SECURITY_LOG_V2_ENABLED;
import static android.app.admin.flags.Flags.onboardingBugreportV2Enabled;
import static android.app.admin.flags.Flags.FLAG_IS_MTE_POLICY_ENFORCED;
@@ -90,7 +93,6 @@
import android.app.IServiceConnection;
import android.app.KeyguardManager;
import android.app.admin.SecurityLog.SecurityEvent;
-import android.app.admin.flags.Flags;
import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
@@ -153,10 +155,10 @@
import com.android.internal.infra.AndroidFuture;
import com.android.internal.net.NetworkUtilsInternal;
import com.android.internal.os.BackgroundThread;
+import com.android.internal.os.Zygote;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.Preconditions;
import com.android.org.conscrypt.TrustedCertificateStore;
-import com.android.internal.os.Zygote;
import java.io.ByteArrayInputStream;
import java.io.FileNotFoundException;
@@ -2879,7 +2881,7 @@
* @hide
*/
@SystemApi
- @FlaggedApi(Flags.FLAG_HEADLESS_DEVICE_OWNER_SINGLE_USER_ENABLED)
+ @FlaggedApi(FLAG_HEADLESS_DEVICE_OWNER_SINGLE_USER_ENABLED)
public static final int STATUS_HEADLESS_ONLY_SYSTEM_USER = 17;
/**
@@ -13447,7 +13449,7 @@
*/
@RequiresPermission(value = MANAGE_DEVICE_POLICY_QUERY_SYSTEM_UPDATES, conditional = true)
@SuppressLint("RequiresPermission")
- @FlaggedApi(Flags.FLAG_PERMISSION_MIGRATION_FOR_ZERO_TRUST_API_ENABLED)
+ @FlaggedApi(FLAG_PERMISSION_MIGRATION_FOR_ZERO_TRUST_API_ENABLED)
public @Nullable SystemUpdateInfo getPendingSystemUpdate(@Nullable ComponentName admin) {
throwIfParentInstance("getPendingSystemUpdate");
try {
@@ -16608,7 +16610,7 @@
*/
@RequiresPermission(value = MANAGE_DEVICE_POLICY_CERTIFICATES, conditional = true)
@SuppressLint("RequiresPermission")
- @FlaggedApi(Flags.FLAG_PERMISSION_MIGRATION_FOR_ZERO_TRUST_API_ENABLED)
+ @FlaggedApi(FLAG_PERMISSION_MIGRATION_FOR_ZERO_TRUST_API_ENABLED)
@NonNull public String getEnrollmentSpecificId() {
throwIfParentInstance("getEnrollmentSpecificId");
if (mService == null) {
@@ -17134,7 +17136,7 @@
*/
@SystemApi
@RequiresPermission(value = MANAGE_DEVICE_POLICY_THEFT_DETECTION)
- @FlaggedApi(Flags.FLAG_DEVICE_THEFT_API_ENABLED)
+ @FlaggedApi(FLAG_DEVICE_THEFT_API_ENABLED)
public boolean isTheftDetectionTriggered() {
throwIfParentInstance("isTheftDetectionTriggered");
if (mService == null) {
diff --git a/core/java/android/app/admin/SecurityLog.java b/core/java/android/app/admin/SecurityLog.java
index ed1b8ca..477f2e0 100644
--- a/core/java/android/app/admin/SecurityLog.java
+++ b/core/java/android/app/admin/SecurityLog.java
@@ -16,6 +16,8 @@
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;
@@ -24,7 +26,6 @@
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.TestApi;
-import android.app.admin.flags.Flags;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.os.Build;
@@ -610,7 +611,7 @@
* <li> [2] backup service state ({@code Integer}, 1 for enabled, 0 for disabled)
* @see DevicePolicyManager#setBackupServiceEnabled(ComponentName, boolean)
*/
- @FlaggedApi(Flags.FLAG_BACKUP_SERVICE_SECURITY_LOG_EVENT_ENABLED)
+ @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/ondeviceintelligence/Content.aidl b/core/java/android/app/ondeviceintelligence/Content.aidl
new file mode 100644
index 0000000..40f0ef9
--- /dev/null
+++ b/core/java/android/app/ondeviceintelligence/Content.aidl
@@ -0,0 +1,22 @@
+/**
+ * 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.ondeviceintelligence;
+
+/**
+ * @hide
+ */
+parcelable Content;
diff --git a/core/java/android/app/ondeviceintelligence/Content.java b/core/java/android/app/ondeviceintelligence/Content.java
new file mode 100644
index 0000000..51bd156
--- /dev/null
+++ b/core/java/android/app/ondeviceintelligence/Content.java
@@ -0,0 +1,90 @@
+/*
+ * 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.ondeviceintelligence;
+
+import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Represents content sent to and received from the on-device inference service.
+ * Can contain a collection of text, image, and binary parts or any combination of these.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(FLAG_ENABLE_ON_DEVICE_INTELLIGENCE)
+public final class Content implements Parcelable {
+ //TODO: Improve javadoc after adding validation logic.
+ private static final String TAG = "Content";
+ private final Bundle mData;
+
+ /**
+ * Create a content object using a Bundle of only known types that are read-only.
+ */
+ public Content(@NonNull Bundle data) {
+ Objects.requireNonNull(data);
+ validateBundleData(data);
+ this.mData = data;
+ }
+
+ /**
+ * Returns the Content's data represented as a Bundle.
+ */
+ @NonNull
+ public Bundle getData() {
+ return mData;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeBundle(mData);
+ }
+
+ @Override
+ public int describeContents() {
+ int mask = 0;
+ mask |= mData.describeContents();
+ return mask;
+ }
+
+ @NonNull
+ public static final Creator<Content> CREATOR = new Creator<>() {
+ @Override
+ @NonNull
+ public Content createFromParcel(@NonNull Parcel in) {
+ return new Content(in.readBundle(getClass().getClassLoader()));
+ }
+
+ @Override
+ @NonNull
+ public Content[] newArray(int size) {
+ return new Content[size];
+ }
+ };
+
+ private void validateBundleData(Bundle unused) {
+ // TODO: Validate there are only known types.
+ }
+}
diff --git a/core/java/android/app/ondeviceintelligence/DownloadCallback.java b/core/java/android/app/ondeviceintelligence/DownloadCallback.java
new file mode 100644
index 0000000..684c71f
--- /dev/null
+++ b/core/java/android/app/ondeviceintelligence/DownloadCallback.java
@@ -0,0 +1,114 @@
+/*
+ * 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.ondeviceintelligence;
+
+import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.PersistableBundle;
+
+import androidx.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Callback functions used for feature downloading via the
+ * {@link OnDeviceIntelligenceManager#requestFeatureDownload}.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(FLAG_ENABLE_ON_DEVICE_INTELLIGENCE)
+public interface DownloadCallback {
+ int DOWNLOAD_FAILURE_STATUS_UNKNOWN = 0;
+
+ /**
+ * Sent when feature download could not succeed due to there being no available disk space on
+ * the device.
+ */
+ int DOWNLOAD_FAILURE_STATUS_NOT_ENOUGH_DISK_SPACE = 1;
+
+ /**
+ * Sent when feature download could not succeed due to a network error.
+ */
+ int DOWNLOAD_FAILURE_STATUS_NETWORK_FAILURE = 2;
+
+ /**
+ * Sent when feature download has been initiated already, hence no need to request download
+ * again. Caller can query {@link OnDeviceIntelligenceManager#getFeatureStatus} to check if
+ * download has been completed.
+ */
+ int DOWNLOAD_FAILURE_STATUS_DOWNLOADING = 3;
+
+ /**
+ * Sent when feature download did not start due to errors (e.g. remote exception of features not
+ * available). Caller can query {@link OnDeviceIntelligenceManager#getFeatureStatus} to check
+ * if feature-status is {@link FeatureDetails#FEATURE_STATUS_DOWNLOADABLE}.
+ */
+ int DOWNLOAD_FAILURE_STATUS_UNAVAILABLE = 4;
+
+ /** @hide */
+ @IntDef(value = {
+ DOWNLOAD_FAILURE_STATUS_UNKNOWN,
+ DOWNLOAD_FAILURE_STATUS_NOT_ENOUGH_DISK_SPACE,
+ DOWNLOAD_FAILURE_STATUS_NETWORK_FAILURE,
+ DOWNLOAD_FAILURE_STATUS_DOWNLOADING,
+ DOWNLOAD_FAILURE_STATUS_UNAVAILABLE
+ }, open = true)
+ @Retention(RetentionPolicy.SOURCE)
+ @interface DownloadFailureStatus {
+ }
+
+ /**
+ * Called when model download started properly.
+ *
+ * @param bytesToDownload the total bytes to be downloaded for this {@link Feature}
+ */
+ default void onDownloadStarted(long bytesToDownload) {
+ }
+
+ /**
+ * Called when model download failed.
+ *
+ * @param failureStatus the download failure status
+ * @param errorMessage the error message associated with the download failure
+ */
+ void onDownloadFailed(
+ @DownloadFailureStatus int failureStatus,
+ @Nullable String errorMessage,
+ @NonNull PersistableBundle errorParams);
+
+ /**
+ * Called when model download is in progress.
+ *
+ * @param totalBytesDownloaded the already downloaded bytes for this {@link Feature}
+ */
+ default void onDownloadProgress(long totalBytesDownloaded) {
+ }
+
+ /**
+ * Called when model download via MDD completed. The remote implementation can populate any
+ * associated download params like file stats etc. in this callback to inform the client.
+ *
+ * @param downloadParams params containing info about the completed download.
+ */
+ void onDownloadCompleted(@NonNull PersistableBundle downloadParams);
+}
diff --git a/core/java/android/app/ondeviceintelligence/Feature.aidl b/core/java/android/app/ondeviceintelligence/Feature.aidl
new file mode 100644
index 0000000..18494d7
--- /dev/null
+++ b/core/java/android/app/ondeviceintelligence/Feature.aidl
@@ -0,0 +1,22 @@
+/*
+ * 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.ondeviceintelligence;
+
+/**
+ * @hide
+ */
+parcelable Feature;
diff --git a/core/java/android/app/ondeviceintelligence/Feature.java b/core/java/android/app/ondeviceintelligence/Feature.java
new file mode 100644
index 0000000..5107354
--- /dev/null
+++ b/core/java/android/app/ondeviceintelligence/Feature.java
@@ -0,0 +1,279 @@
+/*
+ * 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.ondeviceintelligence;
+
+import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.PersistableBundle;
+
+/**
+ * Represents a typical feature associated with on-device intelligence.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(FLAG_ENABLE_ON_DEVICE_INTELLIGENCE)
+public final class Feature implements Parcelable {
+ // TODO(b/325315604) - Check if we can expose non-hidden IntDefs in Framework.
+ private final int mId;
+ @Nullable
+ private final String mName;
+ @Nullable
+ private final String mModelName;
+ private final int mType;
+ private final int mVariant;
+ @NonNull
+ private final PersistableBundle mFeatureParams;
+
+ /* package-private */ Feature(
+ int id,
+ @Nullable String name,
+ @Nullable String modelName,
+ int type,
+ int variant,
+ @NonNull PersistableBundle featureParams) {
+ this.mId = id;
+ this.mName = name;
+ this.mModelName = modelName;
+ this.mType = type;
+ this.mVariant = variant;
+ this.mFeatureParams = featureParams;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mFeatureParams);
+ }
+
+ /** Returns the unique and immutable identifier of this feature. */
+ public int getId() {
+ return mId;
+ }
+
+ /** Returns human-readable name of this feature. */
+ public @Nullable String getName() {
+ return mName;
+ }
+
+ /** Returns base model name of this feature. */
+ public @Nullable String getModelName() {
+ return mModelName;
+ }
+
+ /** Returns type identifier of this feature. */
+ public int getType() {
+ return mType;
+ }
+
+ /** Returns variant kind for this feature. */
+ public int getVariant() {
+ return mVariant;
+ }
+
+ public @NonNull PersistableBundle getFeatureParams() {
+ return mFeatureParams;
+ }
+
+ @Override
+ public String toString() {
+ return "Feature { " +
+ "id = " + mId + ", " +
+ "name = " + mName + ", " +
+ "modelName = " + mModelName + ", " +
+ "type = " + mType + ", " +
+ "variant = " + mVariant + ", " +
+ "featureParams = " + mFeatureParams +
+ " }";
+ }
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ @SuppressWarnings("unchecked")
+ Feature that = (Feature) o;
+ //noinspection PointlessBooleanExpression
+ return true
+ && mId == that.mId
+ && java.util.Objects.equals(mName, that.mName)
+ && java.util.Objects.equals(mModelName, that.mModelName)
+ && mType == that.mType
+ && mVariant == that.mVariant
+ && java.util.Objects.equals(mFeatureParams, that.mFeatureParams);
+ }
+
+ @Override
+ public int hashCode() {
+ int _hash = 1;
+ _hash = 31 * _hash + mId;
+ _hash = 31 * _hash + java.util.Objects.hashCode(mName);
+ _hash = 31 * _hash + java.util.Objects.hashCode(mModelName);
+ _hash = 31 * _hash + mType;
+ _hash = 31 * _hash + mVariant;
+ _hash = 31 * _hash + java.util.Objects.hashCode(mFeatureParams);
+ return _hash;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ byte flg = 0;
+ if (mName != null) flg |= 0x2;
+ if (mModelName != null) flg |= 0x4;
+ dest.writeByte(flg);
+ dest.writeInt(mId);
+ if (mName != null) dest.writeString8(mName);
+ if (mModelName != null) dest.writeString8(mModelName);
+ dest.writeInt(mType);
+ dest.writeInt(mVariant);
+ dest.writeTypedObject(mFeatureParams, flags);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ /* package-private */ Feature(@NonNull Parcel in) {
+ byte flg = in.readByte();
+ int id = in.readInt();
+ String name = (flg & 0x2) == 0 ? null : in.readString();
+ String modelName = (flg & 0x4) == 0 ? null : in.readString();
+ int type = in.readInt();
+ int variant = in.readInt();
+ PersistableBundle featureParams = (PersistableBundle) in.readTypedObject(
+ PersistableBundle.CREATOR);
+
+ this.mId = id;
+ this.mName = name;
+ this.mModelName = modelName;
+ this.mType = type;
+ this.mVariant = variant;
+ this.mFeatureParams = featureParams;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mFeatureParams);
+ }
+
+ public static final @NonNull Parcelable.Creator<Feature> CREATOR
+ = new Parcelable.Creator<Feature>() {
+ @Override
+ public Feature[] newArray(int size) {
+ return new Feature[size];
+ }
+
+ @Override
+ public Feature createFromParcel(@NonNull Parcel in) {
+ return new Feature(in);
+ }
+ };
+
+ /**
+ * A builder for {@link Feature}
+ */
+ @SuppressWarnings("WeakerAccess")
+ public static final class Builder {
+ private int mId;
+ private @Nullable String mName;
+ private @Nullable String mModelName;
+ private int mType;
+ private int mVariant;
+ private @NonNull PersistableBundle mFeatureParams;
+
+ private long mBuilderFieldsSet = 0L;
+
+ public Builder(
+ int id,
+ int type,
+ int variant,
+ @NonNull PersistableBundle featureParams) {
+ mId = id;
+ mType = type;
+ mVariant = variant;
+ mFeatureParams = featureParams;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mFeatureParams);
+ }
+
+ public @NonNull Builder setId(int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x1;
+ mId = value;
+ return this;
+ }
+
+ public @NonNull Builder setName(@NonNull String value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x2;
+ mName = value;
+ return this;
+ }
+
+ public @NonNull Builder setModelName(@NonNull String value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x4;
+ mModelName = value;
+ return this;
+ }
+
+ public @NonNull Builder setType(int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x8;
+ mType = value;
+ return this;
+ }
+
+ public @NonNull Builder setVariant(int value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x10;
+ mVariant = value;
+ return this;
+ }
+
+ public @NonNull Builder setFeatureParams(@NonNull PersistableBundle value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x20;
+ mFeatureParams = value;
+ return this;
+ }
+
+ /** Builds the instance. This builder should not be touched after calling this! */
+ public @NonNull Feature build() {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x40; // Mark builder used
+
+ Feature o = new Feature(
+ mId,
+ mName,
+ mModelName,
+ mType,
+ mVariant,
+ mFeatureParams);
+ return o;
+ }
+
+ private void checkNotUsed() {
+ if ((mBuilderFieldsSet & 0x40) != 0) {
+ throw new IllegalStateException(
+ "This Builder should not be reused. Use a new Builder instance instead");
+ }
+ }
+ }
+}
diff --git a/core/java/android/app/ondeviceintelligence/FeatureDetails.aidl b/core/java/android/app/ondeviceintelligence/FeatureDetails.aidl
new file mode 100644
index 0000000..0589bf8
--- /dev/null
+++ b/core/java/android/app/ondeviceintelligence/FeatureDetails.aidl
@@ -0,0 +1,22 @@
+/*
+ * 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.ondeviceintelligence;
+
+/**
+ * @hide
+ */
+parcelable FeatureDetails;
diff --git a/core/java/android/app/ondeviceintelligence/FeatureDetails.java b/core/java/android/app/ondeviceintelligence/FeatureDetails.java
new file mode 100644
index 0000000..92f3513
--- /dev/null
+++ b/core/java/android/app/ondeviceintelligence/FeatureDetails.java
@@ -0,0 +1,176 @@
+/*
+ * 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.ondeviceintelligence;
+
+import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcelable;
+import android.os.PersistableBundle;
+
+import androidx.annotation.IntDef;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.text.MessageFormat;
+
+/**
+ * Represents a status of a requested {@link Feature}.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(FLAG_ENABLE_ON_DEVICE_INTELLIGENCE)
+public final class FeatureDetails implements Parcelable {
+ @Status
+ private final int mStatus;
+ @NonNull
+ private final PersistableBundle mFeatureDetailParams;
+
+ /** Invalid or unavailable {@code AiFeature}. */
+ public static final int FEATURE_STATUS_UNAVAILABLE = 0;
+
+ /** Feature can be downloaded on request. */
+ public static final int FEATURE_STATUS_DOWNLOADABLE = 1;
+
+ /** Feature is being downloaded. */
+ public static final int FEATURE_STATUS_DOWNLOADING = 2;
+
+ /** Feature is fully downloaded and ready to use. */
+ public static final int FEATURE_STATUS_AVAILABLE = 3;
+
+ /** Underlying service is unavailable and feature status cannot be fetched. */
+ public static final int FEATURE_STATUS_SERVICE_UNAVAILABLE = 4;
+
+ @IntDef(value = {
+ FEATURE_STATUS_UNAVAILABLE,
+ FEATURE_STATUS_DOWNLOADABLE,
+ FEATURE_STATUS_DOWNLOADING,
+ FEATURE_STATUS_AVAILABLE,
+ FEATURE_STATUS_SERVICE_UNAVAILABLE
+ }, open = true)
+ @Target({ElementType.TYPE_USE, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Status {
+ }
+
+ public FeatureDetails(
+ @Status int status,
+ @NonNull PersistableBundle featureDetailParams) {
+ this.mStatus = status;
+ com.android.internal.util.AnnotationValidations.validate(
+ Status.class, null, mStatus);
+ this.mFeatureDetailParams = featureDetailParams;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mFeatureDetailParams);
+ }
+
+ public FeatureDetails(
+ @Status int status) {
+ this.mStatus = status;
+ com.android.internal.util.AnnotationValidations.validate(
+ Status.class, null, mStatus);
+ this.mFeatureDetailParams = new PersistableBundle();
+ }
+
+
+ /**
+ * Returns an integer value associated with the feature status.
+ */
+ public @Status int getStatus() {
+ return mStatus;
+ }
+
+
+ /**
+ * Returns a persistable bundle contain any additional status related params.
+ */
+ public @NonNull PersistableBundle getFeatureDetailParams() {
+ return mFeatureDetailParams;
+ }
+
+ @Override
+ public String toString() {
+ return MessageFormat.format("FeatureDetails '{' status = {0}, "
+ + "persistableBundle = {1} '}'",
+ mStatus,
+ mFeatureDetailParams);
+ }
+
+ @Override
+ public boolean equals(@android.annotation.Nullable Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ @SuppressWarnings("unchecked")
+ FeatureDetails that = (FeatureDetails) o;
+ return mStatus == that.mStatus
+ && java.util.Objects.equals(mFeatureDetailParams, that.mFeatureDetailParams);
+ }
+
+ @Override
+ public int hashCode() {
+ int _hash = 1;
+ _hash = 31 * _hash + mStatus;
+ _hash = 31 * _hash + java.util.Objects.hashCode(mFeatureDetailParams);
+ return _hash;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
+ dest.writeInt(mStatus);
+ dest.writeTypedObject(mFeatureDetailParams, flags);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /** @hide */
+ @SuppressWarnings({"unchecked", "RedundantCast"})
+ FeatureDetails(@NonNull android.os.Parcel in) {
+ int status = in.readInt();
+ PersistableBundle persistableBundle = (PersistableBundle) in.readTypedObject(
+ PersistableBundle.CREATOR);
+
+ this.mStatus = status;
+ com.android.internal.util.AnnotationValidations.validate(
+ Status.class, null, mStatus);
+ this.mFeatureDetailParams = persistableBundle;
+ com.android.internal.util.AnnotationValidations.validate(
+ NonNull.class, null, mFeatureDetailParams);
+ }
+
+
+ public static final @NonNull Parcelable.Creator<FeatureDetails> CREATOR =
+ new Parcelable.Creator<>() {
+ @Override
+ public FeatureDetails[] newArray(int size) {
+ return new FeatureDetails[size];
+ }
+
+ @Override
+ public FeatureDetails createFromParcel(@NonNull android.os.Parcel in) {
+ return new FeatureDetails(in);
+ }
+ };
+
+}
diff --git a/core/java/android/app/ondeviceintelligence/FilePart.java b/core/java/android/app/ondeviceintelligence/FilePart.java
new file mode 100644
index 0000000..e9fb5f2
--- /dev/null
+++ b/core/java/android/app/ondeviceintelligence/FilePart.java
@@ -0,0 +1,137 @@
+/*
+ * 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.ondeviceintelligence;
+
+import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE;
+
+import android.annotation.FlaggedApi;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
+import android.os.Parcelable;
+import android.os.PersistableBundle;
+
+import android.annotation.NonNull;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.Objects;
+
+/**
+ * Represents file data with an associated file descriptor sent to and received from remote
+ * processing. The interface ensures that the underlying file-descriptor is always opened in
+ * read-only mode.
+ *
+ * @hide
+ */
+@FlaggedApi(FLAG_ENABLE_ON_DEVICE_INTELLIGENCE)
+@SystemApi
+public final class FilePart implements Parcelable {
+ private final String mPartKey;
+ private final PersistableBundle mPartParams;
+ private final ParcelFileDescriptor mParcelFileDescriptor;
+
+ private FilePart(@NonNull String partKey, @NonNull PersistableBundle partParams,
+ @NonNull ParcelFileDescriptor parcelFileDescriptor) {
+ Objects.requireNonNull(partKey);
+ Objects.requireNonNull(partParams);
+ this.mPartKey = partKey;
+ this.mPartParams = partParams;
+ this.mParcelFileDescriptor = Objects.requireNonNull(parcelFileDescriptor);
+ }
+
+ /**
+ * Create a file part using a filePath and any additional params.
+ */
+ public FilePart(@NonNull String partKey, @NonNull PersistableBundle partParams,
+ @NonNull String filePath)
+ throws FileNotFoundException {
+ this(partKey, partParams, Objects.requireNonNull(ParcelFileDescriptor.open(
+ new File(Objects.requireNonNull(filePath)), ParcelFileDescriptor.MODE_READ_ONLY)));
+ }
+
+ /**
+ * Create a file part using a file input stream and any additional params.
+ * It is the caller's responsibility to close the stream. It is safe to do so as soon as this
+ * call returns.
+ */
+ public FilePart(@NonNull String partKey, @NonNull PersistableBundle partParams,
+ @NonNull FileInputStream fileInputStream)
+ throws IOException {
+ this(partKey, partParams, ParcelFileDescriptor.dup(fileInputStream.getFD()));
+ }
+
+ /**
+ * Returns a FileInputStream for the associated File.
+ * Caller must close the associated stream when done reading from it.
+ *
+ * @return the FileInputStream associated with the FilePart.
+ */
+ @NonNull
+ public FileInputStream getFileInputStream() {
+ return new FileInputStream(mParcelFileDescriptor.getFileDescriptor());
+ }
+
+ /**
+ * Returns the unique key associated with the part. Each Part key added to a content object
+ * should be ensured to be unique.
+ */
+ @NonNull
+ public String getFilePartKey() {
+ return mPartKey;
+ }
+
+ /**
+ * Returns the params associated with Part.
+ */
+ @NonNull
+ public PersistableBundle getFilePartParams() {
+ return mPartParams;
+ }
+
+
+ @Override
+ public int describeContents() {
+ return CONTENTS_FILE_DESCRIPTOR;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString8(getFilePartKey());
+ dest.writePersistableBundle(getFilePartParams());
+ mParcelFileDescriptor.writeToParcel(dest, flags
+ | Parcelable.PARCELABLE_WRITE_RETURN_VALUE); // This flag ensures that the sender's
+ // copy of the Pfd is closed as soon as the Binder call succeeds.
+ }
+
+ @NonNull
+ public static final Creator<FilePart> CREATOR = new Creator<>() {
+ @Override
+ public FilePart createFromParcel(Parcel in) {
+ return new FilePart(in.readString(), in.readTypedObject(PersistableBundle.CREATOR),
+ in.readParcelable(
+ getClass().getClassLoader(), ParcelFileDescriptor.class));
+ }
+
+ @Override
+ public FilePart[] newArray(int size) {
+ return new FilePart[size];
+ }
+ };
+}
diff --git a/core/java/android/app/ondeviceintelligence/IDownloadCallback.aidl b/core/java/android/app/ondeviceintelligence/IDownloadCallback.aidl
new file mode 100644
index 0000000..aba563f
--- /dev/null
+++ b/core/java/android/app/ondeviceintelligence/IDownloadCallback.aidl
@@ -0,0 +1,32 @@
+/*
+ * 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.ondeviceintelligence;
+
+import android.app.ondeviceintelligence.IProcessingSignal;
+import android.os.PersistableBundle;
+
+/**
+ * Interface for Download callback to passed onto service implementation,
+ *
+ * @hide
+ */
+oneway interface IDownloadCallback {
+ void onDownloadStarted(long bytesToDownload) = 1;
+ void onDownloadProgress(long bytesDownloaded) = 2;
+ void onDownloadFailed(int failureStatus, String errorMessage, in PersistableBundle errorParams) = 3;
+ void onDownloadCompleted(in PersistableBundle downloadParams) = 4;
+}
\ No newline at end of file
diff --git a/core/java/android/app/ondeviceintelligence/IFeatureCallback.aidl b/core/java/android/app/ondeviceintelligence/IFeatureCallback.aidl
new file mode 100644
index 0000000..93a84ec
--- /dev/null
+++ b/core/java/android/app/ondeviceintelligence/IFeatureCallback.aidl
@@ -0,0 +1,14 @@
+package android.app.ondeviceintelligence;
+
+import android.app.ondeviceintelligence.Feature;
+import android.os.PersistableBundle;
+
+/**
+ * Interface for receiving a feature for the given identifier.
+ *
+ * @hide
+ */
+interface IFeatureCallback {
+ void onSuccess(in Feature result) = 1;
+ void onFailure(int errorCode, in String errorMessage, in PersistableBundle errorParams) = 2;
+}
diff --git a/core/java/android/app/ondeviceintelligence/IFeatureDetailsCallback.aidl b/core/java/android/app/ondeviceintelligence/IFeatureDetailsCallback.aidl
new file mode 100644
index 0000000..d950290
--- /dev/null
+++ b/core/java/android/app/ondeviceintelligence/IFeatureDetailsCallback.aidl
@@ -0,0 +1,14 @@
+package android.app.ondeviceintelligence;
+
+import android.app.ondeviceintelligence.FeatureDetails;
+import android.os.PersistableBundle;
+
+/**
+ * Interface for receiving details about a given feature. .
+ *
+ * @hide
+ */
+interface IFeatureDetailsCallback {
+ void onSuccess(in FeatureDetails result) = 1;
+ void onFailure(int errorCode, in String errorMessage, in PersistableBundle errorParams) = 2;
+}
diff --git a/core/java/android/app/ondeviceintelligence/IListFeaturesCallback.aidl b/core/java/android/app/ondeviceintelligence/IListFeaturesCallback.aidl
new file mode 100644
index 0000000..374cb71
--- /dev/null
+++ b/core/java/android/app/ondeviceintelligence/IListFeaturesCallback.aidl
@@ -0,0 +1,15 @@
+package android.app.ondeviceintelligence;
+
+import java.util.List;
+import android.app.ondeviceintelligence.Feature;
+import android.os.PersistableBundle;
+
+/**
+ * Interface for receiving list of supported features.
+ *
+ * @hide
+ */
+interface IListFeaturesCallback {
+ void onSuccess(in List<Feature> result) = 1;
+ void onFailure(int errorCode, in String errorMessage, in PersistableBundle errorParams) = 2;
+}
diff --git a/core/java/android/app/ondeviceintelligence/IOnDeviceIntelligenceManager.aidl b/core/java/android/app/ondeviceintelligence/IOnDeviceIntelligenceManager.aidl
new file mode 100644
index 0000000..b925f48
--- /dev/null
+++ b/core/java/android/app/ondeviceintelligence/IOnDeviceIntelligenceManager.aidl
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ package android.app.ondeviceintelligence;
+
+ import com.android.internal.infra.AndroidFuture;
+ import android.os.ICancellationSignal;
+ import android.os.ParcelFileDescriptor;
+ import android.os.PersistableBundle;
+ import android.os.RemoteCallback;
+ import android.app.ondeviceintelligence.Content;
+ import android.app.ondeviceintelligence.Feature;
+ import android.app.ondeviceintelligence.FeatureDetails;
+ import android.app.ondeviceintelligence.IDownloadCallback;
+ import android.app.ondeviceintelligence.IListFeaturesCallback;
+ import android.app.ondeviceintelligence.IFeatureCallback;
+ import android.app.ondeviceintelligence.IFeatureDetailsCallback;
+ import android.app.ondeviceintelligence.IResponseCallback;
+ import android.app.ondeviceintelligence.IStreamingResponseCallback;
+ import android.app.ondeviceintelligence.IProcessingSignal;
+ import android.app.ondeviceintelligence.ITokenCountCallback;
+
+
+ /**
+ * Interface for a OnDeviceIntelligenceManager for managing OnDeviceIntelligenceService and OnDeviceSandboxedInferenceService.
+ *
+ * @hide
+ */
+ oneway interface IOnDeviceIntelligenceManager {
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)")
+ void getVersion(in RemoteCallback remoteCallback) = 1;
+
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)")
+ void getFeature(in int featureId, in IFeatureCallback remoteCallback) = 2;
+
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)")
+ void listFeatures(in IListFeaturesCallback listFeaturesCallback) = 3;
+
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)")
+ void getFeatureDetails(in Feature feature, in IFeatureDetailsCallback featureDetailsCallback) = 4;
+
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)")
+ void requestFeatureDownload(in Feature feature, ICancellationSignal signal, in IDownloadCallback callback) = 5;
+
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)")
+ void requestTokenCount(in Feature feature, in Content request, in ICancellationSignal signal,
+ in ITokenCountCallback tokenCountcallback) = 6;
+
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)")
+ void processRequest(in Feature feature, in Content request, int requestType, in ICancellationSignal cancellationSignal, in IProcessingSignal signal,
+ in IResponseCallback responseCallback) = 7;
+
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)")
+ void processRequestStreaming(in Feature feature,
+ in Content request, int requestType, in ICancellationSignal cancellationSignal, in IProcessingSignal signal,
+ in IStreamingResponseCallback streamingCallback) = 8;
+ }
diff --git a/core/java/android/app/ondeviceintelligence/IProcessingSignal.aidl b/core/java/android/app/ondeviceintelligence/IProcessingSignal.aidl
new file mode 100644
index 0000000..03946ee
--- /dev/null
+++ b/core/java/android/app/ondeviceintelligence/IProcessingSignal.aidl
@@ -0,0 +1,14 @@
+package android.app.ondeviceintelligence;
+
+import android.os.PersistableBundle;
+
+/**
+* Signal to provide to the remote implementation in context of a given request or
+* feature specific event.
+*
+* @hide
+*/
+
+oneway interface IProcessingSignal {
+ void sendSignal(in PersistableBundle actionParams) = 2;
+}
diff --git a/core/java/android/app/ondeviceintelligence/IResponseCallback.aidl b/core/java/android/app/ondeviceintelligence/IResponseCallback.aidl
new file mode 100644
index 0000000..9848e1d
--- /dev/null
+++ b/core/java/android/app/ondeviceintelligence/IResponseCallback.aidl
@@ -0,0 +1,15 @@
+package android.app.ondeviceintelligence;
+
+import android.app.ondeviceintelligence.Content;
+import android.app.ondeviceintelligence.IProcessingSignal;
+import android.os.PersistableBundle;
+
+/**
+ * Interface for a IResponseCallback for receiving response from on-device intelligence service.
+ *
+ * @hide
+ */
+interface IResponseCallback {
+ void onSuccess(in Content result) = 1;
+ void onFailure(int errorCode, in String errorMessage, in PersistableBundle errorParams) = 2;
+}
diff --git a/core/java/android/app/ondeviceintelligence/IStreamingResponseCallback.aidl b/core/java/android/app/ondeviceintelligence/IStreamingResponseCallback.aidl
new file mode 100644
index 0000000..a680574
--- /dev/null
+++ b/core/java/android/app/ondeviceintelligence/IStreamingResponseCallback.aidl
@@ -0,0 +1,18 @@
+package android.app.ondeviceintelligence;
+
+import android.app.ondeviceintelligence.Content;
+import android.app.ondeviceintelligence.IResponseCallback;
+import android.app.ondeviceintelligence.IProcessingSignal;
+import android.os.PersistableBundle;
+
+
+/**
+ * This callback is a streaming variant of {@link IResponseCallback}.
+ *
+ * @hide
+ */
+interface IStreamingResponseCallback {
+ void onNewContent(in Content result) = 1;
+ void onSuccess(in Content result) = 2;
+ void onFailure(int errorCode, in String errorMessage, in PersistableBundle errorParams) = 3;
+}
diff --git a/core/java/android/app/ondeviceintelligence/ITokenCountCallback.aidl b/core/java/android/app/ondeviceintelligence/ITokenCountCallback.aidl
new file mode 100644
index 0000000..b724e03
--- /dev/null
+++ b/core/java/android/app/ondeviceintelligence/ITokenCountCallback.aidl
@@ -0,0 +1,13 @@
+package android.app.ondeviceintelligence;
+
+import android.os.PersistableBundle;
+
+/**
+ * Interface for receiving the token count of a request for a given features.
+ *
+ * @hide
+ */
+interface ITokenCountCallback {
+ void onSuccess(long tokenCount) = 1;
+ void onFailure(int errorCode, in String errorMessage, in PersistableBundle errorParams) = 2;
+}
diff --git a/core/java/android/app/ondeviceintelligence/OWNERS b/core/java/android/app/ondeviceintelligence/OWNERS
index 6932ba2..85e9e65 100644
--- a/core/java/android/app/ondeviceintelligence/OWNERS
+++ b/core/java/android/app/ondeviceintelligence/OWNERS
@@ -4,4 +4,3 @@
shivanker@google.com
hackz@google.com
volnov@google.com
-
diff --git a/core/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java b/core/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java
new file mode 100644
index 0000000..4d8e0d5
--- /dev/null
+++ b/core/java/android/app/ondeviceintelligence/OnDeviceIntelligenceManager.java
@@ -0,0 +1,624 @@
+/*
+ * 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.ondeviceintelligence;
+
+import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE;
+
+import android.Manifest;
+import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.os.Binder;
+import android.os.CancellationSignal;
+import android.os.ICancellationSignal;
+import android.os.OutcomeReceiver;
+import android.os.PersistableBundle;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+
+import androidx.annotation.IntDef;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.function.LongConsumer;
+
+/**
+ * Allows granted apps to manage on-device intelligence service configured on the device. Typical
+ * calling pattern will be to query and setup a required feature before proceeding to request
+ * processing.
+ *
+ * The contracts in this Manager class are designed to be open-ended in general, to allow
+ * interoperability. Therefore, it is recommended that implementations of this system-service
+ * expose this API to the clients via a separate sdk or library which has more defined contract.
+ *
+ * @hide
+ */
+@SystemApi
+@SystemService(Context.ON_DEVICE_INTELLIGENCE_SERVICE)
+@FlaggedApi(FLAG_ENABLE_ON_DEVICE_INTELLIGENCE)
+public class OnDeviceIntelligenceManager {
+ public static final String API_VERSION_BUNDLE_KEY = "ApiVersionBundleKey";
+ private final Context mContext;
+ private final IOnDeviceIntelligenceManager mService;
+
+ /**
+ * @hide
+ */
+ public OnDeviceIntelligenceManager(Context context, IOnDeviceIntelligenceManager service) {
+ mContext = context;
+ mService = service;
+ }
+
+ /**
+ * Asynchronously get the version of the underlying remote implementation.
+ *
+ * @param versionConsumer consumer to populate the version of remote implementation.
+ * @param callbackExecutor executor to run the callback on.
+ */
+ @RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)
+ public void getVersion(
+ @NonNull @CallbackExecutor Executor callbackExecutor,
+ @NonNull LongConsumer versionConsumer) {
+ // TODO explore modifying this method into getServicePackageDetails and return both
+ // version and package name of the remote service implementing this.
+ try {
+ RemoteCallback callback = new RemoteCallback(result -> {
+ if (result == null) {
+ Binder.withCleanCallingIdentity(
+ () -> callbackExecutor.execute(() -> versionConsumer.accept(0)));
+ }
+ long version = result.getLong(API_VERSION_BUNDLE_KEY);
+ Binder.withCleanCallingIdentity(
+ () -> callbackExecutor.execute(() -> versionConsumer.accept(version)));
+ });
+ mService.getVersion(callback);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Asynchronously get feature for a given id.
+ *
+ * @param featureId the identifier pointing to the feature.
+ * @param featureReceiver callback to populate the feature object for given identifier.
+ * @param callbackExecutor executor to run the callback on.
+ */
+ @RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)
+ public void getFeature(
+ int featureId,
+ @NonNull @CallbackExecutor Executor callbackExecutor,
+ @NonNull OutcomeReceiver<Feature, OnDeviceIntelligenceManagerException> featureReceiver) {
+ try {
+ IFeatureCallback callback =
+ new IFeatureCallback.Stub() {
+ @Override
+ public void onSuccess(Feature result) {
+ Binder.withCleanCallingIdentity(() -> callbackExecutor.execute(
+ () -> featureReceiver.onResult(result)));
+ }
+
+ @Override
+ public void onFailure(int errorCode, String errorMessage,
+ PersistableBundle errorParams) {
+ Binder.withCleanCallingIdentity(() -> callbackExecutor.execute(
+ () -> featureReceiver.onError(
+ new OnDeviceIntelligenceManagerException(
+ errorCode, errorMessage, errorParams))));
+ }
+ };
+ mService.getFeature(featureId, callback);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Asynchronously get a list of features that are supported for the caller.
+ *
+ * @param featureListReceiver callback to populate the list of features.
+ * @param callbackExecutor executor to run the callback on.
+ */
+ @RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)
+ public void listFeatures(
+ @NonNull @CallbackExecutor Executor callbackExecutor,
+ @NonNull OutcomeReceiver<List<Feature>, OnDeviceIntelligenceManagerException> featureListReceiver) {
+ try {
+ IListFeaturesCallback callback =
+ new IListFeaturesCallback.Stub() {
+ @Override
+ public void onSuccess(List<Feature> result) {
+ Binder.withCleanCallingIdentity(() -> callbackExecutor.execute(
+ () -> featureListReceiver.onResult(result)));
+ }
+
+ @Override
+ public void onFailure(int errorCode, String errorMessage,
+ PersistableBundle errorParams) {
+ Binder.withCleanCallingIdentity(() -> callbackExecutor.execute(
+ () -> featureListReceiver.onError(
+ new OnDeviceIntelligenceManagerException(
+ errorCode, errorMessage, errorParams))));
+ }
+ };
+ mService.listFeatures(callback);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * This method should be used to fetch details about a feature which need some additional
+ * computation, that can be inefficient to return in all calls to {@link #getFeature}. Callers
+ * and implementation can utilize the {@link Feature#getFeatureParams()} to pass hint on what
+ * details are expected by the caller.
+ *
+ * @param feature the feature to check status for.
+ * @param featureDetailsReceiver callback to populate the feature details to.
+ * @param callbackExecutor executor to run the callback on.
+ */
+ @RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)
+ public void getFeatureDetails(@NonNull Feature feature,
+ @NonNull @CallbackExecutor Executor callbackExecutor,
+ @NonNull OutcomeReceiver<FeatureDetails, OnDeviceIntelligenceManagerException> featureDetailsReceiver) {
+ try {
+ IFeatureDetailsCallback callback = new IFeatureDetailsCallback.Stub() {
+
+ @Override
+ public void onSuccess(FeatureDetails result) {
+ Binder.withCleanCallingIdentity(() -> callbackExecutor.execute(
+ () -> featureDetailsReceiver.onResult(result)));
+ }
+
+ @Override
+ public void onFailure(int errorCode, String errorMessage,
+ PersistableBundle errorParams) {
+ Binder.withCleanCallingIdentity(() -> callbackExecutor.execute(
+ () -> featureDetailsReceiver.onError(
+ new OnDeviceIntelligenceManagerException(errorCode,
+ errorMessage, errorParams))));
+ }
+ };
+ mService.getFeatureDetails(feature, callback);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * This method handles downloading all model and config files required to process requests
+ * sent against a given feature. The caller can listen to updates on the download status via
+ * the callback.
+ *
+ * Note: If a feature was already requested for downloaded previously, the onDownloadFailed
+ * callback would be invoked with {@link DownloadCallback#DOWNLOAD_FAILURE_STATUS_DOWNLOADING}.
+ * In such cases, clients should query the feature status via {@link #getFeatureStatus} to
+ * check
+ * on the feature's download status.
+ *
+ * @param feature feature to request download for.
+ * @param callback callback to populate updates about download status.
+ * @param cancellationSignal signal to invoke cancellation on the operation in the remote
+ * implementation.
+ * @param callbackExecutor executor to run the callback on.
+ */
+ @RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)
+ public void requestFeatureDownload(@NonNull Feature feature,
+ @Nullable CancellationSignal cancellationSignal,
+ @NonNull @CallbackExecutor Executor callbackExecutor,
+ @NonNull DownloadCallback callback) {
+ try {
+ IDownloadCallback downloadCallback = new IDownloadCallback.Stub() {
+
+ @Override
+ public void onDownloadStarted(long bytesToDownload) {
+ Binder.withCleanCallingIdentity(() -> callbackExecutor.execute(
+ () -> callback.onDownloadStarted(bytesToDownload)));
+ }
+
+ @Override
+ public void onDownloadProgress(long bytesDownloaded) {
+ Binder.withCleanCallingIdentity(() -> callbackExecutor.execute(
+ () -> callback.onDownloadProgress(bytesDownloaded)));
+ }
+
+ @Override
+ public void onDownloadFailed(int failureStatus, String errorMessage,
+ PersistableBundle errorParams) {
+ Binder.withCleanCallingIdentity(() -> callbackExecutor.execute(
+ () -> callback.onDownloadFailed(failureStatus, errorMessage,
+ errorParams)));
+ }
+
+ @Override
+ public void onDownloadCompleted(PersistableBundle downloadParams) {
+ Binder.withCleanCallingIdentity(() -> callbackExecutor.execute(
+ () -> onDownloadCompleted(downloadParams)));
+ }
+ };
+
+ ICancellationSignal transport = null;
+ if (cancellationSignal != null) {
+ transport = CancellationSignal.createTransport();
+ cancellationSignal.setRemote(transport);
+ }
+
+ mService.requestFeatureDownload(feature, transport, downloadCallback);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * The methods computes the token-count for a given request payload using the provided Feature
+ * details.
+ *
+ * @param feature feature associated with the request.
+ * @param request request that contains the content data and associated params.
+ * @param outcomeReceiver callback to populate the token count or exception in case of
+ * failure.
+ * @param cancellationSignal signal to invoke cancellation on the operation in the remote
+ * implementation.
+ * @param callbackExecutor executor to run the callback on.
+ */
+ @RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)
+ public void requestTokenCount(@NonNull Feature feature, @NonNull Content request,
+ @Nullable CancellationSignal cancellationSignal,
+ @NonNull @CallbackExecutor Executor callbackExecutor,
+ @NonNull OutcomeReceiver<Long,
+ OnDeviceIntelligenceManagerException> outcomeReceiver) {
+ try {
+ ITokenCountCallback callback = new ITokenCountCallback.Stub() {
+ @Override
+ public void onSuccess(long tokenCount) {
+ Binder.withCleanCallingIdentity(() -> callbackExecutor.execute(
+ () -> outcomeReceiver.onResult(tokenCount)));
+ }
+
+ @Override
+ public void onFailure(int errorCode, String errorMessage,
+ PersistableBundle errorParams) {
+ Binder.withCleanCallingIdentity(() -> callbackExecutor.execute(
+ () -> outcomeReceiver.onError(
+ new OnDeviceIntelligenceManagerProcessingException(
+ errorCode, errorMessage, errorParams))));
+ }
+ };
+
+ ICancellationSignal transport = null;
+ if (cancellationSignal != null) {
+ transport = CancellationSignal.createTransport();
+ cancellationSignal.setRemote(transport);
+ }
+
+ mService.requestTokenCount(feature, request, transport, callback);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+
+ /**
+ * Asynchronously Process a request based on the associated params, to populate a
+ * response in
+ * {@link OutcomeReceiver#onResult} callback or failure callback status code if there
+ * was a
+ * failure.
+ *
+ * @param feature feature associated with the request.
+ * @param request request that contains the Content data and
+ * associated params.
+ * @param requestType type of request being sent for processing the content.
+ * @param responseOutcomeReceiver callback to populate the response content and
+ * associated
+ * params.
+ * @param processingSignal signal to invoke custom actions in the
+ * remote implementation.
+ * @param cancellationSignal signal to invoke cancellation or
+ * @param callbackExecutor executor to run the callback on.
+ */
+ @RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)
+
+ public void processRequest(@NonNull Feature feature, @NonNull Content request,
+ @RequestType int requestType,
+ @Nullable CancellationSignal cancellationSignal,
+ @Nullable ProcessingSignal processingSignal,
+ @NonNull @CallbackExecutor Executor callbackExecutor,
+ @NonNull OutcomeReceiver<Content,
+ OnDeviceIntelligenceManagerProcessingException> responseOutcomeReceiver) {
+ try {
+ IResponseCallback callback = new IResponseCallback.Stub() {
+ @Override
+ public void onSuccess(Content result) {
+ Binder.withCleanCallingIdentity(() -> {
+ callbackExecutor.execute(() -> responseOutcomeReceiver.onResult(result));
+ });
+ }
+
+ @Override
+ public void onFailure(int errorCode, String errorMessage,
+ PersistableBundle errorParams) {
+ Binder.withCleanCallingIdentity(() -> callbackExecutor.execute(
+ () -> responseOutcomeReceiver.onError(
+ new OnDeviceIntelligenceManagerProcessingException(
+ errorCode, errorMessage, errorParams))));
+ }
+ };
+
+ IProcessingSignal transport = null;
+ if (processingSignal != null) {
+ transport = ProcessingSignal.createTransport();
+ processingSignal.setRemote(transport);
+ }
+
+ ICancellationSignal cancellationTransport = null;
+ if (cancellationSignal != null) {
+ cancellationTransport = CancellationSignal.createTransport();
+ cancellationSignal.setRemote(cancellationTransport);
+ }
+
+ mService.processRequest(feature, request, requestType, cancellationTransport, transport,
+ callback);
+
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Variation of {@link #processRequest} that asynchronously processes a request in a streaming
+ * fashion, where new content is pushed to caller in chunks via the
+ * {@link StreamingResponseReceiver#onNewContent}. After the streaming is complete,
+ * the service should call {@link StreamingResponseReceiver#onResult} and can optionally
+ * populate the complete {@link Response}'s Content as part of the callback when the final
+ * {@link Response} contains an enhanced aggregation of the Contents already streamed.
+ *
+ * @param feature feature associated with the request.
+ * @param request request that contains the Content data and associated
+ * params.
+ * @param requestType type of request being sent for processing the content.
+ * @param processingSignal signal to invoke other custom actions in the
+ * remote implementation.
+ * @param cancellationSignal signal to invoke cancellation
+ * @param streamingResponseReceiver streaming callback to populate the response content and
+ * associated params.
+ * @param callbackExecutor executor to run the callback on.
+ */
+ @RequiresPermission(Manifest.permission.USE_ON_DEVICE_INTELLIGENCE)
+ public void processRequestStreaming(@NonNull Feature feature, @NonNull Content request,
+ @RequestType int requestType,
+ @Nullable CancellationSignal cancellationSignal,
+ @Nullable ProcessingSignal processingSignal,
+ @NonNull @CallbackExecutor Executor callbackExecutor,
+ @NonNull StreamingResponseReceiver<Content, Content,
+ OnDeviceIntelligenceManagerProcessingException> streamingResponseReceiver) {
+ try {
+ IStreamingResponseCallback callback = new IStreamingResponseCallback.Stub() {
+ @Override
+ public void onNewContent(Content result) {
+ Binder.withCleanCallingIdentity(() -> {
+ callbackExecutor.execute(
+ () -> streamingResponseReceiver.onNewContent(result));
+ });
+ }
+
+ @Override
+ public void onSuccess(Content result) {
+ Binder.withCleanCallingIdentity(() -> {
+ callbackExecutor.execute(() -> streamingResponseReceiver.onResult(result));
+ });
+ }
+
+ @Override
+ public void onFailure(int errorCode, String errorMessage,
+ PersistableBundle errorParams) {
+ Binder.withCleanCallingIdentity(() -> {
+ callbackExecutor.execute(
+ () -> streamingResponseReceiver.onError(
+ new OnDeviceIntelligenceManagerProcessingException(
+ errorCode, errorMessage, errorParams)));
+ });
+ }
+ };
+
+ IProcessingSignal transport = null;
+ if (processingSignal != null) {
+ transport = ProcessingSignal.createTransport();
+ processingSignal.setRemote(transport);
+ }
+
+ ICancellationSignal cancellationTransport = null;
+ if (cancellationSignal != null) {
+ cancellationTransport = CancellationSignal.createTransport();
+ cancellationSignal.setRemote(cancellationTransport);
+ }
+
+ mService.processRequestStreaming(
+ feature, request, requestType, cancellationTransport, transport, callback);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+
+ /** Request inference with provided Content and Params. */
+ public static final int REQUEST_TYPE_INFERENCE = 0;
+
+ /**
+ * Prepares the remote implementation environment for e.g.loading inference runtime etc.which
+ * are time consuming beforehand to remove overhead and allow quick processing of requests
+ * thereof.
+ */
+ public static final int REQUEST_TYPE_PREPARE = 1;
+
+ /** Request Embeddings of the passed-in Content. */
+ public static final int REQUEST_TYPE_EMBEDDINGS = 2;
+
+ /**
+ * @hide
+ */
+ @IntDef(value = {
+ REQUEST_TYPE_INFERENCE,
+ REQUEST_TYPE_PREPARE,
+ REQUEST_TYPE_EMBEDDINGS
+ }, open = true)
+ @Target({ElementType.TYPE_USE, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface RequestType {
+ }
+
+
+ /**
+ * Exception type to be populated in callbacks to the methods under
+ * {@link OnDeviceIntelligenceManager}.
+ */
+ public static class OnDeviceIntelligenceManagerException extends Exception {
+ /**
+ * Error code returned when the OnDeviceIntelligenceManager service is unavailable.
+ */
+ public static final int ON_DEVICE_INTELLIGENCE_SERVICE_UNAVAILABLE = 1000;
+
+ private final int mErrorCode;
+ private final PersistableBundle errorParams;
+
+ public OnDeviceIntelligenceManagerException(int errorCode, @NonNull String errorMessage,
+ @NonNull PersistableBundle errorParams) {
+ super(errorMessage);
+ this.mErrorCode = errorCode;
+ this.errorParams = errorParams;
+ }
+
+ public OnDeviceIntelligenceManagerException(int errorCode,
+ @NonNull PersistableBundle errorParams) {
+ this.mErrorCode = errorCode;
+ this.errorParams = errorParams;
+ }
+
+ public int getErrorCode() {
+ return mErrorCode;
+ }
+
+ @NonNull
+ public PersistableBundle getErrorParams() {
+ return errorParams;
+ }
+ }
+
+ /**
+ * Exception type to be populated in callbacks to the methods under
+ * {@link OnDeviceIntelligenceManager#processRequest} or
+ * {@link OnDeviceIntelligenceManager#processRequestStreaming} .
+ */
+ public static class OnDeviceIntelligenceManagerProcessingException extends
+ OnDeviceIntelligenceManagerException {
+
+ public static final int PROCESSING_ERROR_UNKNOWN = 1;
+
+ /** Request passed contains bad data for e.g. format. */
+ public static final int PROCESSING_ERROR_BAD_DATA = 2;
+
+ /** Bad request for inputs. */
+ public static final int PROCESSING_ERROR_BAD_REQUEST = 3;
+
+ /** Whole request was classified as not safe, and no response will be generated. */
+ public static final int PROCESSING_ERROR_REQUEST_NOT_SAFE = 4;
+
+ /** Underlying processing encountered an error and failed to compute results. */
+ public static final int PROCESSING_ERROR_COMPUTE_ERROR = 5;
+
+ /** Encountered an error while performing IPC */
+ public static final int PROCESSING_ERROR_IPC_ERROR = 6;
+
+ /** Request was cancelled either by user signal or by the underlying implementation. */
+ public static final int PROCESSING_ERROR_CANCELLED = 7;
+
+ /** Underlying processing in the remote implementation is not available. */
+ public static final int PROCESSING_ERROR_NOT_AVAILABLE = 8;
+
+ /** The service is currently busy. Callers should retry with exponential backoff. */
+ public static final int PROCESSING_ERROR_BUSY = 9;
+
+ /** Something went wrong with safety classification service. */
+ public static final int PROCESSING_ERROR_SAFETY_ERROR = 10;
+
+ /** Response generated was classified unsafe. */
+ public static final int PROCESSING_ERROR_RESPONSE_NOT_SAFE = 11;
+
+ /** Request is too large to be processed. */
+ public static final int PROCESSING_ERROR_REQUEST_TOO_LARGE = 12;
+
+ /** Inference suspended so that higher-priority inference can run. */
+ public static final int PROCESSING_ERROR_SUSPENDED = 13;
+
+ /** Underlying processing encountered an internal error, like a violated precondition. */
+ public static final int PROCESSING_ERROR_INTERNAL = 14;
+
+ /**
+ * The processing was not able to be passed on to the remote implementation, as the service
+ * was unavailable.
+ */
+ public static final int PROCESSING_ERROR_SERVICE_UNAVAILABLE = 15;
+
+ /**
+ * Error code of failed processing request.
+ *
+ * @hide
+ */
+ @IntDef(
+ value = {
+ PROCESSING_ERROR_UNKNOWN,
+ PROCESSING_ERROR_BAD_DATA,
+ PROCESSING_ERROR_BAD_REQUEST,
+ PROCESSING_ERROR_REQUEST_NOT_SAFE,
+ PROCESSING_ERROR_COMPUTE_ERROR,
+ PROCESSING_ERROR_IPC_ERROR,
+ PROCESSING_ERROR_CANCELLED,
+ PROCESSING_ERROR_NOT_AVAILABLE,
+ PROCESSING_ERROR_BUSY,
+ PROCESSING_ERROR_SAFETY_ERROR,
+ PROCESSING_ERROR_RESPONSE_NOT_SAFE,
+ PROCESSING_ERROR_REQUEST_TOO_LARGE,
+ PROCESSING_ERROR_SUSPENDED,
+ PROCESSING_ERROR_INTERNAL,
+ PROCESSING_ERROR_SERVICE_UNAVAILABLE
+ }, open = true)
+ @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
+ @interface ProcessingError {
+ }
+
+ public OnDeviceIntelligenceManagerProcessingException(
+ @ProcessingError int errorCode, @NonNull String errorMessage,
+ @NonNull PersistableBundle errorParams) {
+ super(errorCode, errorMessage, errorParams);
+ }
+
+ public OnDeviceIntelligenceManagerProcessingException(
+ @ProcessingError int errorCode,
+ @NonNull PersistableBundle errorParams) {
+ super(errorCode, errorParams);
+ }
+ }
+}
diff --git a/core/java/android/app/ondeviceintelligence/ProcessingSignal.java b/core/java/android/app/ondeviceintelligence/ProcessingSignal.java
new file mode 100644
index 0000000..3e543d2
--- /dev/null
+++ b/core/java/android/app/ondeviceintelligence/ProcessingSignal.java
@@ -0,0 +1,221 @@
+/*
+ * 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.ondeviceintelligence;
+
+import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.PersistableBundle;
+import android.os.RemoteException;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.ArrayDeque;
+import java.util.Objects;
+import java.util.concurrent.Executor;
+
+/**
+ * A signal to perform orchestration actions on the inference and optionally receive a output about
+ * the result of the signal. This is an extension of {@link android.os.CancellationSignal}.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(FLAG_ENABLE_ON_DEVICE_INTELLIGENCE)
+public final class ProcessingSignal {
+ private final Object mLock = new Object();
+
+ private static final int MAX_QUEUE_SIZE = 10;
+
+ @GuardedBy("mLock")
+ private final ArrayDeque<PersistableBundle> mActionParamsQueue;
+
+ @GuardedBy("mLock")
+ private IProcessingSignal mRemote;
+
+ private OnProcessingSignalCallback mOnProcessingSignalCallback;
+ private Executor mExecutor;
+
+ public ProcessingSignal() {
+ mActionParamsQueue = new ArrayDeque<>(MAX_QUEUE_SIZE);
+ }
+
+ /**
+ * Interface definition for a callback to be invoked when processing signals are received.
+ */
+ public interface OnProcessingSignalCallback {
+ /**
+ * Called when a custom signal was received.
+ * This method allows the receiver to provide logic to be executed based on the signal
+ * received.
+ *
+ * @param actionParams Parameters for the signal in the form of a {@link PersistableBundle}.
+ */
+
+ void onSignalReceived(@NonNull PersistableBundle actionParams);
+ }
+
+
+ /**
+ * Sends a custom signal with the provided parameters. It also signals the remote callback
+ * with the same params if already configured, if not the action is queued to be sent when a
+ * remote is configured. Similarly, on the receiver side, the callback will be invoked if
+ * already set, if not all actions are queued to be sent to callback when it is set.
+ *
+ * @param actionParams Parameters for the signal.
+ */
+ public void sendSignal(@NonNull PersistableBundle actionParams) {
+ final OnProcessingSignalCallback callback;
+ final IProcessingSignal remote;
+ synchronized (mLock) {
+ if (mActionParamsQueue.size() > MAX_QUEUE_SIZE) {
+ throw new RuntimeException(
+ "Maximum actions that can be queued are : " + MAX_QUEUE_SIZE);
+ }
+
+ mActionParamsQueue.add(actionParams);
+ callback = mOnProcessingSignalCallback;
+ remote = mRemote;
+
+ if (callback != null) {
+ while (!mActionParamsQueue.isEmpty()) {
+ PersistableBundle params = mActionParamsQueue.removeFirst();
+ mExecutor.execute(
+ () -> callback.onSignalReceived(params));
+ }
+ }
+ if (remote != null) {
+ while (!mActionParamsQueue.isEmpty()) {
+ try {
+ remote.sendSignal(mActionParamsQueue.removeFirst());
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Sets the processing signal callback to be called when signals are received.
+ *
+ * This method is intended to be used by the recipient of a processing signal
+ * such as the remote implementation for {@link OnDeviceIntelligenceManager} to handle
+ * cancellation requests while performing a long-running operation. This method is not
+ * intended
+ * to be used by applications themselves.
+ *
+ * If {@link ProcessingSignal#sendSignal} has already been called, then the provided callback
+ * is invoked immediately and all previously queued actions are passed to remote signal.
+ *
+ * This method is guaranteed that the callback will not be called after it
+ * has been removed.
+ *
+ * @param callback The processing signal callback, or null to remove the current callback.
+ * @param executor Executor to the run the callback methods on.
+ */
+ public void setOnProcessingSignalCallback(
+ @NonNull @CallbackExecutor Executor executor,
+ @Nullable OnProcessingSignalCallback callback) {
+ Objects.requireNonNull(executor);
+ synchronized (mLock) {
+ if (mOnProcessingSignalCallback == callback) {
+ return;
+ }
+
+ mOnProcessingSignalCallback = callback;
+ mExecutor = executor;
+ if (callback == null || mActionParamsQueue.isEmpty()) {
+ return;
+ }
+
+ while (!mActionParamsQueue.isEmpty()) {
+ PersistableBundle params = mActionParamsQueue.removeFirst();
+ mExecutor.execute(() -> callback.onSignalReceived(params));
+ }
+ }
+ }
+
+ /**
+ * Sets the remote transport.
+ *
+ * If there are actions queued from {@link ProcessingSignal#sendSignal}, they are also
+ * sequentially sent to the remote.
+ *
+ * This method is guaranteed that the remote transport will not be called after it
+ * has been removed.
+ *
+ * @param remote The remote transport, or null to remove.
+ * @hide
+ */
+ void setRemote(IProcessingSignal remote) {
+ synchronized (mLock) {
+ mRemote = remote;
+ if (mActionParamsQueue.isEmpty() || remote == null) {
+ return;
+ }
+
+ while (!mActionParamsQueue.isEmpty()) {
+ try {
+ remote.sendSignal(mActionParamsQueue.removeFirst());
+ } catch (RemoteException e) {
+ throw new RuntimeException("Failed to send action to remote signal", e);
+ }
+ }
+ }
+ }
+
+ /**
+ * Creates a transport that can be returned back to the caller of
+ * a Binder function and subsequently used to dispatch a processing signal.
+ *
+ * @return The new processing signal transport.
+ * @hide
+ */
+ public static IProcessingSignal createTransport() {
+ return new Transport();
+ }
+
+ /**
+ * Given a locally created transport, returns its associated cancellation signal.
+ *
+ * @param transport The locally created transport, or null if none.
+ * @return The associated processing signal, or null if none.
+ * @hide
+ */
+ public static ProcessingSignal fromTransport(IProcessingSignal transport) {
+ if (transport instanceof Transport) {
+ return ((Transport) transport).mProcessingSignal;
+ }
+ return null;
+ }
+
+ private static final class Transport extends IProcessingSignal.Stub {
+ final ProcessingSignal mProcessingSignal = new ProcessingSignal();
+
+ @Override
+ public void sendSignal(PersistableBundle actionParams) {
+ mProcessingSignal.sendSignal(actionParams);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/core/java/android/app/ondeviceintelligence/StreamingResponseReceiver.java b/core/java/android/app/ondeviceintelligence/StreamingResponseReceiver.java
new file mode 100644
index 0000000..ebcf61c
--- /dev/null
+++ b/core/java/android/app/ondeviceintelligence/StreamingResponseReceiver.java
@@ -0,0 +1,43 @@
+/*
+ * 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.ondeviceintelligence;
+
+import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.OutcomeReceiver;
+
+/**
+ * Streaming variant of outcome receiver to populate response while processing a given request,
+ * possibly in
+ * chunks to provide a async processing behaviour to the caller.
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(FLAG_ENABLE_ON_DEVICE_INTELLIGENCE)
+public interface StreamingResponseReceiver<R, T, E extends Throwable> extends
+ OutcomeReceiver<R, E> {
+ /**
+ * Callback to be invoked when a part of the response i.e. some {@link Content} is already
+ * processed and
+ * needs to be passed onto the caller.
+ */
+ void onNewContent(@NonNull T content);
+}
diff --git a/core/java/android/app/ondeviceintelligence/flags/ondevice_intelligence.aconfig b/core/java/android/app/ondeviceintelligence/flags/ondevice_intelligence.aconfig
new file mode 100644
index 0000000..44f3329
--- /dev/null
+++ b/core/java/android/app/ondeviceintelligence/flags/ondevice_intelligence.aconfig
@@ -0,0 +1,8 @@
+package: "android.app.ondeviceintelligence.flags"
+
+flag {
+ name: "enable_on_device_intelligence"
+ namespace: "ondeviceintelligence"
+ description: "Make methods on OnDeviceIntelligenceManager available for local inference."
+ bug: "304755128"
+}
diff --git a/core/java/android/app/servertransaction/ClientTransaction.java b/core/java/android/app/servertransaction/ClientTransaction.java
index 612d433..79696e0 100644
--- a/core/java/android/app/servertransaction/ClientTransaction.java
+++ b/core/java/android/app/servertransaction/ClientTransaction.java
@@ -29,6 +29,7 @@
import android.os.RemoteException;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.window.flags.Flags;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -85,12 +86,15 @@
* @param item A single message that can contain a client activity/window request/callback.
*/
public void addTransactionItem(@NonNull ClientTransactionItem item) {
- if (mTransactionItems == null) {
- mTransactionItems = new ArrayList<>();
+ if (Flags.bundleClientTransactionFlag()) {
+ if (mTransactionItems == null) {
+ mTransactionItems = new ArrayList<>();
+ }
+ mTransactionItems.add(item);
}
- mTransactionItems.add(item);
// TODO(b/324203798): cleanup after remove UnsupportedAppUsage
+ // Populate even if mTransactionItems is set to support the UnsupportedAppUsage.
if (item.isActivityLifecycleItem()) {
setLifecycleStateRequest((ActivityLifecycleItem) item);
} else {
@@ -114,7 +118,7 @@
*/
// TODO(b/324203798): cleanup after remove UnsupportedAppUsage
@Deprecated
- public void addCallback(@NonNull ClientTransactionItem activityCallback) {
+ private void addCallback(@NonNull ClientTransactionItem activityCallback) {
if (mActivityCallbacks == null) {
mActivityCallbacks = new ArrayList<>();
}
@@ -169,7 +173,7 @@
*/
// TODO(b/324203798): cleanup after remove UnsupportedAppUsage
@Deprecated
- public void setLifecycleStateRequest(@NonNull ActivityLifecycleItem stateRequest) {
+ private void setLifecycleStateRequest(@NonNull ActivityLifecycleItem stateRequest) {
if (mLifecycleStateRequest != null) {
return;
}
diff --git a/core/java/android/app/servertransaction/ClientTransactionListenerController.java b/core/java/android/app/servertransaction/ClientTransactionListenerController.java
index 9f97f6f..1a8136e 100644
--- a/core/java/android/app/servertransaction/ClientTransactionListenerController.java
+++ b/core/java/android/app/servertransaction/ClientTransactionListenerController.java
@@ -23,7 +23,6 @@
import android.annotation.NonNull;
import android.app.ActivityThread;
import android.hardware.display.DisplayManagerGlobal;
-import android.os.Process;
import com.android.internal.annotations.VisibleForTesting;
@@ -67,7 +66,7 @@
* window configuration.
*/
public void onDisplayChanged(int displayId) {
- if (!isBundleClientTransactionFlagEnabled()) {
+ if (!bundleClientTransactionFlag()) {
return;
}
if (ActivityThread.isSystem()) {
@@ -76,10 +75,4 @@
}
mDisplayManager.handleDisplayChangeFromWindowManager(displayId);
}
-
- /** Whether {@link #bundleClientTransactionFlag} feature flag is enabled. */
- public boolean isBundleClientTransactionFlagEnabled() {
- // Can't read flag from isolated process.
- return !Process.isIsolated() && bundleClientTransactionFlag();
- }
}
diff --git a/core/java/android/app/servertransaction/TransactionExecutor.java b/core/java/android/app/servertransaction/TransactionExecutor.java
index 406e00a..fa73c99 100644
--- a/core/java/android/app/servertransaction/TransactionExecutor.java
+++ b/core/java/android/app/servertransaction/TransactionExecutor.java
@@ -40,7 +40,6 @@
import android.content.Context;
import android.content.res.Configuration;
import android.os.IBinder;
-import android.os.Process;
import android.os.Trace;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -218,8 +217,6 @@
final boolean shouldTrackConfigUpdatedContext =
// No configuration change for local transaction.
!mTransactionHandler.isExecutingLocalTransaction()
- // Can't read flag from isolated process.
- && !Process.isIsolated()
&& bundleClientTransactionFlag();
final Context configUpdatedContext = shouldTrackConfigUpdatedContext
? item.getContextToUpdate(mTransactionHandler)
diff --git a/core/java/android/app/ui_mode_manager.aconfig b/core/java/android/app/ui_mode_manager.aconfig
index 1ae5264..27a38cc 100644
--- a/core/java/android/app/ui_mode_manager.aconfig
+++ b/core/java/android/app/ui_mode_manager.aconfig
@@ -1,8 +1,11 @@
package: "android.app"
flag {
- namespace: "system_performance"
- name: "enable_night_mode_cache"
+ namespace: "systemui"
+ name: "enable_night_mode_binder_cache"
description: "Enables the use of binder caching for system night mode."
bug: "255999432"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
}
\ No newline at end of file
diff --git a/core/java/android/companion/CompanionDeviceManager.java b/core/java/android/companion/CompanionDeviceManager.java
index d743992..91baa4e 100644
--- a/core/java/android/companion/CompanionDeviceManager.java
+++ b/core/java/android/companion/CompanionDeviceManager.java
@@ -707,7 +707,9 @@
* Only components from the same {@link ComponentName#getPackageName package} as the calling app
* are allowed.
*
- * Your app must have an association with a device before calling this API
+ * Your app must have an association with a device before calling this API.
+ *
+ * Side-loaded apps must allow restricted settings before requesting notification access.
*
* <p>Calling this API requires a uses-feature
* {@link PackageManager#FEATURE_COMPANION_DEVICE_SETUP} declaration in the manifest</p>
@@ -721,6 +723,9 @@
IntentSender intentSender = mService
.requestNotificationAccess(component, mContext.getUserId())
.getIntentSender();
+ if (intentSender == null) {
+ return;
+ }
mContext.startIntentSender(intentSender, null, 0, 0, 0,
ActivityOptions.makeBasic().setPendingIntentBackgroundActivityStartMode(
ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED).toBundle());
diff --git a/core/java/android/companion/virtual/flags/flags.aconfig b/core/java/android/companion/virtual/flags/flags.aconfig
index ab8db6e..24d6a5c 100644
--- a/core/java/android/companion/virtual/flags/flags.aconfig
+++ b/core/java/android/companion/virtual/flags/flags.aconfig
@@ -29,3 +29,18 @@
bug: "291737188"
is_fixed_read_only: true
}
+
+flag {
+ namespace: "virtual_devices"
+ name: "metrics_collection"
+ description: "Enable collection of VDM-related metrics"
+ bug: "324842215"
+ is_fixed_read_only: true
+}
+
+flag {
+ namespace: "virtual_devices"
+ name: "camera_device_awareness"
+ description: "Enable device awareness in camera service"
+ bug: "305170199"
+}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 9e192a0..e6e7fa8 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4815,7 +4815,7 @@
* @see android.net.thread.ThreadNetworkManager
* @hide
*/
- @FlaggedApi("com.android.net.thread.flags.thread_enabled")
+ @FlaggedApi(com.android.net.thread.flags.Flags.FLAG_THREAD_ENABLED_PLATFORM)
@SystemApi
public static final String THREAD_NETWORK_SERVICE = "thread_network";
@@ -5066,6 +5066,7 @@
* {@link android.hardware.fingerprint.FingerprintManager} for handling management
* of fingerprints.
*
+ * @removed See {@link android.hardware.biometrics.BiometricPrompt}
* @see #getSystemService(String)
* @see android.hardware.fingerprint.FingerprintManager
*/
@@ -6450,6 +6451,19 @@
@SystemApi
public static final String WEARABLE_SENSING_SERVICE = "wearable_sensing";
+
+ /**
+ * Use with {@link #getSystemService(String)} to retrieve a
+ * {@link android.app.ondeviceintelligence.OnDeviceIntelligenceManager}.
+ *
+ * @see #getSystemService(String)
+ * @see OnDeviceIntelligenceManager
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE)
+ public static final String ON_DEVICE_INTELLIGENCE_SERVICE = "on_device_intelligence";
+
/**
* Use with {@link #getSystemService(String)} to retrieve a
* {@link android.health.connect.HealthConnectManager}.
diff --git a/core/java/android/content/pm/ArchivedActivityInfo.java b/core/java/android/content/pm/ArchivedActivityInfo.java
index 166d265..9f65f589 100644
--- a/core/java/android/content/pm/ArchivedActivityInfo.java
+++ b/core/java/android/content/pm/ArchivedActivityInfo.java
@@ -24,6 +24,7 @@
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
+import android.util.Slog;
import com.android.internal.util.DataClass;
@@ -39,6 +40,7 @@
@DataClass(genBuilder = false, genConstructor = false, genSetters = true)
@FlaggedApi(Flags.FLAG_ARCHIVING)
public final class ArchivedActivityInfo {
+ private static final String TAG = "ArchivedActivityInfo";
/** The label for the activity. */
private @NonNull CharSequence mLabel;
/** The component name of this activity. */
@@ -138,7 +140,8 @@
bitmap.getByteCount())) {
bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);
return baos.toByteArray();
- } catch (IOException ignored) {
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to compress bitmap", e);
return null;
}
}
@@ -240,10 +243,10 @@
}
@DataClass.Generated(
- time = 1705615445673L,
+ time = 1708042076897L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/content/pm/ArchivedActivityInfo.java",
- inputSignatures = "private @android.annotation.NonNull java.lang.CharSequence mLabel\nprivate @android.annotation.NonNull android.content.ComponentName mComponentName\nprivate @android.annotation.Nullable android.graphics.drawable.Drawable mIcon\nprivate @android.annotation.Nullable android.graphics.drawable.Drawable mMonochromeIcon\n @android.annotation.NonNull android.content.pm.ArchivedActivityParcel getParcel()\npublic static android.graphics.Bitmap drawableToBitmap(android.graphics.drawable.Drawable)\npublic static android.graphics.Bitmap drawableToBitmap(android.graphics.drawable.Drawable,int)\npublic static byte[] bytesFromBitmap(android.graphics.Bitmap)\nprivate static android.graphics.drawable.Drawable drawableFromCompressedBitmap(byte[])\nclass ArchivedActivityInfo extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=false, genConstructor=false, genSetters=true)")
+ inputSignatures = "private static final java.lang.String TAG\nprivate @android.annotation.NonNull java.lang.CharSequence mLabel\nprivate @android.annotation.NonNull android.content.ComponentName mComponentName\nprivate @android.annotation.Nullable android.graphics.drawable.Drawable mIcon\nprivate @android.annotation.Nullable android.graphics.drawable.Drawable mMonochromeIcon\n @android.annotation.NonNull android.content.pm.ArchivedActivityParcel getParcel()\npublic static android.graphics.Bitmap drawableToBitmap(android.graphics.drawable.Drawable)\npublic static android.graphics.Bitmap drawableToBitmap(android.graphics.drawable.Drawable,int)\npublic static byte[] bytesFromBitmap(android.graphics.Bitmap)\nprivate static android.graphics.drawable.Drawable drawableFromCompressedBitmap(byte[])\nclass ArchivedActivityInfo extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=false, genConstructor=false, genSetters=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 2a67353..2a974ed 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3974,7 +3974,7 @@
* The device is capable of communicating with other devices via
* <a href="https://www.threadgroup.org">Thread</a> networking protocol.
*/
- @FlaggedApi("com.android.net.thread.flags.thread_enabled")
+ @FlaggedApi(com.android.net.thread.flags.Flags.FLAG_THREAD_ENABLED_PLATFORM)
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_THREAD_NETWORK = "android.hardware.thread_network";
diff --git a/core/java/android/content/pm/ShortcutServiceInternal.java b/core/java/android/content/pm/ShortcutServiceInternal.java
index 087a795..55d0bc1 100644
--- a/core/java/android/content/pm/ShortcutServiceInternal.java
+++ b/core/java/android/content/pm/ShortcutServiceInternal.java
@@ -115,6 +115,11 @@
public abstract boolean hasShortcutHostPermission(int launcherUserId,
@NonNull String callingPackage, int callingPid, int callingUid);
+ /**
+ * Returns whether or not Shortcuts are supported on Launcher Home Screen.
+ */
+ public abstract boolean areShortcutsSupportedOnHomeScreen(@UserIdInt int userId);
+
public abstract void setShortcutHostPackage(@NonNull String type, @Nullable String packageName,
int userId);
diff --git a/core/java/android/content/pm/multiuser.aconfig b/core/java/android/content/pm/multiuser.aconfig
index 6696ba0..5684929 100644
--- a/core/java/android/content/pm/multiuser.aconfig
+++ b/core/java/android/content/pm/multiuser.aconfig
@@ -156,3 +156,10 @@
description: "Set power mode during a user switch."
bug: "325249845"
}
+
+flag {
+ name: "disable_private_space_items_on_home"
+ namespace: "profile_experiences"
+ description: "Disables adding items belonging to Private Space on Home Screen manually as well as automatically"
+ bug: "287975131"
+}
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
index 23b9d0b..d259e97 100644
--- a/core/java/android/content/res/AssetManager.java
+++ b/core/java/android/content/res/AssetManager.java
@@ -137,6 +137,8 @@
private ArrayList<ApkAssets> mUserApkAssets = new ArrayList<>();
private ArrayList<ResourcesLoader> mLoaders = new ArrayList<>();
+ private boolean mNoInit = false;
+
public Builder addApkAssets(ApkAssets apkAssets) {
mUserApkAssets.add(apkAssets);
return this;
@@ -147,6 +149,11 @@
return this;
}
+ public Builder setNoInit() {
+ mNoInit = true;
+ return this;
+ }
+
public AssetManager build() {
// Retrieving the system ApkAssets forces their creation as well.
final ApkAssets[] systemApkAssets = getSystem().getApkAssets();
@@ -188,7 +195,7 @@
final AssetManager assetManager = new AssetManager(false /*sentinel*/);
assetManager.mApkAssets = apkAssets;
AssetManager.nativeSetApkAssets(assetManager.mObject, apkAssets,
- false /*invalidateCaches*/);
+ false /*invalidateCaches*/, mNoInit /*preset*/);
assetManager.mLoaders = mLoaders.isEmpty() ? null
: mLoaders.toArray(new ResourcesLoader[0]);
@@ -329,7 +336,7 @@
synchronized (this) {
ensureOpenLocked();
mApkAssets = newApkAssets;
- nativeSetApkAssets(mObject, mApkAssets, invalidateCaches);
+ nativeSetApkAssets(mObject, mApkAssets, invalidateCaches, false);
if (invalidateCaches) {
// Invalidate all caches.
invalidateCachesLocked(-1);
@@ -496,7 +503,7 @@
mApkAssets = Arrays.copyOf(mApkAssets, count + 1);
mApkAssets[count] = assets;
- nativeSetApkAssets(mObject, mApkAssets, true);
+ nativeSetApkAssets(mObject, mApkAssets, true, false);
invalidateCachesLocked(-1);
return count + 1;
}
@@ -1503,12 +1510,29 @@
int navigation, int screenWidth, int screenHeight, int smallestScreenWidthDp,
int screenWidthDp, int screenHeightDp, int screenLayout, int uiMode, int colorMode,
int grammaticalGender, int majorVersion) {
+ setConfigurationInternal(mcc, mnc, defaultLocale, locales, orientation,
+ touchscreen, density, keyboard, keyboardHidden, navigation, screenWidth,
+ screenHeight, smallestScreenWidthDp, screenWidthDp, screenHeightDp,
+ screenLayout, uiMode, colorMode, grammaticalGender, majorVersion, false);
+ }
+
+ /**
+ * Change the configuration used when retrieving resources, and potentially force a refresh of
+ * the state. Not for use by applications.
+ * @hide
+ */
+ void setConfigurationInternal(int mcc, int mnc, String defaultLocale, String[] locales,
+ int orientation, int touchscreen, int density, int keyboard, int keyboardHidden,
+ int navigation, int screenWidth, int screenHeight, int smallestScreenWidthDp,
+ int screenWidthDp, int screenHeightDp, int screenLayout, int uiMode, int colorMode,
+ int grammaticalGender, int majorVersion, boolean forceRefresh) {
synchronized (this) {
ensureValidLocked();
nativeSetConfiguration(mObject, mcc, mnc, defaultLocale, locales, orientation,
touchscreen, density, keyboard, keyboardHidden, navigation, screenWidth,
screenHeight, smallestScreenWidthDp, screenWidthDp, screenHeightDp,
- screenLayout, uiMode, colorMode, grammaticalGender, majorVersion);
+ screenLayout, uiMode, colorMode, grammaticalGender, majorVersion,
+ forceRefresh);
}
}
@@ -1593,13 +1617,13 @@
private static native long nativeCreate();
private static native void nativeDestroy(long ptr);
private static native void nativeSetApkAssets(long ptr, @NonNull ApkAssets[] apkAssets,
- boolean invalidateCaches);
+ boolean invalidateCaches, boolean preset);
private static native void nativeSetConfiguration(long ptr, int mcc, int mnc,
@Nullable String defaultLocale, @NonNull String[] locales, int orientation,
int touchscreen, int density, int keyboard, int keyboardHidden, int navigation,
int screenWidth, int screenHeight, int smallestScreenWidthDp, int screenWidthDp,
int screenHeightDp, int screenLayout, int uiMode, int colorMode, int grammaticalGender,
- int majorVersion);
+ int majorVersion, boolean forceRefresh);
private static native @NonNull SparseArray<String> nativeGetAssignedPackageIdentifiers(
long ptr, boolean includeOverlays, boolean includeLoaders);
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index 5e442b8..079c2c1 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -200,7 +200,7 @@
mMetrics.setToDefaults();
mDisplayAdjustments = displayAdjustments;
mConfiguration.setToDefaults();
- updateConfiguration(config, metrics, displayAdjustments.getCompatibilityInfo());
+ updateConfigurationImpl(config, metrics, displayAdjustments.getCompatibilityInfo(), true);
}
public DisplayAdjustments getDisplayAdjustments() {
@@ -402,7 +402,12 @@
}
public void updateConfiguration(Configuration config, DisplayMetrics metrics,
- CompatibilityInfo compat) {
+ CompatibilityInfo compat) {
+ updateConfigurationImpl(config, metrics, compat, false);
+ }
+
+ private void updateConfigurationImpl(Configuration config, DisplayMetrics metrics,
+ CompatibilityInfo compat, boolean forceAssetsRefresh) {
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, "ResourcesImpl#updateConfiguration");
try {
synchronized (mAccessLock) {
@@ -528,7 +533,7 @@
keyboardHidden = mConfiguration.keyboardHidden;
}
- mAssets.setConfiguration(mConfiguration.mcc, mConfiguration.mnc,
+ mAssets.setConfigurationInternal(mConfiguration.mcc, mConfiguration.mnc,
defaultLocale,
selectedLocales,
mConfiguration.orientation,
@@ -539,7 +544,7 @@
mConfiguration.screenWidthDp, mConfiguration.screenHeightDp,
mConfiguration.screenLayout, mConfiguration.uiMode,
mConfiguration.colorMode, mConfiguration.getGrammaticalGender(),
- Build.VERSION.RESOURCES_SDK_INT);
+ Build.VERSION.RESOURCES_SDK_INT, forceAssetsRefresh);
if (DEBUG_CONFIG) {
Slog.i(TAG, "**** Updating config of " + this + ": final config is "
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index d4c58b2..0208fed 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -22,8 +22,9 @@
import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
import static android.hardware.biometrics.BiometricManager.Authenticators;
import static android.hardware.biometrics.Flags.FLAG_ADD_KEY_AGREEMENT_CRYPTO_OBJECT;
-import static android.hardware.biometrics.Flags.FLAG_GET_OP_ID_CRYPTO_OBJECT;
import static android.hardware.biometrics.Flags.FLAG_CUSTOM_BIOMETRIC_PROMPT;
+import static android.hardware.biometrics.Flags.FLAG_GET_OP_ID_CRYPTO_OBJECT;
+import static android.multiuser.Flags.FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE;
import android.annotation.CallbackExecutor;
import android.annotation.DrawableRes;
@@ -501,6 +502,28 @@
}
/**
+ * Remove {@link Builder#setAllowBackgroundAuthentication(boolean)} once
+ * FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE is enabled.
+ *
+ * @param allow If true, allows authentication when the calling package is not in the
+ * foreground. This is set to false by default.
+ * @param useParentProfileForDeviceCredential If true, uses parent profile for device
+ * credential IME request
+ * @return This builder
+ * @hide
+ */
+ @FlaggedApi(FLAG_ENABLE_BIOMETRICS_TO_UNLOCK_PRIVATE_SPACE)
+ @TestApi
+ @NonNull
+ @RequiresPermission(anyOf = {TEST_BIOMETRIC, USE_BIOMETRIC_INTERNAL})
+ public Builder setAllowBackgroundAuthentication(boolean allow,
+ boolean useParentProfileForDeviceCredential) {
+ mPromptInfo.setAllowBackgroundAuthentication(allow);
+ mPromptInfo.setUseParentProfileForDeviceCredential(useParentProfileForDeviceCredential);
+ return this;
+ }
+
+ /**
* If set check the Device Policy Manager for disabled biometrics.
*
* @param checkDevicePolicyManager
diff --git a/core/java/android/hardware/biometrics/PromptInfo.java b/core/java/android/hardware/biometrics/PromptInfo.java
index 2236660..8bb9585 100644
--- a/core/java/android/hardware/biometrics/PromptInfo.java
+++ b/core/java/android/hardware/biometrics/PromptInfo.java
@@ -55,6 +55,7 @@
private boolean mIgnoreEnrollmentState;
private boolean mIsForLegacyFingerprintManager = false;
private boolean mShowEmergencyCallButton = false;
+ private boolean mUseParentProfileForDeviceCredential = false;
public PromptInfo() {
@@ -85,6 +86,7 @@
mIgnoreEnrollmentState = in.readBoolean();
mIsForLegacyFingerprintManager = in.readBoolean();
mShowEmergencyCallButton = in.readBoolean();
+ mUseParentProfileForDeviceCredential = in.readBoolean();
}
public static final Creator<PromptInfo> CREATOR = new Creator<PromptInfo>() {
@@ -129,6 +131,7 @@
dest.writeBoolean(mIgnoreEnrollmentState);
dest.writeBoolean(mIsForLegacyFingerprintManager);
dest.writeBoolean(mShowEmergencyCallButton);
+ dest.writeBoolean(mUseParentProfileForDeviceCredential);
}
// LINT.IfChange
@@ -181,6 +184,13 @@
}
return false;
}
+
+ /**
+ * Returns if parent profile needs to be used for device credential.
+ */
+ public boolean shouldUseParentProfileForDeviceCredential() {
+ return mUseParentProfileForDeviceCredential;
+ }
// LINT.ThenChange(frameworks/base/core/java/android/hardware/biometrics/BiometricPrompt.java)
// Setters
@@ -281,6 +291,11 @@
mShowEmergencyCallButton = showEmergencyCallButton;
}
+ public void setUseParentProfileForDeviceCredential(
+ boolean useParentProfileForDeviceCredential) {
+ mUseParentProfileForDeviceCredential = useParentProfileForDeviceCredential;
+ }
+
// Getters
@DrawableRes
public int getLogoRes() {
diff --git a/core/java/android/hardware/camera2/extension/SessionProcessor.java b/core/java/android/hardware/camera2/extension/SessionProcessor.java
index d8594e5..2e428e5 100644
--- a/core/java/android/hardware/camera2/extension/SessionProcessor.java
+++ b/core/java/android/hardware/camera2/extension/SessionProcessor.java
@@ -18,7 +18,6 @@
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CaptureFailure;
@@ -184,8 +183,8 @@
* capture results. This is the return value of
* either {@link #startRepeating} or {@link
* #startMultiFrameCapture}.
- * @param results The supported capture results. Do note
- * that if results 'android.jpeg.quality' and
+ * @param results Key value map of the supported capture results.
+ * Do note that if results 'android.jpeg.quality' and
* android.jpeg.orientation' are present in the
* process capture input results, then the values
* must also be passed as part of this callback.
@@ -195,7 +194,7 @@
*/
@FlaggedApi(Flags.FLAG_CONCERT_MODE)
void onCaptureCompleted(long shutterTimestamp, int requestId,
- @NonNull CaptureResult results);
+ @NonNull Map<CaptureResult.Key, Object> results);
}
/**
@@ -415,7 +414,7 @@
public int startRepeating(ICaptureCallback callback) throws RemoteException {
return SessionProcessor.this.startRepeating(
new HandlerExecutor(new Handler(Looper.getMainLooper())),
- new CaptureCallbackImpl(callback));
+ new CaptureCallbackImpl(callback, mVendorId));
}
@Override
@@ -428,7 +427,7 @@
throws RemoteException {
return SessionProcessor.this.startMultiFrameCapture(
new HandlerExecutor(new Handler(Looper.getMainLooper())),
- new CaptureCallbackImpl(callback));
+ new CaptureCallbackImpl(callback, mVendorId));
}
@Override
@@ -441,7 +440,7 @@
throws RemoteException {
return SessionProcessor.this.startTrigger(captureRequest,
new HandlerExecutor(new Handler(Looper.getMainLooper())),
- new CaptureCallbackImpl(callback));
+ new CaptureCallbackImpl(callback, mVendorId));
}
@Override
@@ -453,9 +452,11 @@
private static final class CaptureCallbackImpl implements CaptureCallback {
private final ICaptureCallback mCaptureCallback;
+ private long mVendorId = -1;
- CaptureCallbackImpl(@NonNull ICaptureCallback cb) {
+ CaptureCallbackImpl(@NonNull ICaptureCallback cb, long vendorId) {
mCaptureCallback = cb;
+ mVendorId = vendorId;
}
@Override
@@ -505,10 +506,14 @@
@Override
public void onCaptureCompleted(long shutterTimestamp, int requestId,
- @androidx.annotation.NonNull CaptureResult results) {
+ Map<CaptureResult.Key, Object> results) {
+ CameraMetadataNative captureResults = new CameraMetadataNative();
+ captureResults.setVendorId(mVendorId);
+ for (Map.Entry<CaptureResult.Key, Object> entry : results.entrySet()) {
+ captureResults.set(entry.getKey(), entry.getValue());
+ }
try {
- mCaptureCallback.onCaptureCompleted(shutterTimestamp, requestId,
- results.getNativeCopy());
+ mCaptureCallback.onCaptureCompleted(shutterTimestamp, requestId, captureResults);
} catch (RemoteException e) {
Log.e(TAG, "Failed to notify capture complete due to remote exception!");
}
diff --git a/core/java/android/hardware/devicestate/DeviceState.java b/core/java/android/hardware/devicestate/DeviceState.java
index 5a34905..e35e801 100644
--- a/core/java/android/hardware/devicestate/DeviceState.java
+++ b/core/java/android/hardware/devicestate/DeviceState.java
@@ -16,18 +16,25 @@
package android.hardware.devicestate;
-import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE;
-import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE;
+import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE_IDENTIFIER;
+import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE_IDENTIFIER;
+import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
import com.android.internal.util.Preconditions;
+import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.Collections;
import java.util.Objects;
+import java.util.Set;
/**
* A state of the device defined by the {@link DeviceStateProvider} and managed by the
@@ -37,21 +44,29 @@
* state of the system. This is useful for variable-state devices, like foldable or rollable
* devices, that can be configured by users into differing hardware states, which each may have a
* different expected use case.
- * @hide
*
+ * @hide
* @see DeviceStateManager
*/
+@SystemApi
+@FlaggedApi(android.hardware.devicestate.feature.flags.Flags.FLAG_DEVICE_STATE_PROPERTY_API)
public final class DeviceState {
/**
* Flag that indicates override requests should be cancelled when this device state becomes the
* base device state.
+ * @hide
+ * @deprecated use {@link #PROPERTY_POLICY_CANCEL_OVERRIDE_REQUESTS}
*/
+ @Deprecated
public static final int FLAG_CANCEL_OVERRIDE_REQUESTS = 1 << 0;
/**
* Flag that indicates this device state is inaccessible for applications to be placed in. This
- * could be a device-state where the {@link DEFAULT_DISPLAY} is not enabled.
+ * could be a device-state where the {@link Display#DEFAULT_DISPLAY} is not enabled.
+ * @hide
+ * @deprecated use {@link #PROPERTY_APP_INACCESSIBLE}
*/
+ @Deprecated
public static final int FLAG_APP_INACCESSIBLE = 1 << 1;
/**
@@ -60,7 +75,10 @@
* through emulation and have no physical configuration to match.
*
* This flag indicates that the corresponding state can only be entered through emulation.
+ * @hide
+ * @deprecated use {@link #PROPERTY_EMULATED_ONLY}
*/
+ @Deprecated
public static final int FLAG_EMULATED_ONLY = 1 << 2;
/**
@@ -68,19 +86,28 @@
* requesting app is no longer on top. The app is considered not on top when (1) the top
* activity in the system is from a different app, (2) the device is in sleep mode, or
* (3) the keyguard shows up.
+ * @hide
+ * @deprecated use {@link #PROPERTY_POLICY_CANCEL_WHEN_REQUESTER_NOT_ON_TOP}
*/
+ @Deprecated
public static final int FLAG_CANCEL_WHEN_REQUESTER_NOT_ON_TOP = 1 << 3;
/**
* This flag indicates that the corresponding state should be disabled when the device is
* overheating and reaching the critical status.
+ * @hide
+ * @deprecated use {@link #PROPERTY_POLICY_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL}
*/
+ @Deprecated
public static final int FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL = 1 << 4;
/**
* This flag indicates that the corresponding state should be disabled when power save mode
* is enabled.
+ * @hide
+ * @deprecated use {@link #PROPERTY_POLICY_UNSUPPORTED_WHEN_POWER_SAVE_MODE}
*/
+ @Deprecated
public static final int FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE = 1 << 5;
/** @hide */
@@ -92,11 +119,157 @@
FLAG_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL,
FLAG_UNSUPPORTED_WHEN_POWER_SAVE_MODE
})
+ @Deprecated
@Retention(RetentionPolicy.SOURCE)
public @interface DeviceStateFlags {}
+ /**
+ * Property that indicates that a fold-in style foldable device is currently in a fully closed
+ * configuration.
+ */
+ public static final int PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED = 1;
+
+ /**
+ * Property that indicates that a fold-in style foldable device is currently in a half-opened
+ * configuration. This signifies that the device's hinge is positioned somewhere around 90
+ * degrees. Checking for display configuration properties as well can provide information
+ * on which display is currently active.
+ */
+ public static final int PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN = 2;
+
+ /**
+ * Property that indicates that a fold-in style foldable device is currently in a fully open
+ * configuration.
+ */
+ public static final int PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN = 3;
+
+ /**
+ * Property that indicates override requests should be cancelled when the device is physically
+ * put into this state.
+ * @hide
+ */
+ public static final int PROPERTY_POLICY_CANCEL_OVERRIDE_REQUESTS = 4;
+
+ /**
+ * This property indicates that the corresponding state should be automatically canceled when
+ * the requesting app is no longer on top. The app is considered not on top when (1) the top
+ * activity in the system is from a different app, (2) the device is in sleep mode, or
+ * (3) the keyguard shows up.
+ * @hide
+ */
+ public static final int PROPERTY_POLICY_CANCEL_WHEN_REQUESTER_NOT_ON_TOP = 5;
+
+ /**
+ * This property indicates that the corresponding state should be disabled when the device is
+ * overheating and reaching the critical status.
+ * @hide
+ */
+ public static final int PROPERTY_POLICY_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL = 6;
+
+ /**
+ * This property indicates that the corresponding state should be disabled when power save mode
+ * is enabled.
+ * @hide
+ */
+ public static final int PROPERTY_POLICY_UNSUPPORTED_WHEN_POWER_SAVE_MODE = 7;
+
+ /**
+ * This property denotes that this state is available for applications to request and the system
+ * server should deny any request that comes from a process that does not hold the
+ * CONTROL_DEVICE_STATE permission if it is requesting a state that does not have this property
+ * on it.
+ * @hide
+ */
+ @TestApi
+ public static final int PROPERTY_POLICY_AVAILABLE_FOR_APP_REQUEST = 8;
+
+ /**
+ * Property that indicates this device state is inaccessible for applications to be made
+ * visible to the user. This could be a device-state where the {@link Display#DEFAULT_DISPLAY}
+ * is not enabled.
+ * @hide
+ */
+ public static final int PROPERTY_APP_INACCESSIBLE = 9;
+
+ /**
+ * This property indidcates that this state can only be entered through emulation and has no
+ * physical configuration to match.
+ */
+ public static final int PROPERTY_EMULATED_ONLY = 10;
+
+ /**
+ * Property that indicates that the outer display area of a foldable device is currently the
+ * primary display area.
+ *
+ * Note: This does not necessarily mean that the outer display area is the
+ * {@link Display#DEFAULT_DISPLAY}.
+ */
+ public static final int PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY = 11;
+
+ /**
+ * Property that indicates that the inner display area of a foldable device is currently the
+ * primary display area.
+ *
+ * Note: This does not necessarily mean that the inner display area is the
+ * {@link Display#DEFAULT_DISPLAY}.
+ */
+ public static final int PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY = 12;
+
+ /**
+ * Property that indicates that this device state will attempt to trigger the device to go to
+ * sleep.
+ */
+ public static final int PROPERTY_POWER_CONFIGURATION_TRIGGER_SLEEP = 13;
+
+ /**
+ * Property that indicates that this device state will attempt to trigger the device to wake up.
+ */
+ public static final int PROPERTY_POWER_CONFIGURATION_TRIGGER_WAKE = 14;
+
+ /**
+ * Property that indicates that an external display has been connected to the device. Specifics
+ * around display mode or properties around the display should be gathered through
+ * {@link android.hardware.display.DisplayManager}
+ */
+ public static final int PROPERTY_EXTENDED_DEVICE_STATE_EXTERNAL_DISPLAY = 15;
+ /**
+ * Property that indicates that this state corresponds to the device state for rear display
+ * mode. This means that the active display is facing the same direction as the rear camera.
+ */
+ public static final int PROPERTY_FEATURE_REAR_DISPLAY = 16;
+
+ /**
+ * Property that indicates that this state corresponds to the device state where both displays
+ * on a foldable are active, with the internal display being the default display.
+ */
+ public static final int PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT = 17;
+
+ /** @hide */
+ @IntDef(prefix = {"PROPERTY_"}, flag = true, value = {
+ PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_CLOSED,
+ PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_HALF_OPEN,
+ PROPERTY_FOLDABLE_HARDWARE_CONFIGURATION_FOLD_IN_OPEN,
+ PROPERTY_POLICY_CANCEL_OVERRIDE_REQUESTS,
+ PROPERTY_POLICY_CANCEL_WHEN_REQUESTER_NOT_ON_TOP,
+ PROPERTY_POLICY_UNSUPPORTED_WHEN_THERMAL_STATUS_CRITICAL,
+ PROPERTY_POLICY_UNSUPPORTED_WHEN_POWER_SAVE_MODE,
+ PROPERTY_POLICY_AVAILABLE_FOR_APP_REQUEST,
+ PROPERTY_APP_INACCESSIBLE,
+ PROPERTY_EMULATED_ONLY,
+ PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY,
+ PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY,
+ PROPERTY_POWER_CONFIGURATION_TRIGGER_SLEEP,
+ PROPERTY_POWER_CONFIGURATION_TRIGGER_WAKE,
+ PROPERTY_EXTENDED_DEVICE_STATE_EXTERNAL_DISPLAY,
+ PROPERTY_FEATURE_REAR_DISPLAY,
+ PROPERTY_FEATURE_DUAL_DISPLAY_INTERNAL_DEFAULT
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
+ public @interface DeviceStateProperties {}
+
/** Unique identifier for the device state. */
- @IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE)
+ @IntRange(from = MINIMUM_DEVICE_STATE_IDENTIFIER, to = MAXIMUM_DEVICE_STATE_IDENTIFIER)
private final int mIdentifier;
/** String description of the device state. */
@@ -106,20 +279,49 @@
@DeviceStateFlags
private final int mFlags;
+ private final Set<@DeviceStateProperties Integer> mProperties;
+
+ /**
+ * @deprecated Deprecated in favor of {@link #DeviceState(int, String, Set)}
+ * @hide
+ */
+ // TODO(b/325124054): Make non-default and remove deprecated callback methods.
+ @TestApi
+ @Deprecated
public DeviceState(
- @IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE) int identifier,
+ @IntRange(from = MINIMUM_DEVICE_STATE_IDENTIFIER, to =
+ MAXIMUM_DEVICE_STATE_IDENTIFIER) int identifier,
@NonNull String name,
@DeviceStateFlags int flags) {
- Preconditions.checkArgumentInRange(identifier, MINIMUM_DEVICE_STATE, MAXIMUM_DEVICE_STATE,
+ Preconditions.checkArgumentInRange(identifier, MINIMUM_DEVICE_STATE_IDENTIFIER,
+ MAXIMUM_DEVICE_STATE_IDENTIFIER,
"identifier");
mIdentifier = identifier;
mName = name;
mFlags = flags;
+ mProperties = Collections.emptySet();
+ }
+
+ /** @hide */
+ @TestApi
+ public DeviceState(
+ @IntRange(from = MINIMUM_DEVICE_STATE_IDENTIFIER, to =
+ MAXIMUM_DEVICE_STATE_IDENTIFIER) int identifier,
+ @NonNull String name,
+ @NonNull Set<@DeviceStateProperties Integer> properties) {
+ Preconditions.checkArgumentInRange(identifier, MINIMUM_DEVICE_STATE_IDENTIFIER,
+ MAXIMUM_DEVICE_STATE_IDENTIFIER,
+ "identifier");
+
+ mIdentifier = identifier;
+ mName = name;
+ mProperties = Set.copyOf(properties);
+ mFlags = 0;
}
/** Returns the unique identifier for the device state. */
- @IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE)
+ @IntRange(from = MINIMUM_DEVICE_STATE_IDENTIFIER)
public int getIdentifier() {
return mIdentifier;
}
@@ -130,6 +332,12 @@
return mName;
}
+ /**
+ * @hide
+ * @deprecated in favor of {@link #hasProperty(int)} method
+ */
+ // TODO(b/325124054): Make non-default and remove deprecated callback methods.
+ @Deprecated
@DeviceStateFlags
public int getFlags() {
return mFlags;
@@ -138,9 +346,9 @@
@Override
public String toString() {
return "DeviceState{" + "identifier=" + mIdentifier + ", name='" + mName + '\''
- + ", app_accessible=" + !hasFlag(FLAG_APP_INACCESSIBLE)
+ + ", app_accessible=" + !hasProperty(PROPERTY_APP_INACCESSIBLE)
+ ", cancel_when_requester_not_on_top="
- + hasFlag(FLAG_CANCEL_WHEN_REQUESTER_NOT_ON_TOP)
+ + hasProperty(PROPERTY_POLICY_CANCEL_WHEN_REQUESTER_NOT_ON_TOP)
+ "}";
}
@@ -151,17 +359,40 @@
DeviceState that = (DeviceState) o;
return mIdentifier == that.mIdentifier
&& Objects.equals(mName, that.mName)
- && mFlags == that.mFlags;
+ && Objects.equals(mProperties, that.mProperties);
}
@Override
public int hashCode() {
- return Objects.hash(mIdentifier, mName, mFlags);
+ return Objects.hash(mIdentifier, mName, mProperties);
}
/** Checks if a specific flag is set
+ * @hide
+ * @deprecated in favor of {@link #hasProperty(int)}
*/
+ // TODO(b/325124054): Make non-default and remove deprecated callback methods.
+ @Deprecated
public boolean hasFlag(int flagToCheckFor) {
return (mFlags & flagToCheckFor) == flagToCheckFor;
}
+
+ /**
+ * Checks if a specific property is set on this state
+ */
+ public boolean hasProperty(@DeviceStateProperties int propertyToCheckFor) {
+ return mProperties.contains(propertyToCheckFor);
+ }
+
+ /**
+ * Checks if a list of properties are all set on this state
+ */
+ public boolean hasProperties(@NonNull @DeviceStateProperties int... properties) {
+ for (int i = 0; i < properties.length; i++) {
+ if (mProperties.contains(properties[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
}
diff --git a/core/java/android/hardware/devicestate/DeviceStateManager.java b/core/java/android/hardware/devicestate/DeviceStateManager.java
index 6a667fe..8b4d43e 100644
--- a/core/java/android/hardware/devicestate/DeviceStateManager.java
+++ b/core/java/android/hardware/devicestate/DeviceStateManager.java
@@ -18,15 +18,19 @@
import android.Manifest;
import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.content.Context;
import com.android.internal.util.ArrayUtils;
+import java.util.List;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -36,7 +40,8 @@
*
* @hide
*/
-@TestApi
+@SystemApi
+@FlaggedApi(android.hardware.devicestate.feature.flags.Flags.FLAG_DEVICE_STATE_PROPERTY_API)
@SystemService(Context.DEVICE_STATE_SERVICE)
public final class DeviceStateManager {
/**
@@ -46,11 +51,19 @@
*/
public static final int INVALID_DEVICE_STATE = -1;
- /** The minimum allowed device state identifier. */
- public static final int MINIMUM_DEVICE_STATE = 0;
+ /**
+ * The minimum allowed device state identifier.
+ * @hide
+ */
+ @TestApi
+ public static final int MINIMUM_DEVICE_STATE_IDENTIFIER = 0;
- /** The maximum allowed device state identifier. */
- public static final int MAXIMUM_DEVICE_STATE = 255;
+ /**
+ * The maximum allowed device state identifier.
+ * @hide
+ */
+ @TestApi
+ public static final int MAXIMUM_DEVICE_STATE_IDENTIFIER = 10000;
/**
* Intent needed to launch the rear display overlay activity from SysUI
@@ -83,13 +96,27 @@
/**
* Returns the list of device states that are supported and can be requested with
* {@link #requestState(DeviceStateRequest, Executor, DeviceStateRequest.Callback)}.
+ * @deprecated use {@link #getSupportedDeviceStates()}
+ * @hide
*/
+ // TODO(b/325124054): Make non-default and remove deprecated callback methods.
+ @TestApi
+ @Deprecated
@NonNull
public int[] getSupportedStates() {
return mGlobal.getSupportedStates();
}
/**
+ * Returns the list of device states that are supported and can be requested with
+ * {@link #requestState(DeviceStateRequest, Executor, DeviceStateRequest.Callback)}.
+ */
+ @NonNull
+ public List<DeviceState> getSupportedDeviceStates() {
+ return mGlobal.getSupportedDeviceStates();
+ }
+
+ /**
* Submits a {@link DeviceStateRequest request} to modify the device state.
* <p>
* By default, the request is kept active until one of the following occurs:
@@ -107,7 +134,10 @@
* the {@link android.Manifest.permission#CONTROL_DEVICE_STATE} permission is held.
*
* @see DeviceStateRequest
+ * @hide
*/
+ @SuppressLint("RequiresPermission") // Lint doesn't handle conditional permission checks today
+ @TestApi
@RequiresPermission(value = android.Manifest.permission.CONTROL_DEVICE_STATE,
conditional = true)
public void requestState(@NonNull DeviceStateRequest request,
@@ -124,7 +154,10 @@
*
* @throws SecurityException if the caller is neither the current top-focused activity nor if
* the {@link android.Manifest.permission#CONTROL_DEVICE_STATE} permission is held.
+ * @hide
*/
+ @SuppressLint("RequiresPermission") // Lint doesn't handle conditional permission checks today
+ @TestApi
@RequiresPermission(value = android.Manifest.permission.CONTROL_DEVICE_STATE,
conditional = true)
public void cancelStateRequest() {
@@ -151,11 +184,11 @@
* emulated override requests take priority.
*
* @throws IllegalArgumentException if the requested state is unsupported.
- * @throws SecurityException if the caller does not hold the
- * {@link android.Manifest.permission#CONTROL_DEVICE_STATE} permission.
*
* @see DeviceStateRequest
+ * @hide
*/
+ @TestApi
@RequiresPermission(android.Manifest.permission.CONTROL_DEVICE_STATE)
public void requestBaseStateOverride(@NonNull DeviceStateRequest request,
@Nullable @CallbackExecutor Executor executor,
@@ -169,9 +202,9 @@
* <p>
* This method is noop if there is no base state request currently active.
*
- * @throws SecurityException if the caller does not hold the
- * {@link android.Manifest.permission#CONTROL_DEVICE_STATE} permission.
+ * @hide
*/
+ @TestApi
@RequiresPermission(Manifest.permission.CONTROL_DEVICE_STATE)
public void cancelBaseStateOverride() {
mGlobal.cancelBaseStateOverride();
@@ -209,10 +242,31 @@
* @param supportedStates the new supported states.
*
* @see DeviceStateManager#getSupportedStates()
+ * @deprecated use {@link #onSupportedStatesChanged(List)}
+ * @hide
*/
+ // TODO(b/325124054): Make non-default and remove deprecated callback methods.
+ @TestApi
+ @Deprecated
default void onSupportedStatesChanged(@NonNull int[] supportedStates) {}
/**
+ * Called in response to a change in the states supported by the device.
+ * <p>
+ * Guaranteed to be called once on registration of the callback with the initial value and
+ * then on every subsequent change in the supported states.
+ *
+ * The supported device states may change due to certain states becoming unavailable
+ * due to device configuration or device conditions such as if the device is too hot or
+ * external monitors have been connected.
+ *
+ * @param supportedStates the new supported states.
+ *
+ * @see DeviceStateManager#getSupportedDeviceStates()
+ */
+ default void onSupportedStatesChanged(@NonNull List<DeviceState> supportedStates) {}
+
+ /**
* Called in response to a change in the base device state.
* <p>
* The base state is the state of the device without considering any requests made through
@@ -224,7 +278,13 @@
* then on every subsequent change in the non-override state.
*
* @param state the new base device state.
+ * @deprecated use {@link #onDeviceStateChanged(DeviceState)} and query for physical
+ * properties that are relevant to your needs.
+ * @hide
*/
+ // TODO(b/325124054): Make non-default and remove deprecated callback methods.
+ @TestApi
+ @Deprecated
default void onBaseStateChanged(int state) {}
/**
@@ -234,8 +294,24 @@
* then on every subsequent change in device state.
*
* @param state the new device state.
+ * @deprecated use {@link #onDeviceStateChanged(DeviceState)}
+ * @hide
*/
+ // TODO(b/325124054): Make non-default and remove deprecated callback methods.
+ @TestApi
+ @Deprecated
void onStateChanged(int state);
+
+ /**
+ * Called in response to device state changes.
+ * <p>
+ * Guaranteed to be called once on registration of the callback with the initial value and
+ * then on every subsequent change in device state.
+ *
+ * @param state the new device state.
+ */
+ // TODO(b/325124054): Make non-default and remove deprecated callback methods.
+ default void onDeviceStateChanged(@NonNull DeviceState state) {}
}
/**
@@ -247,6 +323,7 @@
public static class FoldStateListener implements DeviceStateCallback {
private final int[] mFoldedDeviceStates;
private final Consumer<Boolean> mDelegate;
+ private final android.hardware.devicestate.feature.flags.FeatureFlags mFeatureFlags;
@Nullable
private Boolean lastResult;
@@ -259,11 +336,23 @@
mFoldedDeviceStates = context.getResources().getIntArray(
com.android.internal.R.array.config_foldedDeviceStates);
mDelegate = listener;
+ mFeatureFlags = new android.hardware.devicestate.feature.flags.FeatureFlagsImpl();
}
@Override
- public final void onStateChanged(int state) {
- final boolean folded = ArrayUtils.contains(mFoldedDeviceStates, state);
+ public final void onStateChanged(int state) {}
+
+ @Override
+ public final void onDeviceStateChanged(@NonNull DeviceState deviceState) {
+ final boolean folded;
+ if (mFeatureFlags.deviceStatePropertyApi()) {
+ // TODO(b/325124054): Update when system server refactor is completed
+ folded = deviceState.hasProperty(
+ DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY)
+ || ArrayUtils.contains(mFoldedDeviceStates, deviceState.getIdentifier());
+ } else {
+ folded = ArrayUtils.contains(mFoldedDeviceStates, deviceState.getIdentifier());
+ }
if (lastResult == null || !lastResult.equals(folded)) {
lastResult = folded;
diff --git a/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java b/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
index c379188..6db5aee 100644
--- a/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
+++ b/core/java/android/hardware/devicestate/DeviceStateManagerGlobal.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.app.ActivityThread;
import android.content.Context;
import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback;
import android.os.Binder;
@@ -32,9 +33,13 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;
+import com.android.internal.util.ArrayUtils;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
import java.util.concurrent.Executor;
/**
@@ -50,12 +55,18 @@
private static final String TAG = "DeviceStateManagerGlobal";
private static final boolean DEBUG = Build.IS_DEBUGGABLE;
+ // TODO(b/325124054): Remove when system server refactor is completed
+ private static int[] sFoldedDeviceStates = new int[0];
+
/**
* Returns an instance of {@link DeviceStateManagerGlobal}. May return {@code null} if a
* connection with the device state service couldn't be established.
*/
@Nullable
public static DeviceStateManagerGlobal getInstance() {
+ // TODO(b/325124054): Remove when system server refactor is completed
+ instantiateFoldedStateArray();
+
synchronized (DeviceStateManagerGlobal.class) {
if (sInstance == null) {
IBinder b = ServiceManager.getService(Context.DEVICE_STATE_SERVICE);
@@ -68,6 +79,16 @@
}
}
+ // TODO(b/325124054): Remove when system server refactor is completed
+ // TODO(b/325330654): Investigate if we need a Context passed in to DSMGlobal
+ private static void instantiateFoldedStateArray() {
+ Context context = ActivityThread.currentApplication();
+ if (context != null) {
+ sFoldedDeviceStates = context.getResources().getIntArray(
+ com.android.internal.R.array.config_foldedDeviceStates);
+ }
+ }
+
private final Object mLock = new Object();
@NonNull
private final IDeviceStateManager mDeviceStateManager;
@@ -116,6 +137,32 @@
}
/**
+ * Returns the {@link List} of supported device states.
+ *
+ * @see DeviceStateManager#getSupportedDeviceStates()
+ */
+ public List<DeviceState> getSupportedDeviceStates() {
+ synchronized (mLock) {
+ final DeviceStateInfo currentInfo;
+ if (mLastReceivedInfo != null) {
+ // If we have mLastReceivedInfo a callback is registered for this instance and it
+ // is receiving the most recent info from the server. Use that info here.
+ currentInfo = mLastReceivedInfo;
+ } else {
+ // If mLastReceivedInfo is null there is no registered callback so we manually
+ // fetch the current info.
+ try {
+ currentInfo = mDeviceStateManager.getDeviceStateInfo();
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ return createDeviceStateList(currentInfo.supportedStates);
+ }
+ }
+
+ /**
* Submits a {@link DeviceStateRequest request} to modify the device state.
*
* @see DeviceStateManager#requestState(DeviceStateRequest, Executor,
@@ -241,8 +288,10 @@
final int[] supportedStates = Arrays.copyOf(mLastReceivedInfo.supportedStates,
mLastReceivedInfo.supportedStates.length);
wrapper.notifySupportedStatesChanged(supportedStates);
+ wrapper.notifySupportedDeviceStatesChanged(createDeviceStateList(supportedStates));
wrapper.notifyBaseStateChanged(mLastReceivedInfo.baseState);
wrapper.notifyStateChanged(mLastReceivedInfo.currentState);
+ wrapper.notifyDeviceStateChanged(createDeviceState(mLastReceivedInfo.currentState));
}
}
}
@@ -327,6 +376,8 @@
final int[] supportedStates = Arrays.copyOf(info.supportedStates,
info.supportedStates.length);
callbacks.get(i).notifySupportedStatesChanged(supportedStates);
+ callbacks.get(i).notifySupportedDeviceStatesChanged(
+ createDeviceStateList(supportedStates));
}
}
if ((diff & DeviceStateInfo.CHANGED_BASE_STATE) > 0) {
@@ -337,6 +388,7 @@
if ((diff & DeviceStateInfo.CHANGED_CURRENT_STATE) > 0) {
for (int i = 0; i < callbacks.size(); i++) {
callbacks.get(i).notifyStateChanged(info.currentState);
+ callbacks.get(i).notifyDeviceStateChanged(createDeviceState(info.currentState));
}
}
}
@@ -369,6 +421,36 @@
}
}
+ /**
+ * Creates a {@link DeviceState} object from a device state identifier, with the
+ * {@link DeviceState} property that corresponds to what display is primary.
+ *
+ */
+ // TODO(b/325124054): Remove when system server refactor is completed
+ @NonNull
+ private DeviceState createDeviceState(int stateIdentifier) {
+ final Set<@DeviceState.DeviceStateProperties Integer> properties = new HashSet<>();
+ if (ArrayUtils.contains(sFoldedDeviceStates, stateIdentifier)) {
+ properties.add(DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_OUTER_PRIMARY);
+ } else {
+ properties.add(DeviceState.PROPERTY_FOLDABLE_DISPLAY_CONFIGURATION_INNER_PRIMARY);
+ }
+ return new DeviceState(stateIdentifier, "" /* name */, properties);
+ }
+
+ /**
+ * Creates a list of {@link DeviceState} objects from an array of state identifiers.
+ */
+ // TODO(b/325124054): Remove when system server refactor is completed
+ @NonNull
+ private List<DeviceState> createDeviceStateList(int[] supportedStates) {
+ List<DeviceState> deviceStateList = new ArrayList<>();
+ for (int i = 0; i < supportedStates.length; i++) {
+ deviceStateList.add(createDeviceState(supportedStates[i]));
+ }
+ return deviceStateList;
+ }
+
private final class DeviceStateManagerCallback extends IDeviceStateManagerCallback.Stub {
@Override
public void onDeviceStateInfoChanged(DeviceStateInfo info) {
@@ -403,6 +485,11 @@
mDeviceStateCallback.onSupportedStatesChanged(newSupportedStates));
}
+ void notifySupportedDeviceStatesChanged(List<DeviceState> newSupportedDeviceStates) {
+ mExecutor.execute(() ->
+ mDeviceStateCallback.onSupportedStatesChanged(newSupportedDeviceStates));
+ }
+
void notifyBaseStateChanged(int newBaseState) {
execute("notifyBaseStateChanged",
() -> mDeviceStateCallback.onBaseStateChanged(newBaseState));
@@ -413,6 +500,11 @@
() -> mDeviceStateCallback.onStateChanged(newDeviceState));
}
+ void notifyDeviceStateChanged(DeviceState newDeviceState) {
+ execute("notifyDeviceStateChanged",
+ () -> mDeviceStateCallback.onDeviceStateChanged(newDeviceState));
+ }
+
private void execute(String traceName, Runnable r) {
mExecutor.execute(() -> {
if (DEBUG) {
diff --git a/core/java/android/hardware/devicestate/feature/flags.aconfig b/core/java/android/hardware/devicestate/feature/flags.aconfig
new file mode 100644
index 0000000..73a9e34
--- /dev/null
+++ b/core/java/android/hardware/devicestate/feature/flags.aconfig
@@ -0,0 +1,9 @@
+package: "android.hardware.devicestate.feature.flags"
+
+flag {
+ name: "device_state_property_api"
+ namespace: "windowing_sdk"
+ description: "Updated DeviceState hasProperty API"
+ bug: "293636629"
+ is_fixed_read_only: true
+}
\ No newline at end of file
diff --git a/core/java/android/hardware/display/BrightnessInfo.java b/core/java/android/hardware/display/BrightnessInfo.java
index 53a9a75..c091062 100644
--- a/core/java/android/hardware/display/BrightnessInfo.java
+++ b/core/java/android/hardware/display/BrightnessInfo.java
@@ -80,6 +80,11 @@
*/
public static final int BRIGHTNESS_MAX_REASON_POWER_IC = 2;
+ /**
+ * Maximum brightness is restricted due to the Wear bedtime mode.
+ */
+ public static final int BRIGHTNESS_MAX_REASON_WEAR_BEDTIME_MODE = 3;
+
/** Brightness */
public final float brightness;
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 8f0e0c9..eb26a76 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -28,6 +28,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
@@ -367,6 +368,8 @@
* @see #createVirtualDisplay
* @hide
*/
+ @SuppressLint("UnflaggedApi") // @TestApi without associated feature.
+ @TestApi
public static final int VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH = 1 << 6;
/**
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index fe7de83..5a38a34 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -83,7 +83,8 @@
/**
* A class that coordinates access to the fingerprint hardware.
- * @deprecated See {@link BiometricPrompt} which shows a system-provided dialog upon starting
+ *
+ * @removed See {@link BiometricPrompt} which shows a system-provided dialog upon starting
* authentication. In a world where devices may have different types of biometric authentication,
* it's much more realistic to have a system-provided authentication dialog since the method may
* vary by vendor/device.
@@ -94,7 +95,6 @@
@RequiresFeature(PackageManager.FEATURE_FINGERPRINT)
public class FingerprintManager implements BiometricAuthenticator, BiometricFingerprintConstants {
private static final String TAG = "FingerprintManager";
- private static final boolean DEBUG = true;
private static final int MSG_ENROLL_RESULT = 100;
private static final int MSG_ACQUIRED = 101;
private static final int MSG_AUTHENTICATION_SUCCEEDED = 102;
@@ -196,6 +196,7 @@
/**
* Retrieves a test session for FingerprintManager.
+ *
* @hide
*/
@TestApi
@@ -254,9 +255,10 @@
}
/**
- * A wrapper class for the crypto objects supported by FingerprintManager. Currently the
+ * A wrapper class for the crypto objects supported by FingerprintManager. Currently, the
* framework supports {@link Signature}, {@link Cipher} and {@link Mac} objects.
- * @deprecated See {@link android.hardware.biometrics.BiometricPrompt.CryptoObject}
+ *
+ * @removed See {@link android.hardware.biometrics.BiometricPrompt.CryptoObject}
*/
@Deprecated
public static final class CryptoObject extends android.hardware.biometrics.CryptoObject {
@@ -330,7 +332,8 @@
/**
* Container for callback data from {@link FingerprintManager#authenticate(CryptoObject,
* CancellationSignal, int, AuthenticationCallback, Handler)}.
- * @deprecated See {@link android.hardware.biometrics.BiometricPrompt.AuthenticationResult}
+ *
+ * @removed See {@link android.hardware.biometrics.BiometricPrompt.AuthenticationResult}
*/
@Deprecated
public static class AuthenticationResult {
@@ -392,7 +395,8 @@
* FingerprintManager#authenticate(CryptoObject, CancellationSignal,
* int, AuthenticationCallback, Handler) } must provide an implementation of this for listening to
* fingerprint events.
- * @deprecated See {@link android.hardware.biometrics.BiometricPrompt.AuthenticationCallback}
+ *
+ * @removed See {@link android.hardware.biometrics.BiometricPrompt.AuthenticationCallback}
*/
@Deprecated
public static abstract class AuthenticationCallback
@@ -455,6 +459,7 @@
/**
* Callback structure provided for {@link #detectFingerprint(CancellationSignal,
* FingerprintDetectionCallback, int, Surface)}.
+ *
* @hide
*/
public interface FingerprintDetectionCallback {
@@ -608,7 +613,8 @@
* by <a href="{@docRoot}training/articles/keystore.html">Android Keystore
* facility</a>.
* @throws IllegalStateException if the crypto primitive is not initialized.
- * @deprecated See {@link BiometricPrompt#authenticate(CancellationSignal, Executor,
+ *
+ * @removed See {@link BiometricPrompt#authenticate(CancellationSignal, Executor,
* BiometricPrompt.AuthenticationCallback)} and {@link BiometricPrompt#authenticate(
* BiometricPrompt.CryptoObject, CancellationSignal, Executor,
* BiometricPrompt.AuthenticationCallback)}
@@ -623,6 +629,7 @@
/**
* Per-user version of authenticate.
* @deprecated use {@link #authenticate(CryptoObject, CancellationSignal, AuthenticationCallback, Handler, FingerprintAuthenticateOptions)}.
+ *
* @hide
*/
@Deprecated
@@ -635,6 +642,7 @@
/**
* Per-user and per-sensor version of authenticate.
* @deprecated use {@link #authenticate(CryptoObject, CancellationSignal, AuthenticationCallback, Handler, FingerprintAuthenticateOptions)}.
+ *
* @hide
*/
@Deprecated
@@ -651,6 +659,7 @@
/**
* Version of authenticate with additional options.
+ *
* @hide
*/
@RequiresPermission(anyOf = {USE_BIOMETRIC, USE_FINGERPRINT})
@@ -698,6 +707,7 @@
/**
* Uses the fingerprint hardware to detect for the presence of a finger, without giving details
* about accept/reject/lockout.
+ *
* @hide
*/
@RequiresPermission(USE_BIOMETRIC_INTERNAL)
@@ -740,6 +750,7 @@
* @param callback an object to receive enrollment events
* @param shouldLogMetrics a flag that indicates if enrollment failure/success metrics
* should be logged.
+ *
* @hide
*/
@RequiresPermission(MANAGE_FINGERPRINT)
@@ -809,6 +820,7 @@
/**
* Same as {@link #generateChallenge(int, GenerateChallengeCallback)}, but assumes the first
* enumerated sensor.
+ *
* @hide
*/
@RequiresPermission(MANAGE_FINGERPRINT)
@@ -823,6 +835,7 @@
/**
* Revokes the specified challenge.
+ *
* @hide
*/
@RequiresPermission(MANAGE_FINGERPRINT)
@@ -848,6 +861,7 @@
* @param sensorId Sensor ID that this operation takes effect for
* @param userId User ID that this operation takes effect for.
* @param hardwareAuthToken An opaque token returned by password confirmation.
+ *
* @hide
*/
@RequiresPermission(RESET_FINGERPRINT_LOCKOUT)
@@ -885,6 +899,7 @@
/**
* Removes all fingerprint templates for the given user.
+ *
* @hide
*/
@RequiresPermission(MANAGE_FINGERPRINT)
@@ -1004,6 +1019,7 @@
/**
* Forwards BiometricStateListener to FingerprintService
* @param listener new BiometricStateListener being added
+ *
* @hide
*/
public void registerBiometricStateListener(@NonNull BiometricStateListener listener) {
@@ -1155,7 +1171,8 @@
}
/**
- * This is triggered by SideFpsEventHandler
+ * This is triggered by SideFpsEventHandler.
+ *
* @hide
*/
@RequiresPermission(USE_BIOMETRIC_INTERNAL)
@@ -1168,7 +1185,8 @@
* Determine if there is at least one fingerprint enrolled.
*
* @return true if at least one fingerprint is enrolled, false otherwise
- * @deprecated See {@link BiometricPrompt} and
+ *
+ * @removed See {@link BiometricPrompt} and
* {@link FingerprintManager#FINGERPRINT_ERROR_NO_FINGERPRINTS}
*/
@Deprecated
@@ -1202,7 +1220,8 @@
* Determine if fingerprint hardware is present and functional.
*
* @return true if hardware is present and functional, false otherwise.
- * @deprecated See {@link BiometricPrompt} and
+ *
+ * @removed See {@link BiometricPrompt} and
* {@link FingerprintManager#FINGERPRINT_ERROR_HW_UNAVAILABLE}
*/
@Deprecated
@@ -1228,6 +1247,7 @@
/**
* Get statically configured sensor properties.
+ *
* @hide
*/
@RequiresPermission(USE_BIOMETRIC_INTERNAL)
@@ -1246,6 +1266,7 @@
/**
* Returns whether the device has a power button fingerprint sensor.
* @return boolean indicating whether power button is fingerprint sensor
+ *
* @hide
*/
public boolean isPowerbuttonFps() {
diff --git a/core/java/android/hardware/input/KeyboardLayoutPreviewDrawable.java b/core/java/android/hardware/input/KeyboardLayoutPreviewDrawable.java
index 1cc910c..e47a48d 100644
--- a/core/java/android/hardware/input/KeyboardLayoutPreviewDrawable.java
+++ b/core/java/android/hardware/input/KeyboardLayoutPreviewDrawable.java
@@ -48,14 +48,13 @@
private static final int GRAVITY_RIGHT = 0x2;
private static final int GRAVITY_TOP = 0x4;
private static final int GRAVITY_BOTTOM = 0x8;
- private static final int GRAVITY_CENTER =
- GRAVITY_LEFT | GRAVITY_RIGHT | GRAVITY_TOP | GRAVITY_BOTTOM;
- private static final int GRAVITY_CENTER_HORIZONTAL = GRAVITY_LEFT | GRAVITY_RIGHT;
+ private static final int TEXT_PADDING_IN_DP = 1;
private static final int KEY_PADDING_IN_DP = 3;
private static final int KEYBOARD_PADDING_IN_DP = 10;
private static final int KEY_RADIUS_IN_DP = 5;
private static final int KEYBOARD_RADIUS_IN_DP = 10;
- private static final int GLYPH_TEXT_SIZE_IN_SP = 10;
+ private static final int MIN_GLYPH_TEXT_SIZE_IN_SP = 10;
+ private static final int MAX_GLYPH_TEXT_SIZE_IN_SP = 20;
private final List<KeyDrawable> mKeyDrawables = new ArrayList<>();
@@ -107,6 +106,8 @@
}
int rowCount = keys.length;
float keyHeight = (float) (height - rowCount * 2 * keyPadding) / rowCount;
+ // Based on key height calculate the max text size that can fit for typing keys
+ mResourceProvider.calculateBestTextSizeForKey(keyHeight);
float isoEnterKeyLeft = 0;
float isoEnterKeyTop = 0;
float isoEnterWidthUnit = 0;
@@ -136,16 +137,19 @@
}
if (PhysicalKeyLayout.isSpecialKey(row[j])) {
mKeyDrawables.add(new TypingKey(null, keyRect, keyRadius,
+ mResourceProvider.getTextPadding(),
mResourceProvider.getSpecialKeyPaint(),
mResourceProvider.getSpecialKeyPaint(),
mResourceProvider.getSpecialKeyPaint()));
} else if (PhysicalKeyLayout.isKeyPositionUnsure(row[j])) {
mKeyDrawables.add(new UnsureTypingKey(row[j].glyph(), keyRect,
- keyRadius, mResourceProvider.getTypingKeyPaint(),
+ keyRadius, mResourceProvider.getTextPadding(),
+ mResourceProvider.getTypingKeyPaint(),
mResourceProvider.getPrimaryGlyphPaint(),
mResourceProvider.getSecondaryGlyphPaint()));
} else {
mKeyDrawables.add(new TypingKey(row[j].glyph(), keyRect, keyRadius,
+ mResourceProvider.getTextPadding(),
mResourceProvider.getTypingKeyPaint(),
mResourceProvider.getPrimaryGlyphPaint(),
mResourceProvider.getSecondaryGlyphPaint()));
@@ -192,15 +196,18 @@
private final RectF mKeyRect;
private final float mKeyRadius;
+ private final float mTextPadding;
private final Paint mKeyPaint;
private final Paint mBaseTextPaint;
private final Paint mModifierTextPaint;
private final List<GlyphDrawable> mGlyphDrawables = new ArrayList<>();
private TypingKey(@Nullable PhysicalKeyLayout.KeyGlyph glyphData, RectF keyRect,
- float keyRadius, Paint keyPaint, Paint baseTextPaint, Paint modifierTextPaint) {
+ float keyRadius, float textPadding, Paint keyPaint, Paint baseTextPaint,
+ Paint modifierTextPaint) {
mKeyRect = keyRect;
mKeyRadius = keyRadius;
+ mTextPadding = textPadding;
mKeyPaint = keyPaint;
mBaseTextPaint = baseTextPaint;
mModifierTextPaint = modifierTextPaint;
@@ -219,20 +226,17 @@
if (!glyphData.hasBaseText()) {
return;
}
- boolean isCenter = !glyphData.hasValidAltGrText() && !glyphData.hasValidAltShiftText();
mGlyphDrawables.add(new GlyphDrawable(glyphData.getBaseText(), new RectF(),
- GRAVITY_BOTTOM | (isCenter ? GRAVITY_CENTER_HORIZONTAL : GRAVITY_LEFT),
- mBaseTextPaint));
+ GRAVITY_BOTTOM | GRAVITY_LEFT, mBaseTextPaint));
if (glyphData.hasValidShiftText()) {
mGlyphDrawables.add(new GlyphDrawable(glyphData.getShiftText(), new RectF(),
- GRAVITY_TOP | (isCenter ? GRAVITY_CENTER_HORIZONTAL : GRAVITY_LEFT),
- mModifierTextPaint));
+ GRAVITY_TOP | GRAVITY_LEFT, mModifierTextPaint));
}
if (glyphData.hasValidAltGrText()) {
mGlyphDrawables.add(new GlyphDrawable(glyphData.getAltGrText(), new RectF(),
GRAVITY_BOTTOM | GRAVITY_RIGHT, mModifierTextPaint));
}
- if (glyphData.hasValidAltShiftText()) {
+ if (glyphData.hasValidAltGrShiftText()) {
mGlyphDrawables.add(new GlyphDrawable(glyphData.getAltGrShiftText(), new RectF(),
GRAVITY_TOP | GRAVITY_RIGHT, mModifierTextPaint));
}
@@ -246,15 +250,19 @@
float centerY = keyHeight / 2;
if ((glyph.gravity & GRAVITY_LEFT) != 0) {
centerX -= keyWidth / 4;
+ centerX += mTextPadding / 2;
}
if ((glyph.gravity & GRAVITY_RIGHT) != 0) {
centerX += keyWidth / 4;
+ centerX -= mTextPadding / 2;
}
if ((glyph.gravity & GRAVITY_TOP) != 0) {
centerY -= keyHeight / 4;
+ centerY += mTextPadding / 2;
}
if ((glyph.gravity & GRAVITY_BOTTOM) != 0) {
centerY += keyHeight / 4;
+ centerY -= mTextPadding / 2;
}
Rect textBounds = new Rect();
glyph.paint.getTextBounds(glyph.text, 0, glyph.text.length(), textBounds);
@@ -285,9 +293,9 @@
private static class UnsureTypingKey extends TypingKey {
private UnsureTypingKey(@Nullable PhysicalKeyLayout.KeyGlyph glyphData,
- RectF keyRect, float keyRadius, Paint keyPaint, Paint baseTextPaint,
- Paint modifierTextPaint) {
- super(glyphData, keyRect, keyRadius, createGreyedOutPaint(keyPaint),
+ RectF keyRect, float keyRadius, float textPadding, Paint keyPaint,
+ Paint baseTextPaint, Paint modifierTextPaint) {
+ super(glyphData, keyRect, keyRadius, textPadding, createGreyedOutPaint(keyPaint),
createGreyedOutPaint(baseTextPaint), createGreyedOutPaint(modifierTextPaint));
}
}
@@ -402,8 +410,11 @@
private final Paint mSecondaryGlyphPaint;
private final int mKeyPadding;
private final int mKeyboardPadding;
+ private final float mTextPadding;
private final float mKeyRadius;
private final float mBackgroundRadius;
+ private final float mSpToPxMultiplier;
+ private final Paint.FontMetrics mFontMetrics;
private ResourceProvider(Context context) {
mKeyPadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
@@ -414,8 +425,10 @@
KEY_RADIUS_IN_DP, context.getResources().getDisplayMetrics());
mBackgroundRadius = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
KEYBOARD_RADIUS_IN_DP, context.getResources().getDisplayMetrics());
- int textSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
- GLYPH_TEXT_SIZE_IN_SP, context.getResources().getDisplayMetrics());
+ mSpToPxMultiplier = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 1,
+ context.getResources().getDisplayMetrics());
+ mTextPadding = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+ TEXT_PADDING_IN_DP, context.getResources().getDisplayMetrics());
boolean isDark = (context.getResources().getConfiguration().uiMode
& Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
int typingKeyColor = context.getColor(
@@ -430,15 +443,37 @@
int backgroundColor = context.getColor(
isDark ? android.R.color.system_surface_container_dark
: android.R.color.system_surface_container_light);
- mPrimaryGlyphPaint = createTextPaint(primaryGlyphColor, textSize,
+ mPrimaryGlyphPaint = createTextPaint(primaryGlyphColor,
+ MIN_GLYPH_TEXT_SIZE_IN_SP * mSpToPxMultiplier,
Typeface.create(Typeface.SANS_SERIF, Typeface.BOLD));
- mSecondaryGlyphPaint = createTextPaint(secondaryGlyphColor, textSize,
+ mSecondaryGlyphPaint = createTextPaint(secondaryGlyphColor,
+ MIN_GLYPH_TEXT_SIZE_IN_SP * mSpToPxMultiplier,
Typeface.create(Typeface.SANS_SERIF, Typeface.NORMAL));
+ mFontMetrics = mPrimaryGlyphPaint.getFontMetrics();
mTypingKeyPaint = createFillPaint(typingKeyColor);
mSpecialKeyPaint = createFillPaint(specialKeyColor);
mBackgroundPaint = createFillPaint(backgroundColor);
}
+ private void calculateBestTextSizeForKey(float keyHeight) {
+ int textSize = (int) (mSpToPxMultiplier * MIN_GLYPH_TEXT_SIZE_IN_SP) + 1;
+ while (textSize < mSpToPxMultiplier * MAX_GLYPH_TEXT_SIZE_IN_SP) {
+ updateTextSize(textSize);
+ if (mFontMetrics.bottom - mFontMetrics.top + 3 * mTextPadding > keyHeight / 2) {
+ textSize--;
+ break;
+ }
+ textSize++;
+ }
+ updateTextSize(textSize);
+ }
+
+ private void updateTextSize(float textSize) {
+ mPrimaryGlyphPaint.setTextSize(textSize);
+ mSecondaryGlyphPaint.setTextSize(textSize);
+ mPrimaryGlyphPaint.getFontMetrics(mFontMetrics);
+ }
+
private Paint getBackgroundPaint() {
return mBackgroundPaint;
}
@@ -467,6 +502,10 @@
return mKeyboardPadding;
}
+ private float getTextPadding() {
+ return mTextPadding;
+ }
+
private float getKeyRadius() {
return mKeyRadius;
}
@@ -476,7 +515,8 @@
}
}
- private static Paint createTextPaint(@ColorInt int textColor, int textSize, Typeface typeface) {
+ private static Paint createTextPaint(@ColorInt int textColor, float textSize,
+ Typeface typeface) {
Paint paint = new Paint();
paint.setColor(textColor);
paint.setStyle(Paint.Style.FILL);
diff --git a/core/java/android/hardware/input/PhysicalKeyLayout.java b/core/java/android/hardware/input/PhysicalKeyLayout.java
index 844e02f..cff444f 100644
--- a/core/java/android/hardware/input/PhysicalKeyLayout.java
+++ b/core/java/android/hardware/input/PhysicalKeyLayout.java
@@ -336,11 +336,13 @@
return "";
}
int utf8Char = (kcm.get(keyCode, modifierState) & KeyCharacterMap.COMBINING_ACCENT_MASK);
+ if (utf8Char == 0) {
+ return "";
+ }
if (Character.isValidCodePoint(utf8Char)) {
return String.valueOf(Character.toChars(utf8Char));
- } else {
- return String.valueOf(kcm.getDisplayLabel(keyCode));
}
+ return "â–¡";
}
private static LayoutKey getKey(int keyCode, float keyWeight) {
@@ -434,10 +436,11 @@
}
public boolean hasValidAltGrText() {
- return !TextUtils.isEmpty(mAltGrText) && !TextUtils.equals(mBaseText, mAltGrText);
+ return !TextUtils.isEmpty(mAltGrText) && !TextUtils.equals(mBaseText, mAltGrText)
+ && !TextUtils.equals(mShiftText, mAltGrText);
}
- public boolean hasValidAltShiftText() {
+ public boolean hasValidAltGrShiftText() {
return !TextUtils.isEmpty(mAltGrShiftText)
&& !TextUtils.equals(mBaseText, mAltGrShiftText)
&& !TextUtils.equals(mAltGrText, mAltGrShiftText)
diff --git a/core/java/android/hardware/input/VirtualKeyboard.java b/core/java/android/hardware/input/VirtualKeyboard.java
index 6eb2ae3..6a7d195 100644
--- a/core/java/android/hardware/input/VirtualKeyboard.java
+++ b/core/java/android/hardware/input/VirtualKeyboard.java
@@ -18,7 +18,9 @@
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.companion.virtual.IVirtualDevice;
import android.os.IBinder;
import android.os.RemoteException;
@@ -66,4 +68,15 @@
throw e.rethrowFromSystemServer();
}
}
+
+ /**
+ * @return The id of the {@link android.view.InputDevice} corresponding to this keyboard.
+ * @hide
+ */
+ @SuppressLint("UnflaggedApi") // @TestApi without associated feature.
+ @TestApi
+ @Override
+ public int getInputDeviceId() {
+ return super.getInputDeviceId();
+ }
}
diff --git a/core/java/android/net/thread/flags.aconfig b/core/java/android/net/thread/flags.aconfig
index 6e72f8e..ff762d7 100644
--- a/core/java/android/net/thread/flags.aconfig
+++ b/core/java/android/net/thread/flags.aconfig
@@ -1,8 +1,18 @@
package: "com.android.net.thread.flags"
+# This file contains aconfig flags used from platform code
+# Flags used for module APIs must be in aconfig files under each modules
+
flag {
name: "thread_user_restriction_enabled"
namespace: "thread_network"
description: "Controls whether user restriction on thread networks is enabled"
bug: "307679182"
}
+
+flag {
+ name: "thread_enabled_platform"
+ namespace: "thread_network"
+ description: "Controls whether the Android Thread feature is enabled"
+ bug: "301473012"
+}
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 2b30a2b..7873a76 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -1920,7 +1920,7 @@
* @see DevicePolicyManager#clearUserRestriction(ComponentName, String)
* @see #getUserRestrictions()
*/
- @FlaggedApi("com.android.net.thread.flags.thread_user_restriction_enabled")
+ @FlaggedApi(com.android.net.thread.flags.Flags.FLAG_THREAD_USER_RESTRICTION_ENABLED)
public static final String DISALLOW_THREAD_NETWORK = "no_thread_network";
/**
diff --git a/core/java/android/service/chooser/CustomChoosers.java b/core/java/android/service/chooser/CustomChoosers.java
deleted file mode 100644
index 5b89432..0000000
--- a/core/java/android/service/chooser/CustomChoosers.java
+++ /dev/null
@@ -1,84 +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 android.service.chooser;
-
-import android.annotation.FlaggedApi;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.content.Intent;
-import android.content.pm.ResolveInfo;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Static helper methods that privileged clients can use to initiate Share sessions with extra
- * customization options that aren't usually available in the stock "Resolver/Chooser" flows.
- *
- * @hide
- */
-@FlaggedApi(Flags.FLAG_SUPPORT_NFC_RESOLVER)
-@SystemApi
-public class CustomChoosers {
- /**
- * Intent action to start a Share session with additional customization options. Clients should
- * use the helper methods in this class to configure their customized share intents, and should
- * avoid using this action to construct their own intents directly.
- */
- private static final String ACTION_SHOW_CUSTOMIZED_RESOLVER =
- "android.service.chooser.action.SHOW_CUSTOMIZED_RESOLVER";
-
- /**
- * "Extras" key for an ArrayList of {@link ResolveInfo} records which are to be shown as the
- * targets in the customized share session.
- *
- * @hide
- */
- public static final String EXTRA_RESOLVE_INFOS = "android.service.chooser.extra.RESOLVE_INFOS";
-
- /**
- * Build an {@link Intent} to dispatch a "Chooser flow" that picks a target resolution for the
- * specified {@code target} intent, styling the Chooser UI according to the specified
- * customization parameters.
- *
- * @param target The ambiguous intent that should be resolved to a specific target selected
- * via the Chooser flow.
- * @param title An optional "headline" string to display at the top of the Chooser UI, or null
- * to use the system default.
- * @param resolutionList Explicit resolution info for targets that should be shown in the
- * dispatched Share UI.
- *
- * @hide
- */
- @FlaggedApi(Flags.FLAG_SUPPORT_NFC_RESOLVER)
- @SystemApi
- @NonNull
- public static Intent createNfcResolverIntent(
- @NonNull Intent target,
- @Nullable CharSequence title,
- @NonNull List<ResolveInfo> resolutionList) {
- Intent resolverIntent = new Intent(ACTION_SHOW_CUSTOMIZED_RESOLVER);
- resolverIntent.putExtra(Intent.EXTRA_INTENT, target);
- resolverIntent.putExtra(Intent.EXTRA_TITLE, title);
- resolverIntent.putParcelableArrayListExtra(
- EXTRA_RESOLVE_INFOS, new ArrayList<>(resolutionList));
- return resolverIntent;
- }
-
- private CustomChoosers() {}
-}
diff --git a/core/java/android/service/chooser/flags.aconfig b/core/java/android/service/chooser/flags.aconfig
index add575b..00236df 100644
--- a/core/java/android/service/chooser/flags.aconfig
+++ b/core/java/android/service/chooser/flags.aconfig
@@ -15,13 +15,6 @@
}
flag {
- name: "support_nfc_resolver"
- namespace: "systemui"
- description: "This flag controls the new NFC 'resolver' activity"
- bug: "268089816"
-}
-
-flag {
name: "chooser_payload_toggling"
namespace: "intentresolver"
description: "This flag controls content toggling in Chooser"
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index f169ecd..15fb6cc 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -813,9 +813,9 @@
rt.enabler = parser.getAttributeValue(null, RULE_ATT_ENABLER);
rt.condition = readConditionXml(parser);
- // all default rules and user created rules updated to zenMode important interruptions
- if (rt.zenMode != Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
+ if (!Flags.modesApi() && rt.zenMode != Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
&& Condition.isValidId(rt.conditionId, SYSTEM_AUTHORITY)) {
+ // all default rules and user created rules updated to zenMode important interruptions
Slog.i(TAG, "Updating zenMode of automatic rule " + rt.name);
rt.zenMode = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS;
}
diff --git a/core/java/android/service/notification/flags.aconfig b/core/java/android/service/notification/flags.aconfig
index 446fe3d..c5acc2c 100644
--- a/core/java/android/service/notification/flags.aconfig
+++ b/core/java/android/service/notification/flags.aconfig
@@ -1,4 +1,5 @@
package: "android.service.notification"
+container: "system"
flag {
name: "ranking_update_ashmem"
@@ -12,6 +13,7 @@
namespace: "systemui"
description: "This flag controls the redacting of sensitive notifications from untrusted NotificationListenerServices"
bug: "306271190"
+ is_exported: true
}
flag {
diff --git a/core/java/android/service/ondeviceintelligence/IOnDeviceIntelligenceService.aidl b/core/java/android/service/ondeviceintelligence/IOnDeviceIntelligenceService.aidl
new file mode 100644
index 0000000..bbb4bc6
--- /dev/null
+++ b/core/java/android/service/ondeviceintelligence/IOnDeviceIntelligenceService.aidl
@@ -0,0 +1,44 @@
+/*
+ * 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 android.service.ondeviceintelligence;
+
+import android.os.PersistableBundle;
+import android.os.ParcelFileDescriptor;
+import android.os.ICancellationSignal;
+import android.os.RemoteCallback;
+import android.app.ondeviceintelligence.IDownloadCallback;
+import android.app.ondeviceintelligence.Feature;
+import android.app.ondeviceintelligence.IFeatureCallback;
+import android.app.ondeviceintelligence.IListFeaturesCallback;
+import android.app.ondeviceintelligence.IFeatureDetailsCallback;
+import com.android.internal.infra.AndroidFuture;
+
+
+/**
+ * Interface for a concrete implementation to provide on device intelligence services.
+ *
+ * @hide
+ */
+oneway interface IOnDeviceIntelligenceService {
+ void getVersion(in RemoteCallback remoteCallback);
+ void getFeature(in int featureId, in IFeatureCallback featureCallback);
+ void listFeatures(in IListFeaturesCallback listFeaturesCallback);
+ void getFeatureDetails(in Feature feature, in IFeatureDetailsCallback featureDetailsCallback);
+ void getReadOnlyFileDescriptor(in String fileName, in AndroidFuture<ParcelFileDescriptor> future);
+ void getReadOnlyFeatureFileDescriptorMap(in Feature feature, in RemoteCallback remoteCallback);
+ void requestFeatureDownload(in Feature feature, in ICancellationSignal cancellationSignal, in IDownloadCallback downloadCallback);
+}
\ No newline at end of file
diff --git a/core/java/android/service/ondeviceintelligence/IOnDeviceTrustedInferenceService.aidl b/core/java/android/service/ondeviceintelligence/IOnDeviceTrustedInferenceService.aidl
new file mode 100644
index 0000000..08eb927
--- /dev/null
+++ b/core/java/android/service/ondeviceintelligence/IOnDeviceTrustedInferenceService.aidl
@@ -0,0 +1,44 @@
+/*
+ * 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 android.service.ondeviceintelligence;
+
+import android.app.ondeviceintelligence.IStreamingResponseCallback;
+import android.app.ondeviceintelligence.IResponseCallback;
+import android.app.ondeviceintelligence.ITokenCountCallback;
+import android.app.ondeviceintelligence.IProcessingSignal;
+import android.app.ondeviceintelligence.Content;
+import android.app.ondeviceintelligence.Feature;
+import android.os.ICancellationSignal;
+import android.service.ondeviceintelligence.IRemoteStorageService;
+
+
+/**
+ * Interface for a concrete implementation to provide on device trusted inference.
+ *
+ * @hide
+ */
+oneway interface IOnDeviceTrustedInferenceService {
+ void registerRemoteStorageService(in IRemoteStorageService storageService);
+ void requestTokenCount(in Feature feature, in Content request, in ICancellationSignal cancellationSignal,
+ in ITokenCountCallback tokenCountCallback);
+ void processRequest(in Feature feature, in Content request, in int requestType,
+ in ICancellationSignal cancellationSignal, in IProcessingSignal processingSignal,
+ in IResponseCallback callback);
+ void processRequestStreaming(in Feature feature, in Content request, in int requestType,
+ in ICancellationSignal cancellationSignal, in IProcessingSignal processingSignal,
+ in IStreamingResponseCallback callback);
+}
\ No newline at end of file
diff --git a/core/java/android/service/ondeviceintelligence/IRemoteStorageService.aidl b/core/java/android/service/ondeviceintelligence/IRemoteStorageService.aidl
new file mode 100644
index 0000000..a6f49e1
--- /dev/null
+++ b/core/java/android/service/ondeviceintelligence/IRemoteStorageService.aidl
@@ -0,0 +1,34 @@
+/*
+ * 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 android.service.ondeviceintelligence;
+
+import android.app.ondeviceintelligence.Feature;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteCallback;
+
+import com.android.internal.infra.AndroidFuture;
+
+/**
+ * Interface for a concrete implementation to provide access to storage read access
+ * for the isolated process.
+ *
+ * @hide
+ */
+oneway interface IRemoteStorageService {
+ void getReadOnlyFileDescriptor(in String filePath, in AndroidFuture<ParcelFileDescriptor> future);
+ void getReadOnlyFeatureFileDescriptorMap(in Feature feature, in RemoteCallback remoteCallback);
+}
\ No newline at end of file
diff --git a/core/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java b/core/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java
new file mode 100644
index 0000000..0cba1d3
--- /dev/null
+++ b/core/java/android/service/ondeviceintelligence/OnDeviceIntelligenceService.java
@@ -0,0 +1,383 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.ondeviceintelligence;
+
+import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE;
+
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.app.ondeviceintelligence.DownloadCallback;
+import android.app.ondeviceintelligence.Feature;
+import android.app.ondeviceintelligence.FeatureDetails;
+import android.app.ondeviceintelligence.IDownloadCallback;
+import android.app.ondeviceintelligence.IFeatureCallback;
+import android.app.ondeviceintelligence.IFeatureDetailsCallback;
+import android.app.ondeviceintelligence.IListFeaturesCallback;
+import android.app.ondeviceintelligence.OnDeviceIntelligenceManager;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.IBinder;
+import android.os.ICancellationSignal;
+import android.os.OutcomeReceiver;
+import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.internal.infra.AndroidFuture;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.function.Consumer;
+import java.util.function.LongConsumer;
+
+/**
+ * Abstract base class for performing setup for on-device inference and providing file access to
+ * the isolated counter part {@link OnDeviceTrustedInferenceService}.
+ *
+ * <p> A service that provides configuration and model files relevant to performing inference on
+ * device. The system's default OnDeviceIntelligenceService implementation is configured in
+ * {@code config_defaultOnDeviceIntelligenceService}. If this config has no value, a stub is
+ * returned.
+ *
+ * <pre>
+ * {@literal
+ * <service android:name=".SampleOnDeviceIntelligenceService"
+ * android:permission="android.permission.BIND_ON_DEVICE_INTELLIGENCE_SERVICE">
+ * </service>}
+ * </pre>
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(FLAG_ENABLE_ON_DEVICE_INTELLIGENCE)
+public abstract class OnDeviceIntelligenceService extends Service {
+ private static final String TAG = OnDeviceIntelligenceService.class.getSimpleName();
+
+ /**
+ * The {@link Intent} that must be declared as handled by the service. To be supported, the
+ * service must also require the
+ * {@link android.Manifest.permission#BIND_ON_DEVICE_INTELLIGENCE_SERVICE}
+ * permission so that other applications can not abuse it.
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+ public static final String SERVICE_INTERFACE =
+ "android.service.ondeviceintelligence.OnDeviceIntelligenceService";
+
+ /**
+ * @hide
+ */
+ @Nullable
+ @Override
+ public final IBinder onBind(@NonNull Intent intent) {
+ if (SERVICE_INTERFACE.equals(intent.getAction())) {
+ return new IOnDeviceIntelligenceService.Stub() {
+ /** {@inheritDoc} */
+ @Override
+ public void getVersion(RemoteCallback remoteCallback) {
+ Objects.requireNonNull(remoteCallback);
+ OnDeviceIntelligenceService.this.onGetVersion(l -> {
+ Bundle b = new Bundle();
+ b.putLong(OnDeviceIntelligenceManager.API_VERSION_BUNDLE_KEY, l);
+ remoteCallback.sendResult(b);
+ });
+ }
+
+ @Override
+ public void listFeatures(IListFeaturesCallback listFeaturesCallback) {
+ Objects.requireNonNull(listFeaturesCallback);
+ OnDeviceIntelligenceService.this.onListFeatures(
+ wrapListFeaturesCallback(listFeaturesCallback));
+ }
+
+ @Override
+ public void getFeature(int id, IFeatureCallback featureCallback) {
+ Objects.requireNonNull(featureCallback);
+ OnDeviceIntelligenceService.this.onGetFeature(id,
+ wrapFeatureCallback(featureCallback));
+ }
+
+
+ @Override
+ public void getFeatureDetails(Feature feature,
+ IFeatureDetailsCallback featureDetailsCallback) {
+ Objects.requireNonNull(feature);
+ Objects.requireNonNull(featureDetailsCallback);
+
+ OnDeviceIntelligenceService.this.onGetFeatureDetails(feature,
+ wrapFeatureDetailsCallback(featureDetailsCallback));
+ }
+
+ @Override
+ public void requestFeatureDownload(Feature feature,
+ ICancellationSignal cancellationSignal,
+ IDownloadCallback downloadCallback) {
+ Objects.requireNonNull(feature);
+ Objects.requireNonNull(downloadCallback);
+
+ OnDeviceIntelligenceService.this.onDownloadFeature(feature,
+ CancellationSignal.fromTransport(cancellationSignal),
+ wrapDownloadCallback(downloadCallback));
+ }
+
+ @Override
+ public void getReadOnlyFileDescriptor(String fileName,
+ AndroidFuture<ParcelFileDescriptor> future) {
+ Objects.requireNonNull(fileName);
+ Objects.requireNonNull(future);
+
+ OnDeviceIntelligenceService.this.onGetReadOnlyFileDescriptor(fileName,
+ future);
+ }
+
+ @Override
+ public void getReadOnlyFeatureFileDescriptorMap(
+ Feature feature, RemoteCallback remoteCallback) {
+ Objects.requireNonNull(feature);
+ Objects.requireNonNull(remoteCallback);
+
+ OnDeviceIntelligenceService.this.onGetReadOnlyFeatureFileDescriptorMap(
+ feature, parcelFileDescriptorMap -> {
+ Bundle bundle = new Bundle();
+ parcelFileDescriptorMap.forEach(bundle::putParcelable);
+ remoteCallback.sendResult(bundle);
+ });
+ }
+ };
+ }
+ Slog.w(TAG, "Incorrect service interface, returning null.");
+ return null;
+ }
+
+ private OutcomeReceiver<Feature,
+ OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException> wrapFeatureCallback(
+ IFeatureCallback featureCallback) {
+ return new OutcomeReceiver<>() {
+ @Override
+ public void onResult(@NonNull Feature feature) {
+ try {
+ featureCallback.onSuccess(feature);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending feature: " + e);
+ }
+ }
+
+ @Override
+ public void onError(
+ @NonNull OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException exception) {
+ try {
+ featureCallback.onFailure(exception.getErrorCode(), exception.getMessage(),
+ exception.getErrorParams());
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending download feature: " + e);
+ }
+ }
+ };
+
+ }
+
+ private OutcomeReceiver<List<Feature>,
+ OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException> wrapListFeaturesCallback(
+ IListFeaturesCallback listFeaturesCallback) {
+ return new OutcomeReceiver<>() {
+ @Override
+ public void onResult(@NonNull List<Feature> features) {
+ try {
+ listFeaturesCallback.onSuccess(features);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending feature: " + e);
+ }
+ }
+
+ @Override
+ public void onError(
+ @NonNull OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException exception) {
+ try {
+ listFeaturesCallback.onFailure(exception.getErrorCode(), exception.getMessage(),
+ exception.getErrorParams());
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending download feature: " + e);
+ }
+ }
+ };
+ }
+
+ private OutcomeReceiver<FeatureDetails,
+ OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException> wrapFeatureDetailsCallback(
+ IFeatureDetailsCallback featureStatusCallback) {
+ return new OutcomeReceiver<>() {
+ @Override
+ public void onResult(FeatureDetails result) {
+ try {
+ featureStatusCallback.onSuccess(result);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending feature status: " + e);
+ }
+ }
+
+ @Override
+ public void onError(
+ @NonNull OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException exception) {
+ try {
+ featureStatusCallback.onFailure(exception.getErrorCode(),
+ exception.getMessage(), exception.getErrorParams());
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending feature status: " + e);
+ }
+ }
+ };
+ }
+
+
+ private DownloadCallback wrapDownloadCallback(IDownloadCallback downloadCallback) {
+ return new DownloadCallback() {
+ @Override
+ public void onDownloadStarted(long bytesToDownload) {
+ try {
+ downloadCallback.onDownloadStarted(bytesToDownload);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending download status: " + e);
+ }
+ }
+
+ @Override
+ public void onDownloadFailed(int failureStatus,
+ String errorMessage, @NonNull PersistableBundle errorParams) {
+ try {
+ downloadCallback.onDownloadFailed(failureStatus, errorMessage, errorParams);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending download status: " + e);
+ }
+ }
+
+ @Override
+ public void onDownloadProgress(long totalBytesDownloaded) {
+ try {
+ downloadCallback.onDownloadProgress(totalBytesDownloaded);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending download status: " + e);
+ }
+ }
+
+ @Override
+ public void onDownloadCompleted(@NonNull PersistableBundle persistableBundle) {
+ try {
+ downloadCallback.onDownloadCompleted(persistableBundle);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending download status: " + e);
+ }
+ }
+ };
+ }
+
+ private void onGetReadOnlyFileDescriptor(@NonNull String fileName,
+ @NonNull AndroidFuture<ParcelFileDescriptor> future) {
+ Slog.v(TAG, "onGetReadOnlyFileDescriptor " + fileName);
+ Binder.withCleanCallingIdentity(() -> {
+ Slog.v(TAG,
+ "onGetReadOnlyFileDescriptor: " + fileName + " under internal app storage.");
+ File f = new File(getBaseContext().getFilesDir(), fileName);
+ ParcelFileDescriptor pfd = null;
+ try {
+ pfd = ParcelFileDescriptor.open(f, ParcelFileDescriptor.MODE_READ_ONLY);
+ Slog.d(TAG, "Successfully opened a file with ParcelFileDescriptor.");
+ } catch (FileNotFoundException e) {
+ Slog.e(TAG, "Cannot open file. No ParcelFileDescriptor returned.");
+ } finally {
+ future.complete(pfd);
+ }
+ });
+ }
+
+ /**
+ * Provide implementation for a scenario when caller wants to get all feature related
+ * file-descriptors that might be required for processing a request for the corresponding the
+ * feature.
+ *
+ * @param feature the feature for which files need to be opened.
+ * @param fileDescriptorMapConsumer callback to be populated with a map of file-path and
+ * corresponding ParcelDescriptor to be used in a remote
+ * service.
+ */
+ public abstract void onGetReadOnlyFeatureFileDescriptorMap(
+ @NonNull Feature feature,
+ @NonNull Consumer<Map<String, ParcelFileDescriptor>> fileDescriptorMapConsumer);
+
+ /**
+ * Request download for feature that is requested and listen to download progress updates. If
+ * the download completes successfully, success callback should be populated.
+ *
+ * @param feature the feature for which files need to be downlaoded.
+ * process.
+ * @param cancellationSignal signal to attach a listener to, and receive cancellation signals
+ * from thw client.
+ * @param downloadCallback callback to populate download updates for clients to listen on..
+ */
+ public abstract void onDownloadFeature(
+ @NonNull Feature feature,
+ @Nullable CancellationSignal cancellationSignal,
+ @NonNull DownloadCallback downloadCallback);
+
+ /**
+ * Provide feature details for the passed in feature. Usually the client and remote
+ * implementation use the {@link Feature#getFeatureParams()} as a hint to communicate what
+ * details the client is looking for.
+ *
+ * @param feature the feature for which status needs to be known.
+ * @param featureStatusCallback callback to populate the resulting feature status.
+ */
+ public abstract void onGetFeatureDetails(@NonNull Feature feature,
+ @NonNull OutcomeReceiver<FeatureDetails,
+ OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException> featureStatusCallback);
+
+
+ /**
+ * Get feature using the provided identifier to the remote implementation.
+ *
+ * @param featureCallback callback to populate the features list.
+ */
+ public abstract void onGetFeature(int featureId,
+ @NonNull OutcomeReceiver<Feature,
+ OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException> featureCallback);
+
+ /**
+ * List all features which are available in the remote implementation. The implementation might
+ * choose to provide only a certain list of features based on the caller.
+ *
+ * @param listFeaturesCallback callback to populate the features list.
+ */
+ public abstract void onListFeatures(@NonNull OutcomeReceiver<List<Feature>,
+ OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException> listFeaturesCallback);
+
+ /**
+ * Provides a long value representing the version of the remote implementation processing
+ * requests.
+ *
+ * @param versionConsumer consumer to populate the version.
+ */
+ public abstract void onGetVersion(@NonNull LongConsumer versionConsumer);
+}
diff --git a/core/java/android/service/ondeviceintelligence/OnDeviceTrustedInferenceService.java b/core/java/android/service/ondeviceintelligence/OnDeviceTrustedInferenceService.java
new file mode 100644
index 0000000..96982e3
--- /dev/null
+++ b/core/java/android/service/ondeviceintelligence/OnDeviceTrustedInferenceService.java
@@ -0,0 +1,410 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.ondeviceintelligence;
+
+import static android.app.ondeviceintelligence.flags.Flags.FLAG_ENABLE_ON_DEVICE_INTELLIGENCE;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.FlaggedApi;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.app.ondeviceintelligence.Content;
+import android.app.ondeviceintelligence.Feature;
+import android.app.ondeviceintelligence.IProcessingSignal;
+import android.app.ondeviceintelligence.IResponseCallback;
+import android.app.ondeviceintelligence.IStreamingResponseCallback;
+import android.app.ondeviceintelligence.ITokenCountCallback;
+import android.app.ondeviceintelligence.OnDeviceIntelligenceManager;
+import android.app.ondeviceintelligence.ProcessingSignal;
+import android.app.ondeviceintelligence.StreamingResponseReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.CancellationSignal;
+import android.os.IBinder;
+import android.os.ICancellationSignal;
+import android.os.OutcomeReceiver;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.infra.AndroidFuture;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
+/**
+ * Abstract base class for performing inference in a isolated process. This service exposes its
+ * methods via {@link android.app.ondeviceintelligence.OnDeviceIntelligenceManager}.
+ *
+ * <p> A service that provides methods to perform on-device inference both in streaming and
+ * non-streaming fashion. Also, provides a way to register a storage service that will be used to
+ * read-only access files from the {@link OnDeviceIntelligenceService} counterpart. </p>
+ *
+ * <pre>
+ * {@literal
+ * <service android:name=".SampleTrustedInferenceService"
+ * android:permission="android.permission.BIND_ONDEVICE_TRUSTED_INFERENCE_SERVICE"
+ * android:isolatedProcess="true">
+ * </service>}
+ * </pre>
+ *
+ * @hide
+ */
+@SystemApi
+@FlaggedApi(FLAG_ENABLE_ON_DEVICE_INTELLIGENCE)
+public abstract class OnDeviceTrustedInferenceService extends Service {
+ private static final String TAG = OnDeviceTrustedInferenceService.class.getSimpleName();
+
+ /**
+ * The {@link Intent} that must be declared as handled by the service. To be supported, the
+ * service must also require the
+ * {@link android.Manifest.permission#BIND_ON_DEVICE_TRUSTED_INFERENCE_SERVICE}
+ * permission so that other applications can not abuse it.
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+ public static final String SERVICE_INTERFACE =
+ "android.service.ondeviceintelligence.OnDeviceTrustedInferenceService";
+
+ private IRemoteStorageService mRemoteStorageService;
+
+ /**
+ * @hide
+ */
+ @Nullable
+ @Override
+ public final IBinder onBind(@NonNull Intent intent) {
+ if (SERVICE_INTERFACE.equals(intent.getAction())) {
+ return new IOnDeviceTrustedInferenceService.Stub() {
+ @Override
+ public void registerRemoteStorageService(IRemoteStorageService storageService) {
+ Objects.requireNonNull(storageService);
+ mRemoteStorageService = storageService;
+ }
+
+ @Override
+ public void requestTokenCount(Feature feature, Content request,
+ ICancellationSignal cancellationSignal,
+ ITokenCountCallback tokenCountCallback) {
+ Objects.requireNonNull(feature);
+ Objects.requireNonNull(tokenCountCallback);
+ OnDeviceTrustedInferenceService.this.onCountTokens(feature,
+ request,
+ CancellationSignal.fromTransport(cancellationSignal),
+ wrapTokenCountCallback(tokenCountCallback));
+ }
+
+ @Override
+ public void processRequestStreaming(Feature feature, Content request,
+ int requestType, ICancellationSignal cancellationSignal,
+ IProcessingSignal processingSignal,
+ IStreamingResponseCallback callback) {
+ Objects.requireNonNull(feature);
+ Objects.requireNonNull(request);
+ Objects.requireNonNull(callback);
+
+ OnDeviceTrustedInferenceService.this.onProcessRequestStreaming(feature,
+ request,
+ requestType,
+ CancellationSignal.fromTransport(cancellationSignal),
+ ProcessingSignal.fromTransport(processingSignal),
+ wrapStreamingResponseCallback(callback)
+ );
+ }
+
+ @Override
+ public void processRequest(Feature feature, Content request,
+ int requestType, ICancellationSignal cancellationSignal,
+ IProcessingSignal processingSignal,
+ IResponseCallback callback) {
+ Objects.requireNonNull(feature);
+ Objects.requireNonNull(request);
+ Objects.requireNonNull(callback);
+
+
+ OnDeviceTrustedInferenceService.this.onProcessRequest(feature, request,
+ requestType, CancellationSignal.fromTransport(cancellationSignal),
+ ProcessingSignal.fromTransport(processingSignal),
+ wrapResponseCallback(callback)
+ );
+ }
+ };
+ }
+ Slog.w(TAG, "Incorrect service interface, returning null.");
+ return null;
+ }
+
+ /**
+ * Invoked when caller wants to obtain a count of number of tokens present in the passed in
+ * Request associated with the provided feature.
+ * The expectation from the implementation is that when processing is complete, it
+ * should provide the token count in the {@link OutcomeReceiver#onResult}.
+ *
+ * @param feature feature which is associated with the request.
+ * @param request request that requires processing.
+ * @param cancellationSignal Cancellation Signal to receive cancellation events from client and
+ * configure a listener to.
+ * @param callback callback to populate failure and full response for the provided
+ * request.
+ */
+ @NonNull
+ public abstract void onCountTokens(
+ @NonNull Feature feature,
+ @NonNull Content request,
+ @Nullable CancellationSignal cancellationSignal,
+ @NonNull OutcomeReceiver<Long,
+ OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException> callback);
+
+ /**
+ * Invoked when caller provides a request for a particular feature to be processed in a
+ * streaming manner. The expectation from the implementation is that when processing the
+ * request,
+ * it periodically populates the {@link StreamingResponseReceiver#onNewContent} to continuously
+ * provide partial Content results for the caller to utilize. Optionally the implementation can
+ * provide the complete response in the {@link StreamingResponseReceiver#onResult} upon
+ * processing completion.
+ *
+ * @param feature feature which is associated with the request.
+ * @param request request that requires processing.
+ * @param requestType identifier representing the type of request.
+ * @param cancellationSignal Cancellation Signal to receive cancellation events from client and
+ * configure a listener to.
+ * @param processingSignal Signal to receive custom action instructions from client.
+ * @param callback callback to populate the partial responses, failure and optionally
+ * full response for the provided request.
+ */
+ @NonNull
+ public abstract void onProcessRequestStreaming(
+ @NonNull Feature feature,
+ @NonNull Content request,
+ @OnDeviceIntelligenceManager.RequestType int requestType,
+ @Nullable CancellationSignal cancellationSignal,
+ @Nullable ProcessingSignal processingSignal,
+ @NonNull StreamingResponseReceiver<Content, Content,
+ OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException> callback);
+
+ /**
+ * Invoked when caller provides a request for a particular feature to be processed in one shot
+ * completely.
+ * The expectation from the implementation is that when processing the request is complete, it
+ * should
+ * provide the complete response in the {@link OutcomeReceiver#onResult}.
+ *
+ * @param feature feature which is associated with the request.
+ * @param request request that requires processing.
+ * @param requestType identifier representing the type of request.
+ * @param cancellationSignal Cancellation Signal to receive cancellation events from client and
+ * configure a listener to.
+ * @param processingSignal Signal to receive custom action instructions from client.
+ * @param callback callback to populate failure and full response for the provided
+ * request.
+ */
+ @NonNull
+ public abstract void onProcessRequest(
+ @NonNull Feature feature,
+ @NonNull Content request,
+ @OnDeviceIntelligenceManager.RequestType int requestType,
+ @Nullable CancellationSignal cancellationSignal,
+ @Nullable ProcessingSignal processingSignal,
+ @NonNull OutcomeReceiver<Content,
+ OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException> callback);
+
+ /**
+ * Overrides {@link Context#openFileInput} to read files with the given file names under the
+ * internal app storage of the {@link OnDeviceIntelligenceService}, i.e., only files stored in
+ * {@link Context#getFilesDir()} can be opened.
+ */
+ @Override
+ public final FileInputStream openFileInput(@NonNull String filename) throws
+ FileNotFoundException {
+ try {
+ AndroidFuture<ParcelFileDescriptor> future = new AndroidFuture<>();
+ mRemoteStorageService.getReadOnlyFileDescriptor(filename, future);
+ ParcelFileDescriptor pfd = future.get();
+ return new FileInputStream(pfd.getFileDescriptor());
+ } catch (RemoteException | ExecutionException | InterruptedException e) {
+ Log.w(TAG, "Cannot open file due to remote service failure");
+ throw new FileNotFoundException(e.getMessage());
+ }
+ }
+
+ /**
+ * Provides read-only access to the internal app storage via the
+ * {@link OnDeviceIntelligenceService}. This is an asynchronous implementation for
+ * {@link #openFileInput(String)}.
+ *
+ * @param fileName File name relative to the {@link Context#getFilesDir()}.
+ * @param resultConsumer Consumer to populate the corresponding file stream in.
+ */
+ public final void openFileInputAsync(@NonNull String fileName,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull Consumer<FileInputStream> resultConsumer) throws FileNotFoundException {
+ AndroidFuture<ParcelFileDescriptor> future = new AndroidFuture<>();
+ try {
+ mRemoteStorageService.getReadOnlyFileDescriptor(fileName, future);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Cannot open file due to remote service failure");
+ throw new FileNotFoundException(e.getMessage());
+ }
+ future.whenCompleteAsync((pfd, err) -> {
+ if (err != null) {
+ Log.e(TAG, "Failure when reading file: " + fileName + err);
+ executor.execute(() -> resultConsumer.accept(null));
+ } else {
+ executor.execute(
+ () -> resultConsumer.accept(new FileInputStream(pfd.getFileDescriptor())));
+ }
+ }, executor);
+ }
+
+ /**
+ * Provides access to all file streams required for feature via the
+ * {@link OnDeviceIntelligenceService}.
+ *
+ * @param feature Feature for which the associated files should be fetched.
+ * @param executor Executor to run the consumer callback on.
+ * @param resultConsumer Consumer to receive a map of filePath to the corresponding file input
+ * stream.
+ */
+ public final void fetchFeatureFileInputStreamMap(@NonNull Feature feature,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull Consumer<Map<String, FileInputStream>> resultConsumer) {
+ try {
+ mRemoteStorageService.getReadOnlyFeatureFileDescriptorMap(feature,
+ wrapResultReceiverAsReadOnly(resultConsumer, executor));
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private RemoteCallback wrapResultReceiverAsReadOnly(
+ @NonNull Consumer<Map<String, FileInputStream>> resultConsumer,
+ @NonNull Executor executor) {
+ return new RemoteCallback(result -> {
+ if (result == null) {
+ executor.execute(() -> resultConsumer.accept(new HashMap<>()));
+ } else {
+ Map<String, FileInputStream> bundleMap = new HashMap<>();
+ result.keySet().forEach(key -> {
+ ParcelFileDescriptor pfd = result.getParcelable(key,
+ ParcelFileDescriptor.class);
+ if (pfd != null) {
+ bundleMap.put(key, new FileInputStream(pfd.getFileDescriptor()));
+ }
+ });
+ executor.execute(() -> resultConsumer.accept(bundleMap));
+ }
+ });
+ }
+
+ private OutcomeReceiver<Content,
+ OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException> wrapResponseCallback(
+ IResponseCallback callback) {
+ return new OutcomeReceiver<>() {
+ @Override
+ public void onResult(@androidx.annotation.NonNull Content response) {
+ try {
+ callback.onSuccess(response);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending result: " + e);
+ }
+ }
+
+ @Override
+ public void onError(
+ OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException exception) {
+ try {
+ callback.onFailure(exception.getErrorCode(), exception.getMessage(),
+ exception.getErrorParams());
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending result: " + e);
+ }
+ }
+ };
+ }
+
+ private StreamingResponseReceiver<Content, Content,
+ OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException> wrapStreamingResponseCallback(
+ IStreamingResponseCallback callback) {
+ return new StreamingResponseReceiver<>() {
+ @Override
+ public void onNewContent(@androidx.annotation.NonNull Content content) {
+ try {
+ callback.onNewContent(content);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending result: " + e);
+ }
+ }
+
+ @Override
+ public void onResult(@androidx.annotation.NonNull Content response) {
+ try {
+ callback.onSuccess(response);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending result: " + e);
+ }
+ }
+
+ @Override
+ public void onError(
+ OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException exception) {
+ try {
+ callback.onFailure(exception.getErrorCode(), exception.getMessage(),
+ exception.getErrorParams());
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending result: " + e);
+ }
+ }
+ };
+ }
+
+ private OutcomeReceiver<Long,
+ OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException> wrapTokenCountCallback(
+ ITokenCountCallback tokenCountCallback) {
+ return new OutcomeReceiver<>() {
+ @Override
+ public void onResult(Long tokenCount) {
+ try {
+ tokenCountCallback.onSuccess(tokenCount);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending result: " + e);
+ }
+ }
+
+ @Override
+ public void onError(
+ OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException exception) {
+ try {
+ tokenCountCallback.onFailure(exception.getErrorCode(), exception.getMessage(),
+ exception.getErrorParams());
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error sending failure: " + e);
+ }
+ }
+ };
+ }
+}
diff --git a/core/java/android/service/voice/VisualQueryDetectedResult.java b/core/java/android/service/voice/VisualQueryDetectedResult.java
index 322148a..3b61794 100644
--- a/core/java/android/service/voice/VisualQueryDetectedResult.java
+++ b/core/java/android/service/voice/VisualQueryDetectedResult.java
@@ -68,6 +68,22 @@
return 15;
}
+ /**
+ * Detected signal representing the arbitrary data that will make accessibility detections work.
+ *
+ * This field should only be set if the device setting for allowing accessibility data is on
+ * based on the result of {@link VisualQueryDetector#isAccessibilityDetectionEnabled()}. If the
+ * enable bit return by the method is {@code false}, it would suggest a failure to egress the
+ * {@link VisualQueryDetectedResult} object with this field set. The system server will prevent
+ * egress and invoke
+ * {@link VisualQueryDetector.Callback#onFailure(VisualQueryDetectionServiceFailure)}.
+ */
+ @Nullable
+ private final byte[] mAccessibilityDetectionData;
+ private static byte[] defaultAccessibilityDetectionData() {
+ return null;
+ }
+
private void onConstructed() {
Preconditions.checkArgumentInRange(mSpeakerId, 0, getMaxSpeakerId(), "speakerId");
}
@@ -78,7 +94,10 @@
* @hide
*/
public Builder buildUpon() {
- return new Builder().setPartialQuery(mPartialQuery).setSpeakerId(mSpeakerId);
+ return new Builder()
+ .setPartialQuery(mPartialQuery)
+ .setSpeakerId(mSpeakerId)
+ .setAccessibilityDetectionData(mAccessibilityDetectionData);
}
@@ -98,11 +117,13 @@
@DataClass.Generated.Member
/* package-private */ VisualQueryDetectedResult(
@NonNull String partialQuery,
- int speakerId) {
+ int speakerId,
+ @Nullable byte[] accessibilityDetectionData) {
this.mPartialQuery = partialQuery;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mPartialQuery);
this.mSpeakerId = speakerId;
+ this.mAccessibilityDetectionData = accessibilityDetectionData;
onConstructed();
}
@@ -125,6 +146,16 @@
return mSpeakerId;
}
+ /**
+ * Detected signal representing the data for allowing accessibility feature. This field can
+ * only be set when the secure device settings is set to true by either settings page UI or
+ * {@link VisualQueryDetector@setAccessibilityDetectionEnabled(boolean)}
+ */
+ @DataClass.Generated.Member
+ public @Nullable byte[] getAccessibilityDetectionData() {
+ return mAccessibilityDetectionData;
+ }
+
@Override
@DataClass.Generated.Member
public String toString() {
@@ -133,7 +164,8 @@
return "VisualQueryDetectedResult { " +
"partialQuery = " + mPartialQuery + ", " +
- "speakerId = " + mSpeakerId +
+ "speakerId = " + mSpeakerId + ", " +
+ "accessibilityDetectionData = " + java.util.Arrays.toString(mAccessibilityDetectionData) +
" }";
}
@@ -151,7 +183,8 @@
//noinspection PointlessBooleanExpression
return true
&& Objects.equals(mPartialQuery, that.mPartialQuery)
- && mSpeakerId == that.mSpeakerId;
+ && mSpeakerId == that.mSpeakerId
+ && java.util.Arrays.equals(mAccessibilityDetectionData, that.mAccessibilityDetectionData);
}
@Override
@@ -163,6 +196,7 @@
int _hash = 1;
_hash = 31 * _hash + Objects.hashCode(mPartialQuery);
_hash = 31 * _hash + mSpeakerId;
+ _hash = 31 * _hash + java.util.Arrays.hashCode(mAccessibilityDetectionData);
return _hash;
}
@@ -174,6 +208,7 @@
dest.writeString(mPartialQuery);
dest.writeInt(mSpeakerId);
+ dest.writeByteArray(mAccessibilityDetectionData);
}
@Override
@@ -189,11 +224,13 @@
String partialQuery = in.readString();
int speakerId = in.readInt();
+ byte[] accessibilityDetectionData = in.createByteArray();
this.mPartialQuery = partialQuery;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mPartialQuery);
this.mSpeakerId = speakerId;
+ this.mAccessibilityDetectionData = accessibilityDetectionData;
onConstructed();
}
@@ -221,6 +258,7 @@
private @NonNull String mPartialQuery;
private int mSpeakerId;
+ private @Nullable byte[] mAccessibilityDetectionData;
private long mBuilderFieldsSet = 0L;
@@ -251,10 +289,23 @@
return this;
}
+ /**
+ * Detected signal representing the data for allowing accessibility feature. This field can
+ * only be set when the secure device settings is set to true by either settings page UI or
+ * {@link VisualQueryDetector@setAccessibilityDetectionEnabled(boolean)}
+ */
+ @DataClass.Generated.Member
+ public @NonNull Builder setAccessibilityDetectionData(@NonNull byte... value) {
+ checkNotUsed();
+ mBuilderFieldsSet |= 0x4;
+ mAccessibilityDetectionData = value;
+ return this;
+ }
+
/** Builds the instance. This builder should not be touched after calling this! */
public @NonNull VisualQueryDetectedResult build() {
checkNotUsed();
- mBuilderFieldsSet |= 0x4; // Mark builder used
+ mBuilderFieldsSet |= 0x8; // Mark builder used
if ((mBuilderFieldsSet & 0x1) == 0) {
mPartialQuery = defaultPartialQuery();
@@ -262,14 +313,18 @@
if ((mBuilderFieldsSet & 0x2) == 0) {
mSpeakerId = defaultSpeakerId();
}
+ if ((mBuilderFieldsSet & 0x4) == 0) {
+ mAccessibilityDetectionData = defaultAccessibilityDetectionData();
+ }
VisualQueryDetectedResult o = new VisualQueryDetectedResult(
mPartialQuery,
- mSpeakerId);
+ mSpeakerId,
+ mAccessibilityDetectionData);
return o;
}
private void checkNotUsed() {
- if ((mBuilderFieldsSet & 0x4) != 0) {
+ if ((mBuilderFieldsSet & 0x8) != 0) {
throw new IllegalStateException(
"This Builder should not be reused. Use a new Builder instance instead");
}
@@ -277,10 +332,10 @@
}
@DataClass.Generated(
- time = 1704949386772L,
+ time = 1707429290528L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/service/voice/VisualQueryDetectedResult.java",
- inputSignatures = "private final @android.annotation.NonNull java.lang.String mPartialQuery\nprivate final int mSpeakerId\nprivate static java.lang.String defaultPartialQuery()\nprivate static int defaultSpeakerId()\npublic static int getMaxSpeakerId()\nprivate void onConstructed()\npublic android.service.voice.VisualQueryDetectedResult.Builder buildUpon()\nclass VisualQueryDetectedResult extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genParcelable=true, genToString=true)")
+ inputSignatures = "private final @android.annotation.NonNull java.lang.String mPartialQuery\nprivate final int mSpeakerId\nprivate final @android.annotation.Nullable byte[] mAccessibilityDetectionData\nprivate static java.lang.String defaultPartialQuery()\nprivate static int defaultSpeakerId()\npublic static int getMaxSpeakerId()\nprivate static byte[] defaultAccessibilityDetectionData()\nprivate void onConstructed()\npublic android.service.voice.VisualQueryDetectedResult.Builder buildUpon()\nclass VisualQueryDetectedResult extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=true, genEqualsHashCode=true, genHiddenConstDefs=true, genParcelable=true, genToString=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/service/voice/VisualQueryDetector.java b/core/java/android/service/voice/VisualQueryDetector.java
index 1eb4d67..bf8de06 100644
--- a/core/java/android/service/voice/VisualQueryDetector.java
+++ b/core/java/android/service/voice/VisualQueryDetector.java
@@ -39,6 +39,7 @@
import android.util.Slog;
import com.android.internal.app.IHotwordRecognitionStatusCallback;
+import com.android.internal.app.IVoiceInteractionAccessibilitySettingsListener;
import com.android.internal.app.IVoiceInteractionManagerService;
import com.android.internal.infra.AndroidFuture;
@@ -61,6 +62,8 @@
public class VisualQueryDetector {
private static final String TAG = VisualQueryDetector.class.getSimpleName();
private static final boolean DEBUG = false;
+ private static final int SETTINGS_DISABLE_BIT = 0;
+ private static final int SETTINGS_ENABLE_BIT = 1;
private final Callback mCallback;
private final Executor mExecutor;
@@ -68,6 +71,8 @@
private final IVoiceInteractionManagerService mManagerService;
private final VisualQueryDetectorInitializationDelegate mInitializationDelegate;
private final String mAttributionTag;
+ // Used to manage the internal mapping of exposed listener API and internal aidl impl
+ private AccessibilityDetectionEnabledListenerWrapper mActiveAccessibilityListenerWrapper = null;
VisualQueryDetector(
IVoiceInteractionManagerService managerService,
@@ -174,6 +179,108 @@
}
}
+ /**
+ * Gets the binary value that controls the egress of accessibility data from
+ * {@link VisualQueryDetectedResult#setAccessibilityDetectionData(byte[])} is enabled.
+ *
+ * @return boolean value denoting if the setting is on. Default is {@code false}.
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_ALLOW_COMPLEX_RESULTS_EGRESS_FROM_VQDS)
+ public boolean isAccessibilityDetectionEnabled() {
+ Slog.d(TAG, "Fetching accessibility setting");
+ synchronized (mInitializationDelegate.getLock()) {
+ try {
+ return mManagerService.getAccessibilityDetectionEnabled();
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Sets a listener subscribing to the value of the system setting that controls the egress of
+ * accessibility data from
+ * {@link VisualQueryDetectedResult#setAccessibilityDetectionData(byte[])} is enabled.
+ *
+ * Only one listener can be set at a time. The listener set must be unset with
+ * {@link clearAccessibilityDetectionEnabledListener(Consumer<Boolean>)}
+ * in order to set a new listener. Otherwise, this method will throw a
+ * {@link IllegalStateException}.
+ *
+ * @param listener Listener of type {@code Consumer<Boolean>} to subscribe to the value update.
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_ALLOW_COMPLEX_RESULTS_EGRESS_FROM_VQDS)
+ public void setAccessibilityDetectionEnabledListener(@NonNull Consumer<Boolean> listener) {
+ Slog.d(TAG, "Registering Accessibility settings listener.");
+ synchronized (mInitializationDelegate.getLock()) {
+ try {
+ if (mActiveAccessibilityListenerWrapper != null) {
+ Slog.e(TAG, "Fail to register accessibility setting listener: "
+ + "already registered and not unregistered.");
+ throw new IllegalStateException(
+ "Cannot register listener with listeners already set.");
+ }
+ mActiveAccessibilityListenerWrapper =
+ new AccessibilityDetectionEnabledListenerWrapper(listener);
+ mManagerService.registerAccessibilityDetectionSettingsListener(
+ mActiveAccessibilityListenerWrapper);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Clear the listener that has been set with
+ * {@link setAccessibilityDetectionEnabledListener(Consumer<Boolean>)} such that when the value
+ * of the setting that controls the egress of accessibility data is changed the listener gets
+ * notified.
+ *
+ * If there is not listener that has been registered, the call to this method will lead to a
+ * {@link IllegalStateException}.
+ *
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_ALLOW_COMPLEX_RESULTS_EGRESS_FROM_VQDS)
+ public void clearAccessibilityDetectionEnabledListener() {
+ Slog.d(TAG, "Unregistering Accessibility settings listener.");
+ synchronized (mInitializationDelegate.getLock()) {
+ try {
+ if (mActiveAccessibilityListenerWrapper == null) {
+ Slog.e(TAG, "Not able to remove the listener: listener does not exist.");
+ throw new IllegalStateException("Cannot clear listener since it is not set.");
+ }
+ mManagerService.unregisterAccessibilityDetectionSettingsListener(
+ mActiveAccessibilityListenerWrapper);
+ mActiveAccessibilityListenerWrapper = null;
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+
+ private final class AccessibilityDetectionEnabledListenerWrapper
+ extends IVoiceInteractionAccessibilitySettingsListener.Stub {
+
+ private Consumer<Boolean> mListener;
+
+ AccessibilityDetectionEnabledListenerWrapper(Consumer<Boolean> listener) {
+ mListener = listener;
+ }
+
+ @Override
+ public void onAccessibilityDetectionChanged(boolean enabled) {
+ mListener.accept(enabled);
+ }
+ }
+
/** @hide */
public void dump(String prefix, PrintWriter pw) {
synchronized (mInitializationDelegate.getLock()) {
diff --git a/core/java/android/service/wallpaper/Android.bp b/core/java/android/service/wallpaper/Android.bp
index a527d3d..552a07d 100644
--- a/core/java/android/service/wallpaper/Android.bp
+++ b/core/java/android/service/wallpaper/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_system_ui_please_use_a_more_specific_subteam_if_possible_",
// 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"
diff --git a/core/java/android/text/MeasuredParagraph.java b/core/java/android/text/MeasuredParagraph.java
index 95d1974..2b6684e 100644
--- a/core/java/android/text/MeasuredParagraph.java
+++ b/core/java/android/text/MeasuredParagraph.java
@@ -237,10 +237,13 @@
// Easy case: If the line instance only contains single directionality run, no need
// to reorder visually.
if (bidi.getRunCount() == 1) {
- if ((bidi.getParaLevel() & 0x01) == 1) {
+ if (bidi.getRunLevel(0) == 1) {
return Layout.DIRS_ALL_RIGHT_TO_LEFT;
- } else {
+ } else if (bidi.getRunLevel(0) == 0) {
return Layout.DIRS_ALL_LEFT_TO_RIGHT;
+ } else {
+ return new Directions(new int[] {
+ 0, bidi.getRunLevel(0) << Layout.RUN_LEVEL_SHIFT | (end - start)});
}
}
diff --git a/core/java/android/util/MemoryIntArray.java b/core/java/android/util/MemoryIntArray.java
index 5cbbbef..2226881 100644
--- a/core/java/android/util/MemoryIntArray.java
+++ b/core/java/android/util/MemoryIntArray.java
@@ -58,6 +58,7 @@
private final boolean mIsOwner;
private final long mMemoryAddr;
+ private final int mSize;
private int mFd = -1;
/**
@@ -75,6 +76,9 @@
final String name = UUID.randomUUID().toString();
mFd = nativeCreate(name, size);
mMemoryAddr = nativeOpen(mFd, mIsOwner);
+ // Note that we use the effective size after allocation, rather than the provided size,
+ // preserving compat with the original behavior. In practice these should be equivalent.
+ mSize = nativeSize(mFd);
mCloseGuard.open("MemoryIntArray.close");
}
@@ -86,6 +90,7 @@
}
mFd = pfd.detachFd();
mMemoryAddr = nativeOpen(mFd, mIsOwner);
+ mSize = nativeSize(mFd);
mCloseGuard.open("MemoryIntArray.close");
}
@@ -127,13 +132,11 @@
}
/**
- * Gets the array size.
- *
- * @throws IOException If an error occurs while accessing the shared memory.
+ * @return Gets the array size.
*/
- public int size() throws IOException {
+ public int size() {
enforceNotClosed();
- return nativeSize(mFd);
+ return mSize;
}
/**
@@ -210,11 +213,10 @@
}
}
- private void enforceValidIndex(int index) throws IOException {
- final int size = size();
- if (index < 0 || index > size - 1) {
+ private void enforceValidIndex(int index) {
+ if (index < 0 || index > mSize - 1) {
throw new IndexOutOfBoundsException(
- index + " not between 0 and " + (size - 1));
+ index + " not between 0 and " + (mSize - 1));
}
}
diff --git a/core/java/android/view/KeyCharacterMap.java b/core/java/android/view/KeyCharacterMap.java
index 4fe53c2..a8d4e2d 100644
--- a/core/java/android/view/KeyCharacterMap.java
+++ b/core/java/android/view/KeyCharacterMap.java
@@ -579,6 +579,17 @@
}
/**
+ * Get the combining character that corresponds with the provided accent.
+ *
+ * @param accent The accent character. eg. '`'
+ * @return The combining character
+ * @hide
+ */
+ public static int getCombiningChar(int accent) {
+ return sAccentToCombining.get(accent);
+ }
+
+ /**
* Describes the character mappings associated with a key.
*
* @deprecated instead use {@link KeyCharacterMap#getDisplayLabel(int)},
diff --git a/core/java/android/view/PointerIcon.java b/core/java/android/view/PointerIcon.java
index 715f1be..13b9c45 100644
--- a/core/java/android/view/PointerIcon.java
+++ b/core/java/android/view/PointerIcon.java
@@ -430,6 +430,11 @@
VectorDrawable vectorDrawable) {
Bitmap bitmap = Bitmap.createBitmap(vectorDrawable.getIntrinsicWidth(),
vectorDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
+ // BitmapDrawables and Bitmap have a default density of DisplayMetrics.DENSITY_DEVICE,
+ // (which is deprecated in favor of DENSITY_DEVICE_STABLE/resources.densityDpi). In
+ // rare cases when device density differs from the resource density, the bitmap will
+ // scale as the BitmapDrawable is created. Avoid by explicitly setting density here.
+ bitmap.setDensity(resources.getDisplayMetrics().densityDpi);
Canvas canvas = new Canvas(bitmap);
vectorDrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
vectorDrawable.draw(canvas);
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 3c0ac06..eff35c0c0 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -224,6 +224,8 @@
@DataSpace.NamedDataSpace int dataSpace);
private static native void nativeSetExtendedRangeBrightness(long transactionObj,
long nativeObject, float currentBufferRatio, float desiredRatio);
+ private static native void nativeSetDesiredHdrHeadroom(long transactionObj,
+ long nativeObject, float desiredRatio);
private static native void nativeSetCachingHint(long transactionObj,
long nativeObject, int cachingHint);
@@ -4148,6 +4150,50 @@
}
/**
+ * Sets the desired hdr headroom for the layer.
+ *
+ * <p>Prefer using this API over {@link #setExtendedRangeBrightness} for formats that
+ *. conform to HDR video standards like HLG or HDR10 which do not communicate a HDR/SDR
+ * ratio as part of generating the buffer.
+ *
+ * @param sc The layer whose desired hdr headroom is being specified
+ *
+ * @param desiredRatio The desired hdr/sdr ratio. This can be used to communicate the max
+ * desired brightness range. This is similar to the "max luminance"
+ * value in other HDR metadata formats, but represented as a ratio of
+ * the target SDR whitepoint to the max display brightness. The system
+ * may not be able to, or may choose not to, deliver the
+ * requested range.
+ *
+ * <p>Default value is 0.0f and indicates that the system will choose
+ * the best headroom for this surface control's content. Typically,
+ * this means that HLG/PQ encoded content will be displayed with some
+ * HDR headroom greater than 1.0.
+ *
+ * <p>When called after {@link #setExtendedRangeBrightness}, the
+ * desiredHeadroom will override the desiredRatio provided by
+ * {@link #setExtendedRangeBrightness}. Conversely, when called
+ * before {@link #setExtendedRangeBrightness}, the desiredRatio provided
+ * by {@link #setExtendedRangeBrightness} will override the
+ * desiredHeadroom.
+ *
+ * <p>Must be finite && >= 1.0f or 0.0f.
+ * @return this
+ * @see #setExtendedRangeBrightness
+ **/
+ @FlaggedApi(com.android.graphics.hwui.flags.Flags.FLAG_LIMITED_HDR)
+ public @NonNull Transaction setDesiredHdrHeadroom(@NonNull SurfaceControl sc,
+ @FloatRange(from = 0.0f) float desiredRatio) {
+ checkPreconditions(sc);
+ if (!Float.isFinite(desiredRatio) || (desiredRatio != 0 && desiredRatio < 1.0f)) {
+ throw new IllegalArgumentException(
+ "desiredRatio must be finite && >= 1.0f or 0; got " + desiredRatio);
+ }
+ nativeSetDesiredHdrHeadroom(mNativeObject, sc.mNativeObject, desiredRatio);
+ return this;
+ }
+
+ /**
* Sets the caching hint for the layer. By default, the caching hint is
* {@link CACHING_ENABLED}.
*
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 9caf7a6..d494e28 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -21,6 +21,8 @@
import static android.view.WindowManagerPolicyConstants.APPLICATION_MEDIA_SUBLAYER;
import static android.view.WindowManagerPolicyConstants.APPLICATION_PANEL_SUBLAYER;
+import android.annotation.FlaggedApi;
+import android.annotation.FloatRange;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -187,6 +189,7 @@
final Rect mScreenRect = new Rect();
private final SurfaceSession mSurfaceSession = new SurfaceSession();
+ private final boolean mLimitedHdrEnabled = Flags.limitedHdr();
SurfaceControl mSurfaceControl;
SurfaceControl mBackgroundControl;
@@ -197,6 +200,9 @@
@SurfaceLifecycleStrategy
private int mSurfaceLifecycleStrategy = SURFACE_LIFECYCLE_DEFAULT;
+ private float mRequestedHdrHeadroom = 0.f;
+ private float mHdrHeadroom = 0.f;
+
/**
* We use this lock to protect access to mSurfaceControl. Both are accessed on the UI
* thread and the render thread via RenderNode.PositionUpdateListener#positionLost.
@@ -821,6 +827,45 @@
updateSurface();
}
+
+ /**
+ * Sets the desired amount of HDR headroom to be used when HDR content is presented on this
+ * SurfaceView.
+ *
+ * <p>By default the system will choose an amount of HDR headroom that is appropriate
+ * for the underlying device capabilities & bit-depth of the panel. However, for some types
+ * of content this can end up being more headroom than necessary or desired. An example
+ * would be a messaging app or gallery thumbnail view where some amount of HDR pop is desired
+ * without overly influencing the perceived brightness of the majority SDR content. This can
+ * also be used to animate in/out of an HDR range for smoother transitions.</p>
+ *
+ * <p>Note: The actual amount of HDR headroom that will be given is subject to a variety
+ * of factors such as ambient conditions, display capabilities, or bit-depth limitations.
+ * See {@link Display#getHdrSdrRatio()} for more information as well as how to query the
+ * current value.</p>
+ *
+ * @param desiredHeadroom The amount of HDR headroom that is desired. Must be >= 1.0 (no HDR)
+ * and <= 10,000.0. Passing 0.0 will reset to the default, automatically
+ * chosen value.
+ * @see Display#getHdrSdrRatio()
+ */
+ @FlaggedApi(com.android.graphics.hwui.flags.Flags.FLAG_LIMITED_HDR)
+ public void setDesiredHdrHeadroom(
+ @FloatRange(from = 0.0f, to = 10000.0) float desiredHeadroom) {
+ if (!Float.isFinite(desiredHeadroom)) {
+ throw new IllegalArgumentException("desiredHeadroom must be finite: "
+ + desiredHeadroom);
+ }
+ if (desiredHeadroom != 0 && (desiredHeadroom < 1.0f || desiredHeadroom > 10000.0f)) {
+ throw new IllegalArgumentException(
+ "desiredHeadroom must be 0.0 or in the range [1.0, 10000.0f], received: "
+ + desiredHeadroom);
+ }
+ mRequestedHdrHeadroom = desiredHeadroom;
+ updateSurface();
+ invalidate();
+ }
+
private void updateOpaqueFlag() {
if (!PixelFormat.formatHasAlpha(mRequestedFormat)) {
mSurfaceFlags |= SurfaceControl.OPAQUE;
@@ -941,6 +986,10 @@
updateBackgroundVisibility(surfaceUpdateTransaction);
updateBackgroundColor(surfaceUpdateTransaction);
+ if (mLimitedHdrEnabled) {
+ surfaceUpdateTransaction.setDesiredHdrHeadroom(
+ mBlastSurfaceControl, mHdrHeadroom);
+ }
if (isAboveParent()) {
float alpha = getAlpha();
surfaceUpdateTransaction.setAlpha(mSurfaceControl, alpha);
@@ -1085,11 +1134,12 @@
final boolean relativeZChanged = mSubLayer != mRequestedSubLayer;
final boolean surfaceLifecycleStrategyChanged =
mSurfaceLifecycleStrategy != mRequestedSurfaceLifecycleStrategy;
+ final boolean hdrHeadroomChanged = mHdrHeadroom != mRequestedHdrHeadroom;
if (creating || formatChanged || sizeChanged || visibleChanged
|| alphaChanged || windowVisibleChanged || positionChanged
|| layoutSizeChanged || hintChanged || relativeZChanged || !mAttachedToWindow
- || surfaceLifecycleStrategyChanged) {
+ || surfaceLifecycleStrategyChanged || hdrHeadroomChanged) {
if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+ "Changes: creating=" + creating
@@ -1117,6 +1167,7 @@
final int previousSurfaceLifecycleStrategy = mSurfaceLifecycleStrategy;
mSurfaceLifecycleStrategy = mRequestedSurfaceLifecycleStrategy;
+ mHdrHeadroom = mRequestedHdrHeadroom;
mScreenRect.left = mWindowSpaceLeft;
mScreenRect.top = mWindowSpaceTop;
diff --git a/core/java/android/view/animation/AnimationUtils.java b/core/java/android/view/animation/AnimationUtils.java
index 57a3b76..8a407c3 100644
--- a/core/java/android/view/animation/AnimationUtils.java
+++ b/core/java/android/view/animation/AnimationUtils.java
@@ -185,7 +185,7 @@
@FlaggedApi(FLAG_EXPECTED_PRESENTATION_TIME_READ_ONLY)
public static long getExpectedPresentationTimeNanos() {
if (!sExpectedPresentationTimeFlagValue) {
- return SystemClock.uptimeMillis();
+ return SystemClock.uptimeMillis() * TimeUtils.NANOS_PER_MS;
}
AnimationState state = sAnimationState.get();
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index bd9f504..83683ca 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -2364,6 +2364,7 @@
synchronized (mLock) {
if (!isActiveLocked()) {
+ Log.w(TAG, "onAuthenticationResult(): sessionId=" + mSessionId + " not active");
return;
}
mState = STATE_ACTIVE;
@@ -2380,6 +2381,7 @@
}
if (data == null) {
// data is set to null when result is not RESULT_OK
+ Log.i(TAG, "onAuthenticationResult(): empty intent");
return;
}
diff --git a/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java b/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
index dc5e0e5..88ca2a4 100644
--- a/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
+++ b/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
@@ -66,6 +66,7 @@
@Nullable
private static volatile IImeTracker sTrackerServiceCache = null;
+ private static int sCurStartInputSeq = 0;
/**
* @return {@code true} if {@link IInputMethodManager} is available.
@@ -327,6 +328,7 @@
}
}
+ // TODO(b/293640003): Remove method once Flags.useZeroJankProxy() is enabled.
@AnyThread
@NonNull
@RequiresPermission(value = Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true)
@@ -353,6 +355,41 @@
}
}
+ /**
+ * Returns a sequence number for startInput.
+ */
+ @AnyThread
+ @NonNull
+ @RequiresPermission(value = Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true)
+ static int startInputOrWindowGainedFocusAsync(@StartInputReason int startInputReason,
+ @NonNull IInputMethodClient client, @Nullable IBinder windowToken,
+ @StartInputFlags int startInputFlags,
+ @WindowManager.LayoutParams.SoftInputModeFlags int softInputMode,
+ @WindowManager.LayoutParams.Flags int windowFlags, @Nullable EditorInfo editorInfo,
+ @Nullable IRemoteInputConnection remoteInputConnection,
+ @Nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
+ int unverifiedTargetSdkVersion, @UserIdInt int userId,
+ @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
+ final IInputMethodManager service = getService();
+ if (service == null) {
+ return -1;
+ }
+ try {
+ service.startInputOrWindowGainedFocusAsync(startInputReason, client, windowToken,
+ startInputFlags, softInputMode, windowFlags, editorInfo, remoteInputConnection,
+ remoteAccessibilityInputConnection, unverifiedTargetSdkVersion, userId,
+ imeDispatcher, advanceAngGetStartInputSequenceNumber());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ return sCurStartInputSeq;
+ }
+
+ private static int advanceAngGetStartInputSequenceNumber() {
+ return ++sCurStartInputSeq;
+ }
+
+
@AnyThread
static void showInputMethodPickerFromClient(@NonNull IInputMethodClient client,
int auxiliarySubtypeMode) {
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index f4b09df..72125ba 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -321,6 +321,22 @@
};
/**
+ * A runnable that reports {@link InputConnection} opened event for calls to
+ * {@link IInputMethodManagerGlobalInvoker#startInputOrWindowGainedFocusAsync}.
+ */
+ private abstract static class ReportInputConnectionOpenedRunner implements Runnable {
+ /**
+ * Sequence number to track startInput requests to
+ * {@link IInputMethodManagerGlobalInvoker#startInputOrWindowGainedFocusAsync}
+ */
+ int mSequenceNum;
+ ReportInputConnectionOpenedRunner(int sequenceNum) {
+ this.mSequenceNum = sequenceNum;
+ }
+ }
+ private ReportInputConnectionOpenedRunner mReportInputConnectionOpenedRunner;
+
+ /**
* Ensures that {@link #sInstance} becomes non-{@code null} for application that have directly
* or indirectly relied on {@link #sInstance} via reflection or something like that.
*
@@ -691,6 +707,7 @@
private static final int MSG_UNBIND_ACCESSIBILITY_SERVICE = 12;
private static final int MSG_SET_INTERACTIVE = 13;
private static final int MSG_ON_SHOW_REQUESTED = 31;
+ private static final int MSG_START_INPUT_RESULT = 40;
/**
* Calling this will invalidate Local stylus handwriting availability Cache which
@@ -1045,7 +1062,7 @@
return;
}
case MSG_BIND: {
- final InputBindResult res = (InputBindResult)msg.obj;
+ final InputBindResult res = (InputBindResult) msg.obj;
if (DEBUG) {
Log.i(TAG, "handleMessage: MSG_BIND " + res.sequence + "," + res.id);
}
@@ -1071,6 +1088,60 @@
startInputInner(StartInputReason.BOUND_TO_IMMS, null, 0, 0, 0);
return;
}
+
+ case MSG_START_INPUT_RESULT: {
+ final InputBindResult res = (InputBindResult) msg.obj;
+ final int startInputSeq = msg.arg1;
+ if (res == null) {
+ // IMMS logs .wtf already.
+ return;
+ }
+ if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res);
+ synchronized (mH) {
+ if (res.id != null) {
+ updateInputChannelLocked(res.channel);
+ mCurMethod = res.method; // for @UnsupportedAppUsage
+ mCurBindState = new BindState(res);
+ mAccessibilityInputMethodSession.clear();
+ if (res.accessibilitySessions != null) {
+ for (int i = 0; i < res.accessibilitySessions.size(); i++) {
+ IAccessibilityInputMethodSessionInvoker wrapper =
+ IAccessibilityInputMethodSessionInvoker.createOrNull(
+ res.accessibilitySessions.valueAt(i));
+ if (wrapper != null) {
+ mAccessibilityInputMethodSession.append(
+ res.accessibilitySessions.keyAt(i), wrapper);
+ }
+ }
+ }
+ mCurId = res.id; // for @UnsupportedAppUsage
+ } else if (res.channel != null && res.channel != mCurChannel) {
+ res.channel.dispose();
+ }
+ switch (res.result) {
+ case InputBindResult.ResultCode.ERROR_NOT_IME_TARGET_WINDOW:
+ mRestartOnNextWindowFocus = true;
+ mServedView = null;
+ break;
+ }
+ if (mCompletions != null) {
+ if (isImeSessionAvailableLocked()) {
+ mCurBindState.mImeSession.displayCompletions(mCompletions);
+ }
+ }
+
+ if (res != null
+ && res.method != null
+ && mServedView != null
+ && mReportInputConnectionOpenedRunner != null
+ && mReportInputConnectionOpenedRunner.mSequenceNum
+ == startInputSeq) {
+ mReportInputConnectionOpenedRunner.run();
+ }
+ mReportInputConnectionOpenedRunner = null;
+ }
+ return;
+ }
case MSG_UNBIND: {
final int sequence = msg.arg1;
@UnbindReason
@@ -1322,6 +1393,12 @@
}
@Override
+ public void onStartInputResult(InputBindResult res, int startInputSeq) {
+ mH.obtainMessage(MSG_START_INPUT_RESULT, startInputSeq, -1 /* unused */, res)
+ .sendToTarget();
+ }
+
+ @Override
public void onBindAccessibilityService(InputBindResult res, int id) {
mH.obtainMessage(MSG_BIND_ACCESSIBILITY_SERVICE, id, 0, res).sendToTarget();
}
@@ -2010,6 +2087,7 @@
mServedConnecting = false;
clearConnectionLocked();
}
+ mReportInputConnectionOpenedRunner = null;
// Clear the back callbacks held by the ime dispatcher to avoid memory leaks.
mImeDispatcher.clear();
}
@@ -3080,14 +3158,52 @@
final int targetUserId = editorInfo.targetInputMethodUser != null
? editorInfo.targetInputMethodUser.getIdentifier() : UserHandle.myUserId();
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMM.startInputOrWindowGainedFocus");
- res = IInputMethodManagerGlobalInvoker.startInputOrWindowGainedFocus(
- startInputReason, mClient, windowGainingFocus, startInputFlags,
- softInputMode, windowFlags, editorInfo, servedInputConnection,
- servedInputConnection == null ? null
- : servedInputConnection.asIRemoteAccessibilityInputConnection(),
- view.getContext().getApplicationInfo().targetSdkVersion, targetUserId,
- mImeDispatcher);
+
+ int startInputSeq = -1;
+ if (Flags.useZeroJankProxy()) {
+ // async result delivered via MSG_START_INPUT_RESULT.
+ startInputSeq = IInputMethodManagerGlobalInvoker.startInputOrWindowGainedFocusAsync(
+ startInputReason, mClient, windowGainingFocus, startInputFlags,
+ softInputMode, windowFlags, editorInfo, servedInputConnection,
+ servedInputConnection == null ? null
+ : servedInputConnection.asIRemoteAccessibilityInputConnection(),
+ view.getContext().getApplicationInfo().targetSdkVersion, targetUserId,
+ mImeDispatcher);
+ } else {
+ res = IInputMethodManagerGlobalInvoker.startInputOrWindowGainedFocus(
+ startInputReason, mClient, windowGainingFocus, startInputFlags,
+ softInputMode, windowFlags, editorInfo, servedInputConnection,
+ servedInputConnection == null ? null
+ : servedInputConnection.asIRemoteAccessibilityInputConnection(),
+ view.getContext().getApplicationInfo().targetSdkVersion, targetUserId,
+ mImeDispatcher);
+ }
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+ if (Flags.useZeroJankProxy()) {
+ // Create a runnable for delayed notification to the app that the InputConnection is
+ // initialized and ready for use.
+ if (ic != null) {
+ final int seqId = startInputSeq;
+ mReportInputConnectionOpenedRunner =
+ new ReportInputConnectionOpenedRunner(startInputSeq) {
+ @Override
+ public void run() {
+ if (DEBUG) {
+ Log.v(TAG, "Calling View.onInputConnectionOpened: view= "
+ + view
+ + ", ic=" + ic + ", editorInfo=" + editorInfo
+ + ", handler="
+ + icHandler + ", startInputSeq=" + seqId);
+ }
+ reportInputConnectionOpened(ic, editorInfo, icHandler, view);
+ }
+ };
+ } else {
+ mReportInputConnectionOpenedRunner = null;
+ }
+ return true;
+ }
+
if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res);
if (res == null) {
Log.wtf(TAG, "startInputOrWindowGainedFocus must not return"
@@ -3118,6 +3234,7 @@
} else if (res.channel != null && res.channel != mCurChannel) {
res.channel.dispose();
}
+
switch (res.result) {
case InputBindResult.ResultCode.ERROR_NOT_IME_TARGET_WINDOW:
mRestartOnNextWindowFocus = true;
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 6e1131f..13dc4ef 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -7573,6 +7573,8 @@
@FlaggedApi(FLAG_DRAW_DATA_PARCEL)
public static final class DrawInstructions {
+ private static final long VERSION = 1L;
+
@NonNull
final List<byte[]> mInstructions;
@@ -7607,6 +7609,7 @@
}
return new DrawInstructions(instructions);
}
+
private static void writeToParcel(@Nullable final DrawInstructions drawInstructions,
@NonNull final Parcel dest, final int flags) {
if (drawInstructions == null) {
@@ -7622,6 +7625,14 @@
}
/**
+ * Version number of {@link DrawInstructions} currently supported.
+ */
+ @FlaggedApi(FLAG_DRAW_DATA_PARCEL)
+ public static long getSupportedVersion() {
+ return VERSION;
+ }
+
+ /**
* Builder class for {@link DrawInstructions} objects.
*/
@FlaggedApi(FLAG_DRAW_DATA_PARCEL)
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 9847cb1..1721462 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -5513,6 +5513,7 @@
*
* @attr ref android.R.styleable#TextView_fontVariationSettings
*/
+ @android.view.RemotableViewMethod
public boolean setFontVariationSettings(@Nullable String fontVariationSettings) {
final String existingSettings = mTextPaint.getFontVariationSettings();
if (fontVariationSettings == existingSettings
diff --git a/core/java/com/android/internal/app/IVoiceInteractionAccessibilitySettingsListener.aidl b/core/java/com/android/internal/app/IVoiceInteractionAccessibilitySettingsListener.aidl
new file mode 100644
index 0000000..a919035
--- /dev/null
+++ b/core/java/com/android/internal/app/IVoiceInteractionAccessibilitySettingsListener.aidl
@@ -0,0 +1,24 @@
+/*
+ * 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.app;
+
+oneway interface IVoiceInteractionAccessibilitySettingsListener {
+ /**
+ * Called when the value of secure setting has changed.
+ */
+ void onAccessibilityDetectionChanged(boolean enable);
+}
diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
index 314ed69..98d39397 100644
--- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
@@ -35,6 +35,7 @@
import com.android.internal.app.IHotwordRecognitionStatusCallback;
import com.android.internal.app.IVoiceActionCheckCallback;
+import com.android.internal.app.IVoiceInteractionAccessibilitySettingsListener;
import com.android.internal.app.IVoiceInteractionSessionListener;
import com.android.internal.app.IVoiceInteractionSessionShowCallback;
import com.android.internal.app.IVoiceInteractionSoundTriggerSession;
@@ -382,4 +383,21 @@
oneway void notifyActivityEventChanged(
in IBinder activityToken,
int type);
+
+ /**
+ * rely on the system server to get the secure settings
+ */
+ boolean getAccessibilityDetectionEnabled();
+
+ /**
+ * register the listener
+ */
+ oneway void registerAccessibilityDetectionSettingsListener(
+ in IVoiceInteractionAccessibilitySettingsListener listener);
+
+ /**
+ * unregister the listener
+ */
+ oneway void unregisterAccessibilityDetectionSettingsListener(
+ in IVoiceInteractionAccessibilitySettingsListener listener);
}
diff --git a/core/java/com/android/internal/app/NfcResolverActivity.java b/core/java/com/android/internal/app/NfcResolverActivity.java
index 402192a..78427fe 100644
--- a/core/java/com/android/internal/app/NfcResolverActivity.java
+++ b/core/java/com/android/internal/app/NfcResolverActivity.java
@@ -16,25 +16,25 @@
package com.android.internal.app;
-import static android.service.chooser.CustomChoosers.EXTRA_RESOLVE_INFOS;
-import static android.service.chooser.Flags.supportNfcResolver;
+import static android.nfc.Flags.enableNfcMainline;
import android.content.Intent;
import android.content.pm.ResolveInfo;
+import android.nfc.NfcAdapter;
import android.os.Bundle;
import java.util.ArrayList;
/**
* Caller-customizable variant of {@link ResolverActivity} to support the
- * {@link CustomChoosers#showNfcResolver()} API.
+ * NFC resolver intent.
*/
public class NfcResolverActivity extends ResolverActivity {
@Override
@SuppressWarnings("MissingSuperCall") // Called indirectly via `super_onCreate()`.
protected void onCreate(Bundle savedInstanceState) {
- if (!supportNfcResolver()) {
+ if (!enableNfcMainline()) {
super_onCreate(savedInstanceState);
finish();
return;
@@ -43,7 +43,8 @@
Intent intent = getIntent();
Intent target = intent.getParcelableExtra(Intent.EXTRA_INTENT, Intent.class);
ArrayList<ResolveInfo> rList =
- intent.getParcelableArrayListExtra(EXTRA_RESOLVE_INFOS, ResolveInfo.class);
+ intent.getParcelableArrayListExtra(
+ NfcAdapter.EXTRA_RESOLVE_INFOS, ResolveInfo.class);
CharSequence title = intent.getExtras().getCharSequence(
Intent.EXTRA_TITLE,
getResources().getText(com.android.internal.R.string.chooseActivity));
diff --git a/core/java/com/android/internal/colorextraction/OWNERS b/core/java/com/android/internal/colorextraction/OWNERS
index ffade1e..041559c 100644
--- a/core/java/com/android/internal/colorextraction/OWNERS
+++ b/core/java/com/android/internal/colorextraction/OWNERS
@@ -1,3 +1,2 @@
-dupin@google.com
cinek@google.com
-jamesoleary@google.com
+arteiro@google.com
diff --git a/core/java/com/android/internal/inputmethod/IInputMethodClient.aidl b/core/java/com/android/internal/inputmethod/IInputMethodClient.aidl
index 9251d2d..babd9a0 100644
--- a/core/java/com/android/internal/inputmethod/IInputMethodClient.aidl
+++ b/core/java/com/android/internal/inputmethod/IInputMethodClient.aidl
@@ -24,6 +24,7 @@
*/
oneway interface IInputMethodClient {
void onBindMethod(in InputBindResult res);
+ void onStartInputResult(in InputBindResult res, int startInputSeq);
void onBindAccessibilityService(in InputBindResult res, int id);
void onUnbindMethod(int sequence, int unbindReason);
void onUnbindAccessibilityService(int sequence, int id);
diff --git a/core/java/com/android/internal/inputmethod/InputBindResult.java b/core/java/com/android/internal/inputmethod/InputBindResult.java
index b6eca07..243b103 100644
--- a/core/java/com/android/internal/inputmethod/InputBindResult.java
+++ b/core/java/com/android/internal/inputmethod/InputBindResult.java
@@ -271,6 +271,7 @@
public String toString() {
return "InputBindResult{result=" + getResultString() + " method=" + method + " id=" + id
+ " sequence=" + sequence
+ + " result=" + result
+ " isInputMethodSuppressingSpellChecker=" + isInputMethodSuppressingSpellChecker
+ "}";
}
diff --git a/core/java/com/android/internal/jank/Cuj.java b/core/java/com/android/internal/jank/Cuj.java
index 48c455a..3662d69 100644
--- a/core/java/com/android/internal/jank/Cuj.java
+++ b/core/java/com/android/internal/jank/Cuj.java
@@ -120,7 +120,7 @@
public static final int CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY = 84;
public static final int CUJ_PREDICTIVE_BACK_CROSS_TASK = 85;
public static final int CUJ_PREDICTIVE_BACK_HOME = 86;
- public static final int CUJ_LAUNCHER_SEARCH_QSB_OPEN = 87;
+ // 87 is reserved - previously assigned to deprecated CUJ_LAUNCHER_SEARCH_QSB_OPEN.
public static final int CUJ_BACK_PANEL_ARROW = 88;
public static final int CUJ_LAUNCHER_CLOSE_ALL_APPS_BACK = 89;
public static final int CUJ_LAUNCHER_SEARCH_QSB_WEB_SEARCH = 90;
@@ -209,7 +209,6 @@
CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY,
CUJ_PREDICTIVE_BACK_CROSS_TASK,
CUJ_PREDICTIVE_BACK_HOME,
- CUJ_LAUNCHER_SEARCH_QSB_OPEN,
CUJ_BACK_PANEL_ARROW,
CUJ_LAUNCHER_CLOSE_ALL_APPS_BACK,
CUJ_LAUNCHER_SEARCH_QSB_WEB_SEARCH,
@@ -304,7 +303,6 @@
CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PREDICTIVE_BACK_CROSS_ACTIVITY;
CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_PREDICTIVE_BACK_CROSS_TASK] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PREDICTIVE_BACK_CROSS_TASK;
CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_PREDICTIVE_BACK_HOME] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PREDICTIVE_BACK_HOME;
- CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_SEARCH_QSB_OPEN] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_SEARCH_QSB_OPEN;
CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_BACK_PANEL_ARROW] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__BACK_PANEL_ARROW;
CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_CLOSE_ALL_APPS_BACK] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_CLOSE_ALL_APPS_BACK;
CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_SEARCH_QSB_WEB_SEARCH] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_SEARCH_QSB_WEB_SEARCH;
@@ -480,8 +478,6 @@
return "PREDICTIVE_BACK_CROSS_TASK";
case CUJ_PREDICTIVE_BACK_HOME:
return "PREDICTIVE_BACK_HOME";
- case CUJ_LAUNCHER_SEARCH_QSB_OPEN:
- return "LAUNCHER_SEARCH_QSB_OPEN";
case CUJ_BACK_PANEL_ARROW:
return "BACK_PANEL_ARROW";
case CUJ_LAUNCHER_CLOSE_ALL_APPS_BACK:
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index ed43b81..d463b62 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -376,4 +376,10 @@
* @param packageName of the session for which the output switcher is shown.
*/
void showMediaOutputSwitcher(String packageName);
+
+ /** Enters desktop mode.
+ *
+ * @param displayId the id of the current display.
+ */
+ void enterDesktop(int displayId);
}
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index e95127b..b90f8bf 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -69,6 +69,8 @@
boolean hideSoftInput(in IInputMethodClient client, @nullable IBinder windowToken,
in @nullable ImeTracker.Token statsToken, int flags,
in @nullable ResultReceiver resultReceiver, int reason);
+
+ // TODO(b/293640003): Remove method once Flags.useZeroJankProxy() is enabled.
// If windowToken is null, this just does startInput(). Otherwise this reports that a window
// has gained focus, and if 'editorInfo' is non-null then also does startInput.
// @NonNull
@@ -85,6 +87,21 @@
int unverifiedTargetSdkVersion, int userId,
in ImeOnBackInvokedDispatcher imeDispatcher);
+ // If windowToken is null, this just does startInput(). Otherwise this reports that a window
+ // has gained focus, and if 'editorInfo' is non-null then also does startInput.
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
+ + "android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true)")
+ void startInputOrWindowGainedFocusAsync(
+ /* @StartInputReason */ int startInputReason,
+ in IInputMethodClient client, in @nullable IBinder windowToken,
+ /* @StartInputFlags */ int startInputFlags,
+ /* @android.view.WindowManager.LayoutParams.SoftInputModeFlags */ int softInputMode,
+ /* @android.view.WindowManager.LayoutParams.Flags */ int windowFlags,
+ in @nullable EditorInfo editorInfo, in @nullable IRemoteInputConnection inputConnection,
+ in @nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
+ int unverifiedTargetSdkVersion, int userId,
+ in ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq);
+
void showInputMethodPickerFromClient(in IInputMethodClient client,
int auxiliarySubtypeMode);
@@ -156,6 +173,7 @@
in String delegatePackageName,
in String delegatorPackageName);
+ // TODO(b/293640003): introduce a new API method to provide async way to return boolean.
/** Accepts and starts a stylus handwriting session for the delegate view **/
boolean acceptStylusHandwritingDelegation(in IInputMethodClient client, in int userId,
in String delegatePackageName, in String delegatorPackageName, int flags);
diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp
index 3ee15ab..3d0ab4e 100644
--- a/core/jni/android_util_AssetManager.cpp
+++ b/core/jni/android_util_AssetManager.cpp
@@ -314,7 +314,8 @@
}
static void NativeSetApkAssets(JNIEnv* env, jclass /*clazz*/, jlong ptr,
- jobjectArray apk_assets_array, jboolean invalidate_caches) {
+ jobjectArray apk_assets_array, jboolean invalidate_caches,
+ jboolean preset) {
ATRACE_NAME("AssetManager::SetApkAssets");
const jsize apk_assets_len = env->GetArrayLength(apk_assets_array);
@@ -343,7 +344,11 @@
}
auto assetmanager = LockAndStartAssetManager(ptr);
- assetmanager->SetApkAssets(apk_assets, invalidate_caches);
+ if (preset) {
+ assetmanager->PresetApkAssets(apk_assets);
+ } else {
+ assetmanager->SetApkAssets(apk_assets, invalidate_caches);
+ }
}
static void NativeSetConfiguration(JNIEnv* env, jclass /*clazz*/, jlong ptr, jint mcc, jint mnc,
@@ -353,7 +358,7 @@
jint screen_height, jint smallest_screen_width_dp,
jint screen_width_dp, jint screen_height_dp, jint screen_layout,
jint ui_mode, jint color_mode, jint grammatical_gender,
- jint major_version) {
+ jint major_version, jboolean force_refresh) {
ATRACE_NAME("AssetManager::SetConfiguration");
const jsize locale_count = (locales == NULL) ? 0 : env->GetArrayLength(locales);
@@ -413,7 +418,7 @@
}
auto assetmanager = LockAndStartAssetManager(ptr);
- assetmanager->SetConfigurations(configs);
+ assetmanager->SetConfigurations(std::move(configs), force_refresh != JNI_FALSE);
assetmanager->SetDefaultLocale(default_locale_int);
}
@@ -1522,8 +1527,8 @@
// AssetManager setup methods.
{"nativeCreate", "()J", (void*)NativeCreate},
{"nativeDestroy", "(J)V", (void*)NativeDestroy},
- {"nativeSetApkAssets", "(J[Landroid/content/res/ApkAssets;Z)V", (void*)NativeSetApkAssets},
- {"nativeSetConfiguration", "(JIILjava/lang/String;[Ljava/lang/String;IIIIIIIIIIIIIIII)V",
+ {"nativeSetApkAssets", "(J[Landroid/content/res/ApkAssets;ZZ)V", (void*)NativeSetApkAssets},
+ {"nativeSetConfiguration", "(JIILjava/lang/String;[Ljava/lang/String;IIIIIIIIIIIIIIIIZ)V",
(void*)NativeSetConfiguration},
{"nativeGetAssignedPackageIdentifiers", "(JZZ)Landroid/util/SparseArray;",
(void*)NativeGetAssignedPackageIdentifiers},
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 98f409a..6fec527a 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -716,6 +716,13 @@
transaction->setExtendedRangeBrightness(ctrl, currentBufferRatio, desiredRatio);
}
+static void nativeSetDesiredHdrHeadroom(JNIEnv* env, jclass clazz, jlong transactionObj,
+ jlong nativeObject, float desiredRatio) {
+ auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
+ SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl*>(nativeObject);
+ transaction->setDesiredHdrHeadroom(ctrl, desiredRatio);
+}
+
static void nativeSetCachingHint(JNIEnv* env, jclass clazz, jlong transactionObj,
jlong nativeObject, jint cachingHint) {
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
@@ -2340,7 +2347,9 @@
(void*)nativeSetDataSpace },
{"nativeSetExtendedRangeBrightness", "(JJFF)V",
(void*)nativeSetExtendedRangeBrightness },
- {"nativeSetCachingHint", "(JJI)V",
+ {"nativeSetDesiredHdrHeadroom", "(JJF)V",
+ (void*)nativeSetDesiredHdrHeadroom },
+ {"nativeSetCachingHint", "(JJI)V",
(void*)nativeSetCachingHint },
{"nativeAddWindowInfosReportedListener", "(JLjava/lang/Runnable;)V",
(void*)nativeAddWindowInfosReportedListener },
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 71f06f1..0b3a065 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2281,7 +2281,7 @@
<!-- @SystemApi @hide Allows changing Thread network state and access to Thread network
credentials such as Network Key and PSKc.
<p>Not for use by third-party applications.
- @FlaggedApi("com.android.net.thread.flags.thread_enabled") -->
+ @FlaggedApi("com.android.net.thread.flags.thread_enabled_platform") -->
<permission android:name="android.permission.THREAD_NETWORK_PRIVILEGED"
android:protectionLevel="signature|privileged" />
@@ -7696,7 +7696,7 @@
<!-- @SystemApi Allows the holder to launch an Intent Resolver flow with custom presentation
and/or targets.
- @FlaggedApi("android.service.chooser.support_nfc_resolver")
+ @FlaggedApi("android.nfc.enable_nfc_mainline")
@hide -->
<permission android:name="android.permission.SHOW_CUSTOMIZED_RESOLVER"
android:protectionLevel="signature|privileged" />
@@ -7937,6 +7937,33 @@
<permission android:name="android.permission.MANAGE_WEARABLE_SENSING_SERVICE"
android:protectionLevel="signature|privileged" />
+ <!-- @SystemApi Allows an app to use the on-device intelligence service.
+ <p>Protection level: signature|privileged
+ @hide
+ @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence")
+ -->
+ <permission android:name="android.permission.USE_ON_DEVICE_INTELLIGENCE"
+ android:protectionLevel="signature|privileged" />
+
+
+ <!-- @SystemApi Allows an app to bind the on-device intelligence service.
+ <p>Protection level: signature|privileged
+ @hide
+ @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence")
+ -->
+ <permission android:name="android.permission.BIND_ON_DEVICE_INTELLIGENCE_SERVICE"
+ android:protectionLevel="signature|privileged" />
+
+
+ <!-- @SystemApi Allows an app to bind the on-device trusted service.
+ <p>Protection level: signature|privileged
+ @hide
+ @FlaggedApi("android.app.ondeviceintelligence.flags.enable_on_device_intelligence")
+ -->
+ <permission android:name="android.permission.BIND_ON_DEVICE_TRUSTED_SERVICE"
+ android:protectionLevel="signature"/>
+
+
<!-- Allows applications to use the user-initiated jobs API. For more details
see {@link android.app.job.JobInfo.Builder#setUserInitiated}.
<p>Protection level: normal
@@ -8196,8 +8223,8 @@
android:multiprocess="true"
android:permission="android.permission.SHOW_CUSTOMIZED_RESOLVER"
android:exported="true">
- <intent-filter>
- <action android:name="android.service.chooser.action.SHOW_CUSTOMIZED_RESOLVER" />
+ <intent-filter android:priority="100" >
+ <action android:name="android.nfc.action.SHOW_NFC_RESOLVER" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index c6bc589..1f06b0b 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4670,6 +4670,13 @@
-->
<string name="config_defaultWearableSensingService" translatable="false"></string>
+
+ <!-- The component name for the default system on-device intelligence service, -->
+ <string name="config_defaultOnDeviceIntelligenceService" translatable="false"></string>
+
+ <!-- The component name for the default system on-device trusted inference service. -->
+ <string name="config_defaultOnDeviceTrustedInferenceService" translatable="false"></string>
+
<!-- Component name that accepts ACTION_SEND intents for requesting ambient context consent for
wearable sensing. -->
<string translatable="false" name="config_defaultWearableSensingConsentComponent"></string>
diff --git a/core/res/res/values/dimens_material.xml b/core/res/res/values/dimens_material.xml
index fa15c3f..972fe7e 100644
--- a/core/res/res/values/dimens_material.xml
+++ b/core/res/res/values/dimens_material.xml
@@ -204,11 +204,4 @@
<dimen name="progress_bar_size_small">16dip</dimen>
<dimen name="progress_bar_size_medium">48dp</dimen>
<dimen name="progress_bar_size_large">76dp</dimen>
-
- <!-- System corner radius baseline sizes. Used by Material styling of rounded corner shapes-->
- <dimen name="system_corner_radius_xsmall">4dp</dimen>
- <dimen name="system_corner_radius_small">8dp</dimen>
- <dimen name="system_corner_radius_medium">16dp</dimen>
- <dimen name="system_corner_radius_large">26dp</dimen>
- <dimen name="system_corner_radius_xlarge">36dp</dimen>
</resources>
diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml
index c797210..5987f6e 100644
--- a/core/res/res/values/public-staging.xml
+++ b/core/res/res/values/public-staging.xml
@@ -184,11 +184,11 @@
<staging-public-group type="dimen" first-id="0x01b90000">
<!-- System corner radius baseline sizes. Used by Material styling of rounded corner shapes-->
- <public name="system_corner_radius_xsmall" />
- <public name="system_corner_radius_small" />
- <public name="system_corner_radius_medium" />
- <public name="system_corner_radius_large" />
- <public name="system_corner_radius_xlarge" />
+ <public name="removed_system_corner_radius_xsmall" />
+ <public name="removed_system_corner_radius_small" />
+ <public name="removed_system_corner_radius_medium" />
+ <public name="removed_system_corner_radius_large" />
+ <public name="removed_system_corner_radius_xlarge" />
</staging-public-group>
<staging-public-group type="color" first-id="0x01b80000">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 7c290b1..cf9c02a 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3909,6 +3909,8 @@
<java-symbol type="string" name="config_ambientContextPackageNameExtraKey" />
<java-symbol type="string" name="config_ambientContextEventArrayExtraKey" />
<java-symbol type="string" name="config_defaultWearableSensingService" />
+ <java-symbol type="string" name="config_defaultOnDeviceIntelligenceService" />
+ <java-symbol type="string" name="config_defaultOnDeviceTrustedInferenceService" />
<java-symbol type="string" name="config_retailDemoPackage" />
<java-symbol type="string" name="config_retailDemoPackageSignature" />
@@ -5356,11 +5358,4 @@
<java-symbol type="drawable" name="ic_satellite_alt_24px" />
<java-symbol type="bool" name="config_watchlistUseFileHashesCache" />
-
- <!-- System corner radius baseline sizes. Used by Material styling of rounded corner shapes-->
- <java-symbol type="dimen" name="system_corner_radius_xsmall" />
- <java-symbol type="dimen" name="system_corner_radius_small" />
- <java-symbol type="dimen" name="system_corner_radius_medium" />
- <java-symbol type="dimen" name="system_corner_radius_large" />
- <java-symbol type="dimen" name="system_corner_radius_xlarge" />
</resources>
diff --git a/core/tests/coretests/src/android/accessibilityservice/BrailleDisplayControllerImplTest.java b/core/tests/coretests/src/android/accessibilityservice/BrailleDisplayControllerImplTest.java
new file mode 100644
index 0000000..aaa199d
--- /dev/null
+++ b/core/tests/coretests/src/android/accessibilityservice/BrailleDisplayControllerImplTest.java
@@ -0,0 +1,107 @@
+/*
+ * 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.accessibilityservice;
+
+import static org.junit.Assert.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
+
+import android.hardware.usb.UsbDevice;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityInteractionClient;
+import android.view.accessibility.Flags;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Tests for internal details of BrailleDisplayControllerImpl.
+ *
+ * <p>Prefer adding new tests in CTS where possible.
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+@RequiresFlagsEnabled(Flags.FLAG_BRAILLE_DISPLAY_HID)
+public class BrailleDisplayControllerImplTest {
+ private static final int TEST_SERVICE_CONNECTION_ID = 123;
+
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+
+ private BrailleDisplayController mBrailleDisplayController;
+
+ @Mock
+ private IAccessibilityServiceConnection mAccessibilityServiceConnection;
+ @Mock
+ private BrailleDisplayController.BrailleDisplayCallback mBrailleDisplayCallback;
+
+ public static class TestAccessibilityService extends AccessibilityService {
+ public void onAccessibilityEvent(AccessibilityEvent event) {
+ }
+
+ public void onInterrupt() {
+ }
+ }
+
+ @Before
+ public void test() {
+ MockitoAnnotations.initMocks(this);
+ AccessibilityService accessibilityService = spy(new TestAccessibilityService());
+ doReturn((Executor) Runnable::run).when(accessibilityService).getMainExecutor();
+ doReturn(TEST_SERVICE_CONNECTION_ID).when(accessibilityService).getConnectionId();
+ AccessibilityInteractionClient.addConnection(TEST_SERVICE_CONNECTION_ID,
+ mAccessibilityServiceConnection, /*initializeCache=*/false);
+ mBrailleDisplayController = accessibilityService.getBrailleDisplayController();
+ }
+
+ // Automated CTS tests only use the BluetoothDevice version of BrailleDisplayController#connect
+ // because fake UsbDevice objects cannot be created in CTS. This internal test can mock the
+ // UsbDevice object and at least validate that the correct system_server AIDL call is made.
+ @Test
+ public void connect_withUsbDevice_callsConnectUsbBrailleDisplay() throws Exception {
+ UsbDevice usbDevice = Mockito.mock(UsbDevice.class);
+
+ mBrailleDisplayController.connect(usbDevice, mBrailleDisplayCallback);
+
+ verify(mAccessibilityServiceConnection).connectUsbBrailleDisplay(eq(usbDevice), any());
+ }
+
+ @Test
+ public void connect_serviceNotConnected_throwsIllegalStateException() {
+ AccessibilityInteractionClient.removeConnection(TEST_SERVICE_CONNECTION_ID);
+ UsbDevice usbDevice = Mockito.mock(UsbDevice.class);
+
+ assertThrows(IllegalStateException.class,
+ () -> mBrailleDisplayController.connect(usbDevice, mBrailleDisplayCallback));
+ }
+}
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index 48ef7e6..ebf4cca 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -43,7 +43,6 @@
import android.app.PictureInPictureUiState;
import android.app.ResourcesManager;
import android.app.servertransaction.ActivityConfigurationChangeItem;
-import android.app.servertransaction.ActivityLifecycleItem;
import android.app.servertransaction.ActivityRelaunchItem;
import android.app.servertransaction.ClientTransaction;
import android.app.servertransaction.ClientTransactionItem;
@@ -75,7 +74,6 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.content.ReferrerIntent;
-import com.android.window.flags.Flags;
import org.junit.After;
import org.junit.Before;
@@ -230,7 +228,7 @@
try {
// Send process level config change.
ClientTransaction transaction = newTransaction(activityThread);
- addClientTransactionItem(transaction, ConfigurationChangeItem.obtain(
+ transaction.addTransactionItem(ConfigurationChangeItem.obtain(
newConfig, DEVICE_ID_INVALID));
appThread.scheduleTransaction(transaction);
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
@@ -247,7 +245,7 @@
newConfig.seq++;
newConfig.smallestScreenWidthDp++;
transaction = newTransaction(activityThread);
- addClientTransactionItem(transaction, ActivityConfigurationChangeItem.obtain(
+ transaction.addTransactionItem(ActivityConfigurationChangeItem.obtain(
activity.getActivityToken(), newConfig));
appThread.scheduleTransaction(transaction);
InstrumentationRegistry.getInstrumentation().waitForIdleSync();
@@ -448,16 +446,16 @@
activity.mTestLatch = new CountDownLatch(1);
ClientTransaction transaction = newTransaction(activityThread);
- addClientTransactionItem(transaction, ConfigurationChangeItem.obtain(
+ transaction.addTransactionItem(ConfigurationChangeItem.obtain(
processConfigLandscape, DEVICE_ID_INVALID));
appThread.scheduleTransaction(transaction);
transaction = newTransaction(activityThread);
- addClientTransactionItem(transaction, ActivityConfigurationChangeItem.obtain(
+ transaction.addTransactionItem(ActivityConfigurationChangeItem.obtain(
activity.getActivityToken(), activityConfigLandscape));
- addClientTransactionItem(transaction, ConfigurationChangeItem.obtain(
+ transaction.addTransactionItem(ConfigurationChangeItem.obtain(
processConfigPortrait, DEVICE_ID_INVALID));
- addClientTransactionItem(transaction, ActivityConfigurationChangeItem.obtain(
+ transaction.addTransactionItem(ActivityConfigurationChangeItem.obtain(
activity.getActivityToken(), activityConfigPortrait));
appThread.scheduleTransaction(transaction);
@@ -847,8 +845,8 @@
false /* shouldSendCompatFakeFocus*/);
final ClientTransaction transaction = newTransaction(activity);
- addClientTransactionItem(transaction, callbackItem);
- addClientTransactionItem(transaction, resumeStateRequest);
+ transaction.addTransactionItem(callbackItem);
+ transaction.addTransactionItem(resumeStateRequest);
return transaction;
}
@@ -860,7 +858,7 @@
false /* shouldSendCompatFakeFocus */);
final ClientTransaction transaction = newTransaction(activity);
- addClientTransactionItem(transaction, resumeStateRequest);
+ transaction.addTransactionItem(resumeStateRequest);
return transaction;
}
@@ -871,7 +869,7 @@
activity.getActivityToken(), 0 /* configChanges */);
final ClientTransaction transaction = newTransaction(activity);
- addClientTransactionItem(transaction, stopStateRequest);
+ transaction.addTransactionItem(stopStateRequest);
return transaction;
}
@@ -883,7 +881,7 @@
activity.getActivityToken(), config);
final ClientTransaction transaction = newTransaction(activity);
- addClientTransactionItem(transaction, item);
+ transaction.addTransactionItem(item);
return transaction;
}
@@ -895,7 +893,7 @@
resume);
final ClientTransaction transaction = newTransaction(activity);
- addClientTransactionItem(transaction, item);
+ transaction.addTransactionItem(item);
return transaction;
}
@@ -910,17 +908,6 @@
return ClientTransaction.obtain(activityThread.getApplicationThread());
}
- private static void addClientTransactionItem(@NonNull ClientTransaction transaction,
- @NonNull ClientTransactionItem item) {
- if (Flags.bundleClientTransactionFlag()) {
- transaction.addTransactionItem(item);
- } else if (item.isActivityLifecycleItem()) {
- transaction.setLifecycleStateRequest((ActivityLifecycleItem) item);
- } else {
- transaction.addCallback(item);
- }
- }
-
// Test activity
public static class TestActivity extends Activity {
static final String PIP_REQUESTED_OVERRIDE_ENTER = "pip_requested_override_enter";
diff --git a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java
index 95d5049..213fd7b 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java
@@ -16,10 +16,13 @@
package android.app.servertransaction;
+import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
+
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+import static com.android.window.flags.Flags.FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG;
+
import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import android.hardware.display.DisplayManager;
@@ -28,12 +31,14 @@
import android.os.Handler;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.view.DisplayInfo;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
@@ -49,6 +54,10 @@
@SmallTest
@Presubmit
public class ClientTransactionListenerControllerTest {
+
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
+
@Mock
private IDisplayManager mIDisplayManager;
@Mock
@@ -60,12 +69,12 @@
@Before
public void setup() {
+ mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
+
MockitoAnnotations.initMocks(this);
mDisplayManager = new DisplayManagerGlobal(mIDisplayManager);
mHandler = getInstrumentation().getContext().getMainThreadHandler();
- mController = spy(ClientTransactionListenerController.createInstanceForTesting(
- mDisplayManager));
- doReturn(true).when(mController).isBundleClientTransactionFlagEnabled();
+ mController = ClientTransactionListenerController.createInstanceForTesting(mDisplayManager);
}
@Test
diff --git a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java
index d10cf16..5272416 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java
@@ -16,16 +16,22 @@
package android.app.servertransaction;
+import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
+
+import static com.android.window.flags.Flags.FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG;
+
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.app.ClientTransactionHandler;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -43,31 +49,28 @@
@Presubmit
public class ClientTransactionTests {
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
+
@Test
public void testPreExecute() {
- final ClientTransactionItem callback1 = mock(ClientTransactionItem.class);
- final ClientTransactionItem callback2 = mock(ClientTransactionItem.class);
- final ActivityLifecycleItem stateRequest = mock(ActivityLifecycleItem.class);
- final ClientTransactionHandler clientTransactionHandler =
- mock(ClientTransactionHandler.class);
+ mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
- final ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
- transaction.addCallback(callback1);
- transaction.addCallback(callback2);
- transaction.setLifecycleStateRequest(stateRequest);
-
- transaction.preExecute(clientTransactionHandler);
-
- verify(callback1, times(1)).preExecute(clientTransactionHandler);
- verify(callback2, times(1)).preExecute(clientTransactionHandler);
- verify(stateRequest, times(1)).preExecute(clientTransactionHandler);
+ testPreExecuteInner();
}
@Test
- public void testPreExecuteTransactionItems() {
+ public void testPreExecute_bundleClientTransaction() {
+ mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
+
+ testPreExecuteInner();
+ }
+
+ private void testPreExecuteInner() {
final ClientTransactionItem callback1 = mock(ClientTransactionItem.class);
final ClientTransactionItem callback2 = mock(ClientTransactionItem.class);
final ActivityLifecycleItem stateRequest = mock(ActivityLifecycleItem.class);
+ doReturn(true).when(stateRequest).isActivityLifecycleItem();
final ClientTransactionHandler clientTransactionHandler =
mock(ClientTransactionHandler.class);
@@ -78,8 +81,8 @@
transaction.preExecute(clientTransactionHandler);
- verify(callback1, times(1)).preExecute(clientTransactionHandler);
- verify(callback2, times(1)).preExecute(clientTransactionHandler);
- verify(stateRequest, times(1)).preExecute(clientTransactionHandler);
+ verify(callback1).preExecute(clientTransactionHandler);
+ verify(callback2).preExecute(clientTransactionHandler);
+ verify(stateRequest).preExecute(clientTransactionHandler);
}
}
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
index 2315a58..adb6f2a 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
@@ -25,6 +25,9 @@
import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP;
import static android.app.servertransaction.ActivityLifecycleItem.PRE_ON_CREATE;
import static android.app.servertransaction.ActivityLifecycleItem.UNDEFINED;
+import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
+
+import static com.android.window.flags.Flags.FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
@@ -51,12 +54,14 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.util.ArrayMap;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InOrder;
@@ -83,6 +88,9 @@
@Presubmit
public class TransactionExecutorTests {
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
+
@Mock
private ClientTransactionHandler mTransactionHandler;
@Mock
@@ -240,29 +248,19 @@
@Test
public void testTransactionResolution() {
- ClientTransactionItem callback1 = mock(ClientTransactionItem.class);
- when(callback1.getPostExecutionState()).thenReturn(UNDEFINED);
- ClientTransactionItem callback2 = mock(ClientTransactionItem.class);
- when(callback2.getPostExecutionState()).thenReturn(UNDEFINED);
+ mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
- ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
- transaction.addCallback(callback1);
- transaction.addCallback(callback2);
- transaction.setLifecycleStateRequest(mActivityLifecycleItem);
-
- transaction.preExecute(mTransactionHandler);
- mExecutor.execute(transaction);
-
- InOrder inOrder = inOrder(mTransactionHandler, callback1, callback2,
- mActivityLifecycleItem);
- inOrder.verify(callback1).execute(eq(mTransactionHandler), any());
- inOrder.verify(callback2).execute(eq(mTransactionHandler), any());
- inOrder.verify(mActivityLifecycleItem).execute(eq(mTransactionHandler), eq(mClientRecord),
- any());
+ testTransactionResolutionInner();
}
@Test
- public void testExecuteTransactionItems_transactionResolution() {
+ public void testTransactionResolution_bundleClientTransaction() {
+ mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
+
+ testTransactionResolutionInner();
+ }
+
+ private void testTransactionResolutionInner() {
ClientTransactionItem callback1 = mock(ClientTransactionItem.class);
when(callback1.getPostExecutionState()).thenReturn(UNDEFINED);
ClientTransactionItem callback2 = mock(ClientTransactionItem.class);
@@ -286,38 +284,19 @@
@Test
public void testDoNotLaunchDestroyedActivity() {
- final Map<IBinder, DestroyActivityItem> activitiesToBeDestroyed = new ArrayMap<>();
- when(mTransactionHandler.getActivitiesToBeDestroyed()).thenReturn(activitiesToBeDestroyed);
- // Assume launch transaction is still in queue, so there is no client record.
- when(mTransactionHandler.getActivityClient(any())).thenReturn(null);
+ mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
- // An incoming destroy transaction enters binder thread (preExecute).
- final IBinder token = mock(IBinder.class);
- final ClientTransaction destroyTransaction = ClientTransaction.obtain(null /* client */);
- destroyTransaction.setLifecycleStateRequest(
- DestroyActivityItem.obtain(token, false /* finished */, 0 /* configChanges */));
- destroyTransaction.preExecute(mTransactionHandler);
- // The activity should be added to to-be-destroyed container.
- assertEquals(1, activitiesToBeDestroyed.size());
-
- // A previous queued launch transaction runs on main thread (execute).
- final ClientTransaction launchTransaction = ClientTransaction.obtain(null /* client */);
- final LaunchActivityItem launchItem =
- spy(new LaunchActivityItemBuilder(token, new Intent(), new ActivityInfo()).build());
- launchTransaction.addCallback(launchItem);
- mExecutor.execute(launchTransaction);
-
- // The launch transaction should not be executed because its token is in the
- // to-be-destroyed container.
- verify(launchItem, never()).execute(any(), any());
-
- // After the destroy transaction has been executed, the token should be removed.
- mExecutor.execute(destroyTransaction);
- assertTrue(activitiesToBeDestroyed.isEmpty());
+ testDoNotLaunchDestroyedActivityInner();
}
@Test
- public void testExecuteTransactionItems_doNotLaunchDestroyedActivity() {
+ public void testDoNotLaunchDestroyedActivity_bundleClientTransaction() {
+ mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
+
+ testDoNotLaunchDestroyedActivityInner();
+ }
+
+ private void testDoNotLaunchDestroyedActivityInner() {
final Map<IBinder, DestroyActivityItem> activitiesToBeDestroyed = new ArrayMap<>();
when(mTransactionHandler.getActivitiesToBeDestroyed()).thenReturn(activitiesToBeDestroyed);
// Assume launch transaction is still in queue, so there is no client record.
@@ -350,26 +329,19 @@
@Test
public void testActivityResultRequiredStateResolution() {
- when(mTransactionHandler.getActivity(any())).thenReturn(mock(Activity.class));
+ mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
- PostExecItem postExecItem = new PostExecItem(ON_RESUME);
-
- ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
- transaction.addCallback(postExecItem);
-
- // Verify resolution that should get to onPause
- mClientRecord.setState(ON_RESUME);
- mExecutor.executeCallbacks(transaction);
- verify(mExecutor).cycleToPath(eq(mClientRecord), eq(ON_PAUSE), eq(transaction));
-
- // Verify resolution that should get to onStart
- mClientRecord.setState(ON_STOP);
- mExecutor.executeCallbacks(transaction);
- verify(mExecutor).cycleToPath(eq(mClientRecord), eq(ON_START), eq(transaction));
+ testActivityResultRequiredStateResolutionInner();
}
@Test
- public void testExecuteTransactionItems_activityResultRequiredStateResolution() {
+ public void testActivityResultRequiredStateResolution_bundleClientTransaction() {
+ mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
+
+ testActivityResultRequiredStateResolutionInner();
+ }
+
+ private void testActivityResultRequiredStateResolutionInner() {
when(mTransactionHandler.getActivity(any())).thenReturn(mock(Activity.class));
PostExecItem postExecItem = new PostExecItem(ON_RESUME);
@@ -379,12 +351,12 @@
// Verify resolution that should get to onPause
mClientRecord.setState(ON_RESUME);
- mExecutor.executeTransactionItems(transaction);
+ mExecutor.execute(transaction);
verify(mExecutor).cycleToPath(eq(mClientRecord), eq(ON_PAUSE), eq(transaction));
// Verify resolution that should get to onStart
mClientRecord.setState(ON_STOP);
- mExecutor.executeTransactionItems(transaction);
+ mExecutor.execute(transaction);
verify(mExecutor).cycleToPath(eq(mClientRecord), eq(ON_START), eq(transaction));
}
@@ -523,18 +495,19 @@
@Test(expected = IllegalArgumentException.class)
public void testActivityItemNullRecordThrowsException() {
- final ActivityTransactionItem activityItem = mock(ActivityTransactionItem.class);
- when(activityItem.getPostExecutionState()).thenReturn(UNDEFINED);
- final IBinder token = mock(IBinder.class);
- final ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
- transaction.addCallback(activityItem);
- when(mTransactionHandler.getActivityClient(token)).thenReturn(null);
+ mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
- mExecutor.executeCallbacks(transaction);
+ testActivityItemNullRecordThrowsExceptionInner();
}
@Test(expected = IllegalArgumentException.class)
- public void testExecuteTransactionItems_activityItemNullRecordThrowsException() {
+ public void testActivityItemNullRecordThrowsException_bundleClientTransaction() {
+ mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
+
+ testActivityItemNullRecordThrowsExceptionInner();
+ }
+
+ private void testActivityItemNullRecordThrowsExceptionInner() {
final ActivityTransactionItem activityItem = mock(ActivityTransactionItem.class);
when(activityItem.getPostExecutionState()).thenReturn(UNDEFINED);
final IBinder token = mock(IBinder.class);
@@ -542,28 +515,24 @@
transaction.addTransactionItem(activityItem);
when(mTransactionHandler.getActivityClient(token)).thenReturn(null);
- mExecutor.executeTransactionItems(transaction);
+ mExecutor.execute(transaction);
}
@Test
public void testActivityItemExecute() {
- final ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
- final ActivityTransactionItem activityItem = mock(ActivityTransactionItem.class);
- when(activityItem.getPostExecutionState()).thenReturn(UNDEFINED);
- when(activityItem.getActivityToken()).thenReturn(mActivityToken);
- transaction.addCallback(activityItem);
- transaction.setLifecycleStateRequest(mActivityLifecycleItem);
+ mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
- mExecutor.execute(transaction);
-
- final InOrder inOrder = inOrder(activityItem, mActivityLifecycleItem);
- inOrder.verify(activityItem).execute(eq(mTransactionHandler), eq(mClientRecord), any());
- inOrder.verify(mActivityLifecycleItem).execute(eq(mTransactionHandler), eq(mClientRecord),
- any());
+ testActivityItemExecuteInner();
}
@Test
- public void testExecuteTransactionItems_activityItemExecute() {
+ public void testActivityItemExecute_bundleClientTransaction() {
+ mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
+
+ testActivityItemExecuteInner();
+ }
+
+ private void testActivityItemExecuteInner() {
final ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
final ActivityTransactionItem activityItem = mock(ActivityTransactionItem.class);
when(activityItem.getPostExecutionState()).thenReturn(UNDEFINED);
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index c30d216..aa80013 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -20,6 +20,9 @@
import static android.app.servertransaction.TestUtils.mergedConfig;
import static android.app.servertransaction.TestUtils.referrerIntentList;
import static android.app.servertransaction.TestUtils.resultInfoList;
+import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
+
+import static com.android.window.flags.Flags.FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG;
import static org.junit.Assert.assertEquals;
@@ -36,11 +39,13 @@
import android.os.Parcelable;
import android.os.PersistableBundle;
import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -60,6 +65,9 @@
@Presubmit
public class TransactionParcelTests {
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
+
private Parcel mParcel;
private IBinder mActivityToken;
@@ -275,6 +283,8 @@
@Test
public void testClientTransaction() {
+ mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
+
// Write to parcel
NewIntentItem callback1 = NewIntentItem.obtain(mActivityToken, new ArrayList<>(), true);
ActivityConfigurationChangeItem callback2 = ActivityConfigurationChangeItem.obtain(
@@ -300,14 +310,16 @@
@Test
public void testClientTransactionCallbacksOnly() {
+ mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
+
// Write to parcel
NewIntentItem callback1 = NewIntentItem.obtain(mActivityToken, new ArrayList<>(), true);
ActivityConfigurationChangeItem callback2 = ActivityConfigurationChangeItem.obtain(
mActivityToken, config());
ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
- transaction.addCallback(callback1);
- transaction.addCallback(callback2);
+ transaction.addTransactionItem(callback1);
+ transaction.addTransactionItem(callback2);
writeAndPrepareForReading(transaction);
@@ -321,12 +333,14 @@
@Test
public void testClientTransactionLifecycleOnly() {
+ mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
+
// Write to parcel
StopActivityItem lifecycleRequest = StopActivityItem.obtain(mActivityToken,
78 /* configChanges */);
ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
- transaction.setLifecycleStateRequest(lifecycleRequest);
+ transaction.addTransactionItem(lifecycleRequest);
writeAndPrepareForReading(transaction);
diff --git a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateTest.java b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateTest.java
index 396d403..c287721 100644
--- a/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateTest.java
+++ b/core/tests/devicestatetests/src/android/hardware/devicestate/DeviceStateTest.java
@@ -16,8 +16,8 @@
package android.hardware.devicestate;
-import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE;
-import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE;
+import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE_IDENTIFIER;
+import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE_IDENTIFIER;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNull;
@@ -39,35 +39,33 @@
public final class DeviceStateTest {
@Test
public void testConstruct() {
- final DeviceState state = new DeviceState(MINIMUM_DEVICE_STATE /* identifier */,
+ final DeviceState state = new DeviceState(MINIMUM_DEVICE_STATE_IDENTIFIER /* identifier */,
"TEST_CLOSED" /* name */, DeviceState.FLAG_CANCEL_OVERRIDE_REQUESTS /* flags */);
- assertEquals(state.getIdentifier(), MINIMUM_DEVICE_STATE);
+ assertEquals(state.getIdentifier(), MINIMUM_DEVICE_STATE_IDENTIFIER);
assertEquals(state.getName(), "TEST_CLOSED");
assertEquals(state.getFlags(), DeviceState.FLAG_CANCEL_OVERRIDE_REQUESTS);
}
@Test
public void testConstruct_nullName() {
- final DeviceState state = new DeviceState(MAXIMUM_DEVICE_STATE /* identifier */,
+ final DeviceState state = new DeviceState(MAXIMUM_DEVICE_STATE_IDENTIFIER /* identifier */,
null /* name */, 0/* flags */);
- assertEquals(state.getIdentifier(), MAXIMUM_DEVICE_STATE);
+ assertEquals(state.getIdentifier(), MAXIMUM_DEVICE_STATE_IDENTIFIER);
assertNull(state.getName());
assertEquals(state.getFlags(), 0);
}
@Test
public void testConstruct_tooLargeIdentifier() {
- assertThrows(IllegalArgumentException.class, () -> {
- final DeviceState state = new DeviceState(MAXIMUM_DEVICE_STATE + 1 /* identifier */,
- null /* name */, 0 /* flags */);
- });
+ assertThrows(IllegalArgumentException.class,
+ () -> new DeviceState(MAXIMUM_DEVICE_STATE_IDENTIFIER + 1 /* identifier */,
+ null /* name */, 0 /* flags */));
}
@Test
public void testConstruct_tooSmallIdentifier() {
- assertThrows(IllegalArgumentException.class, () -> {
- final DeviceState state = new DeviceState(MINIMUM_DEVICE_STATE - 1 /* identifier */,
- null /* name */, 0 /* flags */);
- });
+ assertThrows(IllegalArgumentException.class,
+ () -> new DeviceState(MINIMUM_DEVICE_STATE_IDENTIFIER - 1 /* identifier */,
+ null /* name */, 0 /* flags */));
}
}
diff --git a/core/tests/utiltests/src/android/util/MemoryIntArrayTest.java b/core/tests/utiltests/src/android/util/MemoryIntArrayTest.java
index 51013e4..8093af9 100644
--- a/core/tests/utiltests/src/android/util/MemoryIntArrayTest.java
+++ b/core/tests/utiltests/src/android/util/MemoryIntArrayTest.java
@@ -123,7 +123,7 @@
parcel.recycle();
assertNotNull("Should marshall file descriptor", secondArray);
-
+ assertEquals("Marshalled size must be three", 3, secondArray.size());
assertEquals("First element should be 1", 1, secondArray.get(0));
assertEquals("First element should be 2", 2, secondArray.get(1));
assertEquals("First element should be 3", 3, secondArray.get(2));
diff --git a/core/tests/utiltests/src/android/util/RemoteMemoryIntArrayService.java b/core/tests/utiltests/src/android/util/RemoteMemoryIntArrayService.java
index 9264c6c..32dda6b 100644
--- a/core/tests/utiltests/src/android/util/RemoteMemoryIntArrayService.java
+++ b/core/tests/utiltests/src/android/util/RemoteMemoryIntArrayService.java
@@ -84,11 +84,7 @@
@Override
public int size() {
synchronized (mLock) {
- try {
- return mArray.size();
- } catch (IOException e) {
- throw new IllegalStateException(e);
- }
+ return mArray.size();
}
}
diff --git a/core/tests/vibrator/Android.bp b/core/tests/vibrator/Android.bp
index 09608e9..3ebe150 100644
--- a/core/tests/vibrator/Android.bp
+++ b/core/tests/vibrator/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_haptics_framework",
// See: http://go/android-license-faq
default_applicable_licenses: ["frameworks_base_license"],
}
diff --git a/graphics/java/Android.bp b/graphics/java/Android.bp
index db37a387..ece453d 100644
--- a/graphics/java/Android.bp
+++ b/graphics/java/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_android_gpu",
// 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"
diff --git a/graphics/java/android/graphics/pdf/TEST_MAPPING b/graphics/java/android/graphics/pdf/TEST_MAPPING
index d763598..afec35c 100644
--- a/graphics/java/android/graphics/pdf/TEST_MAPPING
+++ b/graphics/java/android/graphics/pdf/TEST_MAPPING
@@ -1,7 +1,12 @@
{
"presubmit": [
{
- "name": "CtsPdfTestCases"
+ "name": "CtsPdfTestCases",
+ "options": [
+ {
+ "include-filter": "android.graphics.pdf.cts.PdfDocumentTest"
+ }
+ ]
}
]
}
diff --git a/graphics/proto/Android.bp b/graphics/proto/Android.bp
index 1b19266..fdcfdd5 100644
--- a/graphics/proto/Android.bp
+++ b/graphics/proto/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_android_gpu",
// 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"
diff --git a/libs/WindowManager/Shell/Android.bp b/libs/WindowManager/Shell/Android.bp
index 310300d..d66c925 100644
--- a/libs/WindowManager/Shell/Android.bp
+++ b/libs/WindowManager/Shell/Android.bp
@@ -206,6 +206,8 @@
srcs: [
"multivalentTests/src/**/*.kt",
],
+ // TODO(b/323188766): Include BubbleStackViewTest once the robolectric issue is fixed.
+ exclude_srcs: ["multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt"],
static_libs: [
"junit",
"androidx.test.runner",
diff --git a/libs/WindowManager/Shell/aconfig/OWNERS b/libs/WindowManager/Shell/aconfig/OWNERS
new file mode 100644
index 0000000..9eba0f2
--- /dev/null
+++ b/libs/WindowManager/Shell/aconfig/OWNERS
@@ -0,0 +1,3 @@
+# Owners for flag changes
+madym@google.com
+hwwang@google.com
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/aconfig/multitasking.aconfig b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
index 9a66c0f..0967f4e 100644
--- a/libs/WindowManager/Shell/aconfig/multitasking.aconfig
+++ b/libs/WindowManager/Shell/aconfig/multitasking.aconfig
@@ -64,3 +64,10 @@
description: "Enables the new bubble bar UI for tablets"
bug: "286246694"
}
+
+flag {
+ name: "enable_bubbles_long_press_nav_handle"
+ namespace: "multitasking"
+ description: "Enables long-press action for nav handle when a bubble is expanded"
+ bug: "324910035"
+}
diff --git a/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt
new file mode 100644
index 0000000..8989fc5
--- /dev/null
+++ b/libs/WindowManager/Shell/multivalentTests/src/com/android/wm/shell/bubbles/BubbleStackViewTest.kt
@@ -0,0 +1,215 @@
+/*
+ * 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.content.Context
+import android.content.Intent
+import android.graphics.Color
+import android.graphics.drawable.Icon
+import android.os.UserHandle
+import android.view.IWindowManager
+import android.view.WindowManager
+import android.view.WindowManagerGlobal
+import androidx.test.annotation.UiThreadTest
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.testing.UiEventLoggerFake
+import com.android.internal.protolog.common.ProtoLog
+import com.android.launcher3.icons.BubbleIconFactory
+import com.android.wm.shell.R
+import com.android.wm.shell.bubbles.Bubbles.SysuiProxy
+import com.android.wm.shell.common.FloatingContentCoordinator
+import com.android.wm.shell.common.ShellExecutor
+import com.android.wm.shell.taskview.TaskView
+import com.android.wm.shell.taskview.TaskViewTaskController
+import com.google.common.truth.Truth.assertThat
+import com.google.common.util.concurrent.MoreExecutors.directExecutor
+import java.util.concurrent.Semaphore
+import java.util.concurrent.TimeUnit
+import java.util.function.Consumer
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.kotlin.mock
+
+/** Unit tests for [BubbleStackView]. */
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class BubbleStackViewTest {
+
+ private val context = ApplicationProvider.getApplicationContext<Context>()
+ private lateinit var positioner: BubblePositioner
+ private lateinit var iconFactory: BubbleIconFactory
+ private lateinit var expandedViewManager: FakeBubbleExpandedViewManager
+ private lateinit var bubbleStackView: BubbleStackView
+ private lateinit var shellExecutor: ShellExecutor
+ private lateinit var windowManager: IWindowManager
+ private lateinit var bubbleTaskViewFactory: BubbleTaskViewFactory
+ private lateinit var bubbleData: BubbleData
+
+ @Before
+ fun setUp() {
+ // Disable protolog tool when running the tests from studio
+ ProtoLog.REQUIRE_PROTOLOGTOOL = false
+ windowManager = WindowManagerGlobal.getWindowManagerService()!!
+ shellExecutor = TestShellExecutor()
+ val windowManager = context.getSystemService(WindowManager::class.java)
+ iconFactory =
+ BubbleIconFactory(
+ context,
+ context.resources.getDimensionPixelSize(R.dimen.bubble_size),
+ context.resources.getDimensionPixelSize(R.dimen.bubble_badge_size),
+ Color.BLACK,
+ context.resources.getDimensionPixelSize(
+ com.android.internal.R.dimen.importance_ring_stroke_width
+ )
+ )
+ positioner = BubblePositioner(context, windowManager)
+ val bubbleStackViewManager = FakeBubbleStackViewManager()
+ bubbleData =
+ BubbleData(
+ context,
+ BubbleLogger(UiEventLoggerFake()),
+ positioner,
+ BubbleEducationController(context),
+ shellExecutor
+ )
+
+ val sysuiProxy = mock<SysuiProxy>()
+ expandedViewManager = FakeBubbleExpandedViewManager()
+ bubbleTaskViewFactory = FakeBubbleTaskViewFactory()
+ bubbleStackView =
+ BubbleStackView(
+ context,
+ bubbleStackViewManager,
+ positioner,
+ bubbleData,
+ null,
+ FloatingContentCoordinator(),
+ { sysuiProxy },
+ shellExecutor
+ )
+ }
+
+ @UiThreadTest
+ @Test
+ fun addBubble() {
+ val bubble = createAndInflateBubble()
+ bubbleStackView.addBubble(bubble)
+ assertThat(bubbleStackView.bubbleCount).isEqualTo(1)
+ }
+
+ @UiThreadTest
+ @Test
+ fun tapBubbleToExpand() {
+ val bubble = createAndInflateBubble()
+ bubbleStackView.addBubble(bubble)
+ assertThat(bubbleStackView.bubbleCount).isEqualTo(1)
+
+ bubble.iconView!!.performClick()
+ // we're checking the expanded state in BubbleData because that's the source of truth. This
+ // will eventually propagate an update back to the stack view, but setting the entire
+ // pipeline is outside the scope of a unit test.
+ assertThat(bubbleData.isExpanded).isTrue()
+ }
+
+ private fun createAndInflateBubble(): Bubble {
+ val intent = Intent(Intent.ACTION_VIEW).setPackage(context.packageName)
+ val icon = Icon.createWithResource(context.resources, R.drawable.bubble_ic_overflow_button)
+ val bubble = Bubble.createAppBubble(intent, UserHandle(1), icon, directExecutor())
+ bubble.setInflateSynchronously(true)
+ bubbleData.notificationEntryUpdated(bubble, true, false)
+
+ val semaphore = Semaphore(0)
+ val callback: BubbleViewInfoTask.Callback =
+ BubbleViewInfoTask.Callback { semaphore.release() }
+ bubble.inflate(
+ callback,
+ context,
+ expandedViewManager,
+ bubbleTaskViewFactory,
+ positioner,
+ bubbleStackView,
+ null,
+ iconFactory,
+ false
+ )
+
+ assertThat(semaphore.tryAcquire(5, TimeUnit.SECONDS)).isTrue()
+ assertThat(bubble.isInflated).isTrue()
+ return bubble
+ }
+
+ private class FakeBubbleStackViewManager : BubbleStackViewManager {
+
+ override fun onAllBubblesAnimatedOut() {}
+
+ override fun updateWindowFlagsForBackpress(interceptBack: Boolean) {}
+
+ override fun checkNotificationPanelExpandedState(callback: Consumer<Boolean>) {}
+
+ override fun hideCurrentInputMethod() {}
+ }
+
+ private class TestShellExecutor : ShellExecutor {
+
+ override fun execute(runnable: Runnable) {
+ runnable.run()
+ }
+
+ override fun executeDelayed(r: Runnable, delayMillis: Long) {
+ r.run()
+ }
+
+ override fun removeCallbacks(r: Runnable) {}
+
+ override fun hasCallback(r: Runnable): Boolean = false
+ }
+
+ private inner class FakeBubbleTaskViewFactory : BubbleTaskViewFactory {
+ override fun create(): BubbleTaskView {
+ val taskViewTaskController = mock<TaskViewTaskController>()
+ val taskView = TaskView(context, taskViewTaskController)
+ return BubbleTaskView(taskView, shellExecutor)
+ }
+ }
+
+ private inner class FakeBubbleExpandedViewManager : BubbleExpandedViewManager {
+
+ override val overflowBubbles: List<Bubble>
+ get() = 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 = false
+
+ override fun isShowingAsBubbleBar(): Boolean = false
+ }
+}
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 4e995bc..8946f41 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
@@ -15,6 +15,7 @@
*/
package com.android.wm.shell.bubbles.bar;
+import static android.view.View.ALPHA;
import static android.view.View.SCALE_X;
import static android.view.View.SCALE_Y;
import static android.view.View.TRANSLATION_X;
@@ -69,6 +70,7 @@
private static final float EXPANDED_VIEW_IN_TARGET_SCALE = 0.2f;
private static final float EXPANDED_VIEW_DRAG_SCALE = 0.4f;
private static final float DISMISS_VIEW_SCALE = 1.25f;
+ private static final int HANDLE_ALPHA_ANIMATION_DURATION = 100;
/** Spring config for the expanded view scale-in animation. */
private final PhysicsAnimator.SpringConfig mScaleInSpringConfig =
@@ -248,15 +250,22 @@
return;
}
setDragPivot(bbev);
- AnimatorSet animatorSet = new AnimatorSet();
// Corner radius gets scaled, apply the reverse scale to ensure we have the desired radius
final float cornerRadius = bbev.getDraggedCornerRadius() / EXPANDED_VIEW_DRAG_SCALE;
- animatorSet.playTogether(
+
+ AnimatorSet contentAnim = new AnimatorSet();
+ contentAnim.playTogether(
ObjectAnimator.ofFloat(bbev, SCALE_X, EXPANDED_VIEW_DRAG_SCALE),
ObjectAnimator.ofFloat(bbev, SCALE_Y, EXPANDED_VIEW_DRAG_SCALE),
ObjectAnimator.ofFloat(bbev, CORNER_RADIUS, cornerRadius)
);
- animatorSet.setDuration(EXPANDED_VIEW_DRAG_ANIMATION_DURATION).setInterpolator(EMPHASIZED);
+ contentAnim.setDuration(EXPANDED_VIEW_DRAG_ANIMATION_DURATION).setInterpolator(EMPHASIZED);
+
+ ObjectAnimator handleAnim = ObjectAnimator.ofFloat(bbev.getHandleView(), ALPHA, 0f)
+ .setDuration(HANDLE_ALPHA_ANIMATION_DURATION);
+
+ AnimatorSet animatorSet = new AnimatorSet();
+ animatorSet.playTogether(contentAnim, handleAnim);
animatorSet.addListener(new DragAnimatorListenerAdapter(bbev));
startNewDragAnimation(animatorSet);
}
@@ -297,15 +306,21 @@
}
Point restPoint = getExpandedViewRestPosition(getExpandedViewSize());
- AnimatorSet animatorSet = new AnimatorSet();
- animatorSet.playTogether(
+ AnimatorSet contentAnim = new AnimatorSet();
+ contentAnim.playTogether(
ObjectAnimator.ofFloat(bbev, X, restPoint.x),
ObjectAnimator.ofFloat(bbev, Y, restPoint.y),
ObjectAnimator.ofFloat(bbev, SCALE_X, 1f),
ObjectAnimator.ofFloat(bbev, SCALE_Y, 1f),
ObjectAnimator.ofFloat(bbev, CORNER_RADIUS, bbev.getRestingCornerRadius())
);
- animatorSet.setDuration(EXPANDED_VIEW_ANIMATE_TO_REST_DURATION).setInterpolator(EMPHASIZED);
+ contentAnim.setDuration(EXPANDED_VIEW_ANIMATE_TO_REST_DURATION).setInterpolator(EMPHASIZED);
+
+ ObjectAnimator handleAlphaAnim = ObjectAnimator.ofFloat(bbev.getHandleView(), ALPHA, 1f)
+ .setDuration(HANDLE_ALPHA_ANIMATION_DURATION);
+
+ AnimatorSet animatorSet = new AnimatorSet();
+ animatorSet.playTogether(contentAnim, handleAlphaAnim);
animatorSet.addListener(new DragAnimatorListenerAdapter(bbev) {
@Override
public void onAnimationEnd(Animator animation) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiInstanceHelper.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiInstanceHelper.kt
new file mode 100644
index 0000000..4c34971
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/MultiInstanceHelper.kt
@@ -0,0 +1,124 @@
+/*
+ * 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.common
+
+import android.app.PendingIntent
+import android.content.ComponentName
+import android.content.Context
+import android.content.pm.LauncherApps
+import android.content.pm.PackageManager
+import android.os.UserHandle
+import android.view.WindowManager
+import android.view.WindowManager.PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI
+import com.android.internal.annotations.VisibleForTesting
+import com.android.wm.shell.R
+import com.android.wm.shell.protolog.ShellProtoLogGroup
+import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL
+import com.android.wm.shell.util.KtProtoLog
+import java.util.Arrays
+
+/**
+ * Helper for multi-instance related checks.
+ */
+class MultiInstanceHelper @JvmOverloads constructor(
+ private val context: Context,
+ private val packageManager: PackageManager,
+ private val staticAppsSupportingMultiInstance: Array<String> = context.resources
+ .getStringArray(R.array.config_appsSupportMultiInstancesSplit)) {
+
+ /**
+ * Returns whether a specific component desires to be launched in multiple instances.
+ */
+ @VisibleForTesting
+ fun supportsMultiInstanceSplit(componentName: ComponentName?): Boolean {
+ if (componentName == null || componentName.packageName == null) {
+ // TODO(b/262864589): Handle empty component case
+ return false
+ }
+
+ // Check the pre-defined allow list
+ val packageName = componentName.packageName
+ for (pkg in staticAppsSupportingMultiInstance) {
+ if (pkg == packageName) {
+ KtProtoLog.v(WM_SHELL, "application=%s in allowlist supports multi-instance",
+ packageName)
+ return true
+ }
+ }
+
+ // Check the activity property first
+ try {
+ val activityProp = packageManager.getProperty(
+ PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, componentName)
+ // If the above call doesn't throw a NameNotFoundException, then the activity property
+ // should override the application property value
+ if (activityProp.isBoolean) {
+ KtProtoLog.v(WM_SHELL, "activity=%s supports multi-instance", componentName)
+ return activityProp.boolean
+ } else {
+ KtProtoLog.w(WM_SHELL, "Warning: property=%s for activity=%s has non-bool type=%d",
+ PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, packageName, activityProp.type)
+ }
+ } catch (nnfe: PackageManager.NameNotFoundException) {
+ // Not specified in the activity, fall through
+ }
+
+ // Check the application property otherwise
+ try {
+ val appProp = packageManager.getProperty(
+ PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, packageName)
+ if (appProp.isBoolean) {
+ KtProtoLog.v(WM_SHELL, "application=%s supports multi-instance", packageName)
+ return appProp.boolean
+ } else {
+ KtProtoLog.w(WM_SHELL,
+ "Warning: property=%s for application=%s has non-bool type=%d",
+ PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, packageName, appProp.type)
+ }
+ } catch (nnfe: PackageManager.NameNotFoundException) {
+ // Not specified in either application or activity
+ }
+ return false
+ }
+
+ companion object {
+ /** Returns the component from a PendingIntent */
+ @JvmStatic
+ fun getComponent(pendingIntent: PendingIntent?): ComponentName? {
+ return pendingIntent?.intent?.component
+ }
+
+ /** Returns the component from a shortcut */
+ @JvmStatic
+ fun getShortcutComponent(packageName: String, shortcutId: String,
+ user: UserHandle, launcherApps: LauncherApps): ComponentName? {
+ val query = LauncherApps.ShortcutQuery()
+ query.setPackage(packageName)
+ query.setShortcutIds(Arrays.asList(shortcutId))
+ query.setQueryFlags(LauncherApps.ShortcutQuery.FLAG_MATCH_ALL_KINDS_WITH_ALL_PINNED)
+ val shortcuts = launcherApps.getShortcuts(query, user)
+ val info = if (shortcuts != null && shortcuts.size > 0) shortcuts[0] else null
+ return info?.activity
+ }
+
+ /** Returns true if package names and user ids match. */
+ @JvmStatic
+ fun samePackage(packageName1: String?, packageName2: String?,
+ userId1: Int, userId2: Int): Boolean {
+ return (packageName1 != null && packageName1 == packageName2) && (userId1 == userId2)
+ }
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/IPip.aidl
similarity index 96%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl
rename to libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/IPip.aidl
index 8b3de62..b5f25433f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPip.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/IPip.aidl
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.pip;
+package com.android.wm.shell.common.pip;
import android.app.PictureInPictureParams;
import android.view.SurfaceControl;
@@ -22,7 +22,7 @@
import android.content.pm.ActivityInfo;
import android.graphics.Rect;
-import com.android.wm.shell.pip.IPipAnimationListener;
+import com.android.wm.shell.common.pip.IPipAnimationListener;
/**
* Interface that is exposed to remote callers to manipulate the Pip feature.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPipAnimationListener.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/IPipAnimationListener.aidl
similarity index 96%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPipAnimationListener.aidl
rename to libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/IPipAnimationListener.aidl
index 062e3ba..b8d1966 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/IPipAnimationListener.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/IPipAnimationListener.aidl
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.pip;
+package com.android.wm.shell.common.pip;
/**
* Listener interface that Launcher attaches to SystemUI to get Pip animation callbacks.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java
index 662f325..f9259e7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitScreenUtils.java
@@ -99,12 +99,6 @@
return taskInfo != null ? taskInfo.userId : -1;
}
- /** Returns true if package names and user ids match. */
- public static boolean samePackage(String packageName1, String packageName2,
- int userId1, int userId2) {
- return (packageName1 != null && packageName1.equals(packageName2)) && (userId1 == userId2);
- }
-
/** Generates a common log message for split screen failures */
public static String splitFailureMessage(String caller, String reason) {
return "(" + caller + ") Splitscreen aborted: " + reason;
@@ -143,28 +137,4 @@
return isLandscape;
}
}
-
- /** Returns the component from a PendingIntent */
- @Nullable
- public static ComponentName getComponent(@Nullable PendingIntent pendingIntent) {
- if (pendingIntent == null || pendingIntent.getIntent() == null) {
- return null;
- }
- return pendingIntent.getIntent().getComponent();
- }
-
- /** Returns the component from a shortcut */
- @Nullable
- public static ComponentName getShortcutComponent(@NonNull String packageName, String shortcutId,
- @NonNull UserHandle user, @NonNull LauncherApps launcherApps) {
- LauncherApps.ShortcutQuery query = new LauncherApps.ShortcutQuery();
- query.setPackage(packageName);
- query.setShortcutIds(Arrays.asList(shortcutId));
- query.setQueryFlags(FLAG_MATCH_ALL_KINDS_WITH_ALL_PINNED);
- List<ShortcutInfo> shortcuts = launcherApps.getShortcuts(query, user);
- ShortcutInfo info = shortcuts != null && shortcuts.size() > 0
- ? shortcuts.get(0)
- : null;
- return info != null ? info.getActivity() : null;
- }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java
index 81d1399..7c28099 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManager.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.AppCompatTaskInfo;
import android.app.TaskInfo;
import android.content.Context;
import android.content.Intent;
@@ -88,7 +89,8 @@
mShellExecutor = shellExecutor;
mUserAspectRatioButtonShownChecker = userAspectRatioButtonStateChecker;
mUserAspectRatioButtonStateConsumer = userAspectRatioButtonShownConsumer;
- mHasUserAspectRatioSettingsButton = getHasUserAspectRatioSettingsButton(taskInfo);
+ mHasUserAspectRatioSettingsButton = shouldShowUserAspectRatioSettingsButton(
+ taskInfo.appCompatTaskInfo, taskInfo.baseIntent);
mCompatUIHintsState = compatUIHintsState;
mOnButtonClicked = onButtonClicked;
mDisappearTimeSupplier = disappearTimeSupplier;
@@ -134,7 +136,8 @@
public boolean updateCompatInfo(@NonNull TaskInfo taskInfo,
@NonNull ShellTaskOrganizer.TaskListener taskListener, boolean canShow) {
final boolean prevHasUserAspectRatioSettingsButton = mHasUserAspectRatioSettingsButton;
- mHasUserAspectRatioSettingsButton = getHasUserAspectRatioSettingsButton(taskInfo);
+ mHasUserAspectRatioSettingsButton = shouldShowUserAspectRatioSettingsButton(
+ taskInfo.appCompatTaskInfo, taskInfo.baseIntent);
if (!super.updateCompatInfo(taskInfo, taskListener, canShow)) {
return false;
@@ -227,12 +230,21 @@
return SystemClock.uptimeMillis() + hideDelay;
}
- private boolean getHasUserAspectRatioSettingsButton(@NonNull TaskInfo taskInfo) {
- final Intent intent = taskInfo.baseIntent;
- return taskInfo.appCompatTaskInfo.topActivityEligibleForUserAspectRatioButton
- && (taskInfo.appCompatTaskInfo.topActivityBoundsLetterboxed
- || taskInfo.appCompatTaskInfo.isUserFullscreenOverrideEnabled)
- && !taskInfo.appCompatTaskInfo.isSystemFullscreenOverrideEnabled
+ private boolean shouldShowUserAspectRatioSettingsButton(@NonNull AppCompatTaskInfo taskInfo,
+ @NonNull Intent intent) {
+ final Rect stableBounds = getTaskStableBounds();
+ final int letterboxHeight = taskInfo.topActivityLetterboxHeight;
+ final int letterboxWidth = taskInfo.topActivityLetterboxWidth;
+ // App is not visibly letterboxed if it covers status bar/bottom insets or matches the
+ // stable bounds, so don't show the button
+ if (stableBounds.height() <= letterboxHeight && stableBounds.width() <= letterboxWidth) {
+ return false;
+ }
+
+ return taskInfo.topActivityEligibleForUserAspectRatioButton
+ && (taskInfo.topActivityBoundsLetterboxed
+ || taskInfo.isUserFullscreenOverrideEnabled)
+ && !taskInfo.isSystemFullscreenOverrideEnabled
&& Intent.ACTION_MAIN.equals(intent.getAction())
&& intent.hasCategory(Intent.CATEGORY_LAUNCHER)
&& (!mUserAspectRatioButtonShownChecker.get() || isShowingButton());
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
index d4ed017..216da07 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/TvWMShellModule.java
@@ -26,6 +26,7 @@
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.LaunchAdjacentController;
+import com.android.wm.shell.common.MultiInstanceHelper;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.SystemWindows;
@@ -88,13 +89,14 @@
IconProvider iconProvider,
Optional<RecentTasksController> recentTasks,
LaunchAdjacentController launchAdjacentController,
+ MultiInstanceHelper multiInstanceHelper,
@ShellMainThread ShellExecutor mainExecutor,
Handler mainHandler,
SystemWindows systemWindows) {
return new TvSplitScreenController(context, shellInit, shellCommandHandler, shellController,
shellTaskOrganizer, syncQueue, rootTDAOrganizer, displayController,
displayImeController, displayInsetsController, transitions, transactionPool,
- iconProvider, recentTasks, launchAdjacentController, mainExecutor, mainHandler,
- systemWindows);
+ iconProvider, recentTasks, launchAdjacentController, multiInstanceHelper,
+ mainExecutor, mainHandler, systemWindows);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index 63a912e..8b2ec0a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -50,6 +50,7 @@
import com.android.wm.shell.common.DockStateReader;
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.LaunchAdjacentController;
+import com.android.wm.shell.common.MultiInstanceHelper;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.SystemWindows;
@@ -322,6 +323,12 @@
return Optional.of(perfHintController.getHinter());
}
+ @WMSingleton
+ @Provides
+ static MultiInstanceHelper provideMultiInstanceHelper(Context context) {
+ return new MultiInstanceHelper(context, context.getPackageManager());
+ }
+
//
// Back animation
//
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 bd9d89c..4fe79c1 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
@@ -47,6 +47,7 @@
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.FloatingContentCoordinator;
import com.android.wm.shell.common.LaunchAdjacentController;
+import com.android.wm.shell.common.MultiInstanceHelper;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TaskStackListenerImpl;
@@ -347,12 +348,14 @@
LaunchAdjacentController launchAdjacentController,
Optional<WindowDecorViewModel> windowDecorViewModel,
Optional<DesktopTasksController> desktopTasksController,
+ MultiInstanceHelper multiInstanceHelper,
@ShellMainThread ShellExecutor mainExecutor) {
return new SplitScreenController(context, shellInit, shellCommandHandler, shellController,
shellTaskOrganizer, syncQueue, rootTaskDisplayAreaOrganizer, displayController,
displayImeController, displayInsetsController, dragAndDropController, transitions,
transactionPool, iconProvider, recentTasks, launchAdjacentController,
- windowDecorViewModel, desktopTasksController, mainExecutor);
+ windowDecorViewModel, desktopTasksController, null /* stageCoordinator */,
+ multiInstanceHelper, mainExecutor);
}
//
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java
index e732a03..8305fa6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopMode.java
@@ -47,4 +47,8 @@
default void addDesktopGestureExclusionRegionListener(Consumer<Region> listener,
Executor callbackExecutor) { }
+
+ /** Called when requested to go to desktop mode from the current focused app. */
+ void enterDesktop(int displayId);
+
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index 837cb99..6de5d74 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -21,6 +21,7 @@
import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
+import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW
import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
import android.app.WindowConfiguration.WindowingMode
import android.content.Context
@@ -62,7 +63,7 @@
import com.android.wm.shell.recents.RecentsTransitionHandler
import com.android.wm.shell.recents.RecentsTransitionStateListener
import com.android.wm.shell.splitscreen.SplitScreenController
-import com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_ENTER_DESKTOP
+import com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DESKTOP_MODE
import com.android.wm.shell.sysui.ShellCommandHandler
import com.android.wm.shell.sysui.ShellController
import com.android.wm.shell.sysui.ShellInit
@@ -240,6 +241,43 @@
return desktopModeTaskRepository.getVisibleTaskCount(displayId)
}
+ /** Enter desktop by using the focused task in given `displayId` */
+ fun enterDesktop(displayId: Int) {
+ val allFocusedTasks =
+ shellTaskOrganizer.getRunningTasks(displayId).filter { taskInfo ->
+ taskInfo.isFocused &&
+ (taskInfo.windowingMode == WINDOWING_MODE_FULLSCREEN ||
+ taskInfo.windowingMode == WINDOWING_MODE_MULTI_WINDOW) &&
+ taskInfo.activityType != ACTIVITY_TYPE_HOME
+ }
+ if (allFocusedTasks.isNotEmpty()) {
+ when (allFocusedTasks.size) {
+ 2 -> {
+ // Split-screen case where there are two focused tasks, then we find the child
+ // task to move to desktop.
+ val splitFocusedTask = findChildFocusedTask(allFocusedTasks)
+ moveToDesktop(splitFocusedTask)
+ }
+ 1 -> {
+ // Fullscreen case where we move the current focused task.
+ moveToDesktop(allFocusedTasks[0].taskId)
+ }
+ else -> {
+ KtProtoLog.v(
+ WM_SHELL_DESKTOP_MODE,
+ "DesktopTasksController: Cannot enter desktop expected less " +
+ "than 3 focused tasks but found " + allFocusedTasks.size
+ )
+ }
+ }
+ }
+ }
+
+ private fun findChildFocusedTask(allFocusedTasks: List<RunningTaskInfo>): RunningTaskInfo {
+ if (allFocusedTasks[0].taskId == allFocusedTasks[1].parentTaskId) return allFocusedTasks[1]
+ return allFocusedTasks[0]
+ }
+
/** Move a task with given `taskId` to desktop */
fun moveToDesktop(
taskId: Int,
@@ -355,7 +393,7 @@
splitScreenController.prepareExitSplitScreen(
wct,
splitScreenController.getStageOfTask(taskInfo.taskId),
- EXIT_REASON_ENTER_DESKTOP
+ EXIT_REASON_DESKTOP_MODE
)
getOtherSplitTask(taskInfo.taskId)?.let { otherTaskInfo ->
wct.removeTask(otherTaskInfo.token)
@@ -919,19 +957,13 @@
}
if (inputCoordinate.x <= transitionAreaWidth) {
releaseVisualIndicator()
- val wct = WindowContainerTransaction()
- addMoveToSplitChanges(wct, taskInfo)
- splitScreenController.requestEnterSplitSelect(taskInfo, wct,
- SPLIT_POSITION_TOP_OR_LEFT, taskBounds)
+ snapToHalfScreen(taskInfo, SnapPosition.LEFT)
return
}
if (inputCoordinate.x >= (displayController.getDisplayLayout(taskInfo.displayId)?.width()
?.minus(transitionAreaWidth) ?: return)) {
releaseVisualIndicator()
- val wct = WindowContainerTransaction()
- addMoveToSplitChanges(wct, taskInfo)
- splitScreenController.requestEnterSplitSelect(taskInfo, wct,
- SPLIT_POSITION_BOTTOM_OR_RIGHT, taskBounds)
+ snapToHalfScreen(taskInfo, SnapPosition.RIGHT)
return
}
// A freeform drag-move ended, remove the indicator immediately.
@@ -1018,6 +1050,12 @@
this@DesktopTasksController.setTaskRegionListener(listener, callbackExecutor)
}
}
+
+ override fun enterDesktop(displayId: Int) {
+ mainExecutor.execute {
+ this@DesktopTasksController.enterDesktop(displayId)
+ }
+ }
}
/** The interface for calls from outside the host process. */
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 05d4f53..4b12134 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -75,6 +75,8 @@
import com.android.wm.shell.common.TabletopModeController;
import com.android.wm.shell.common.TaskStackListenerCallback;
import com.android.wm.shell.common.TaskStackListenerImpl;
+import com.android.wm.shell.common.pip.IPip;
+import com.android.wm.shell.common.pip.IPipAnimationListener;
import com.android.wm.shell.common.pip.PipAppOpsListener;
import com.android.wm.shell.common.pip.PipBoundsAlgorithm;
import com.android.wm.shell.common.pip.PipBoundsState;
@@ -85,8 +87,6 @@
import com.android.wm.shell.common.pip.PipUtils;
import com.android.wm.shell.onehanded.OneHandedController;
import com.android.wm.shell.onehanded.OneHandedTransitionCallback;
-import com.android.wm.shell.pip.IPip;
-import com.android.wm.shell.pip.IPipAnimationListener;
import com.android.wm.shell.pip.PinnedStackListenerForwarder;
import com.android.wm.shell.pip.Pip;
import com.android.wm.shell.pip.PipAnimationController;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
index 05e4af3..ad29d15 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/protolog/ShellProtoLogGroup.java
@@ -26,6 +26,8 @@
public enum ShellProtoLogGroup implements IProtoLogGroup {
// NOTE: Since we enable these from the same WM ShellCommand, these names should not conflict
// with those in the framework ProtoLogGroup
+ WM_SHELL(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
+ Consts.TAG_WM_SHELL),
WM_SHELL_INIT(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true,
Consts.TAG_WM_SHELL),
WM_SHELL_TASK_ORG(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 1b124c2..53dd981 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -23,25 +23,22 @@
import static android.content.Intent.FLAG_ACTIVITY_NO_USER_ACTION;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.RemoteAnimationTarget.MODE_OPENING;
-import static android.view.WindowManager.PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI;
import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
+import static com.android.wm.shell.common.MultiInstanceHelper.getComponent;
+import static com.android.wm.shell.common.MultiInstanceHelper.getShortcutComponent;
+import static com.android.wm.shell.common.MultiInstanceHelper.samePackage;
import static com.android.wm.shell.common.split.SplitScreenConstants.KEY_EXTRA_WIDGET_INTENT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
-import static com.android.wm.shell.common.split.SplitScreenUtils.getComponent;
-import static com.android.wm.shell.common.split.SplitScreenUtils.getShortcutComponent;
import static com.android.wm.shell.common.split.SplitScreenUtils.isValidToSplit;
import static com.android.wm.shell.common.split.SplitScreenUtils.reverseSplitPosition;
-import static com.android.wm.shell.common.split.SplitScreenUtils.samePackage;
import static com.android.wm.shell.common.split.SplitScreenUtils.splitFailureMessage;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_SPLIT_SCREEN;
import static com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
@@ -51,7 +48,6 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.LauncherApps;
-import android.content.pm.PackageManager;
import android.content.pm.ShortcutInfo;
import android.graphics.Rect;
import android.os.Bundle;
@@ -73,6 +69,8 @@
import androidx.annotation.BinderThread;
import androidx.annotation.IntDef;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.InstanceId;
@@ -86,6 +84,7 @@
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.ExternalInterfaceBinder;
import com.android.wm.shell.common.LaunchAdjacentController;
+import com.android.wm.shell.common.MultiInstanceHelper;
import com.android.wm.shell.common.RemoteCallable;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SingleInstanceRemoteListener;
@@ -138,7 +137,7 @@
public static final int EXIT_REASON_CHILD_TASK_ENTER_PIP = 9;
public static final int EXIT_REASON_RECREATE_SPLIT = 10;
public static final int EXIT_REASON_FULLSCREEN_SHORTCUT = 11;
- public static final int EXIT_REASON_ENTER_DESKTOP = 12;
+ public static final int EXIT_REASON_DESKTOP_MODE = 12;
@IntDef(value = {
EXIT_REASON_UNKNOWN,
EXIT_REASON_APP_DOES_NOT_SUPPORT_MULTIWINDOW,
@@ -152,7 +151,7 @@
EXIT_REASON_CHILD_TASK_ENTER_PIP,
EXIT_REASON_RECREATE_SPLIT,
EXIT_REASON_FULLSCREEN_SHORTCUT,
- EXIT_REASON_ENTER_DESKTOP
+ EXIT_REASON_DESKTOP_MODE
})
@Retention(RetentionPolicy.SOURCE)
@interface ExitReason{}
@@ -176,7 +175,6 @@
private final ShellTaskOrganizer mTaskOrganizer;
private final SyncTransactionQueue mSyncQueue;
private final Context mContext;
- private final PackageManager mPackageManager;
private final LauncherApps mLauncherApps;
private final RootTaskDisplayAreaOrganizer mRootTDAOrganizer;
private final ShellExecutor mMainExecutor;
@@ -192,9 +190,8 @@
private final LaunchAdjacentController mLaunchAdjacentController;
private final Optional<WindowDecorViewModel> mWindowDecorViewModel;
private final Optional<DesktopTasksController> mDesktopTasksController;
+ private final MultiInstanceHelper mMultiInstanceHelpher;
private final SplitScreenShellCommandHandler mSplitScreenShellCommandHandler;
- // A static allow list of apps which support multi-instance
- private final String[] mAppsSupportingMultiInstance;
@VisibleForTesting
StageCoordinator mStageCoordinator;
@@ -204,6 +201,10 @@
private SurfaceControl mGoingToRecentsTasksLayer;
private SurfaceControl mStartingSplitTasksLayer;
+ /**
+ * @param stageCoordinator if null, a stage coordinator will be created when this controller is
+ * initialized. Can be non-null for testing purposes.
+ */
public SplitScreenController(Context context,
ShellInit shellInit,
ShellCommandHandler shellCommandHandler,
@@ -222,13 +223,14 @@
LaunchAdjacentController launchAdjacentController,
Optional<WindowDecorViewModel> windowDecorViewModel,
Optional<DesktopTasksController> desktopTasksController,
+ @Nullable StageCoordinator stageCoordinator,
+ MultiInstanceHelper multiInstanceHelper,
ShellExecutor mainExecutor) {
mShellCommandHandler = shellCommandHandler;
mShellController = shellController;
mTaskOrganizer = shellTaskOrganizer;
mSyncQueue = syncQueue;
mContext = context;
- mPackageManager = context.getPackageManager();
mLauncherApps = context.getSystemService(LauncherApps.class);
mRootTDAOrganizer = rootTDAOrganizer;
mMainExecutor = mainExecutor;
@@ -243,65 +245,14 @@
mLaunchAdjacentController = launchAdjacentController;
mWindowDecorViewModel = windowDecorViewModel;
mDesktopTasksController = desktopTasksController;
+ mStageCoordinator = stageCoordinator;
+ mMultiInstanceHelpher = multiInstanceHelper;
mSplitScreenShellCommandHandler = new SplitScreenShellCommandHandler(this);
// TODO(b/238217847): Temporarily add this check here until we can remove the dynamic
// override for this controller from the base module
if (ActivityTaskManager.supportsSplitScreenMultiWindow(context)) {
shellInit.addInitCallback(this::onInit, this);
}
-
- // TODO(255224696): Remove the config once having a way for client apps to opt-in
- // multi-instances split.
- mAppsSupportingMultiInstance = mContext.getResources()
- .getStringArray(R.array.config_appsSupportMultiInstancesSplit);
- }
-
- @VisibleForTesting
- SplitScreenController(Context context,
- ShellInit shellInit,
- ShellCommandHandler shellCommandHandler,
- ShellController shellController,
- ShellTaskOrganizer shellTaskOrganizer,
- SyncTransactionQueue syncQueue,
- RootTaskDisplayAreaOrganizer rootTDAOrganizer,
- DisplayController displayController,
- DisplayImeController displayImeController,
- DisplayInsetsController displayInsetsController,
- DragAndDropController dragAndDropController,
- Transitions transitions,
- TransactionPool transactionPool,
- IconProvider iconProvider,
- RecentTasksController recentTasks,
- LaunchAdjacentController launchAdjacentController,
- WindowDecorViewModel windowDecorViewModel,
- DesktopTasksController desktopTasksController,
- ShellExecutor mainExecutor,
- StageCoordinator stageCoordinator,
- String[] appsSupportingMultiInstance) {
- mShellCommandHandler = shellCommandHandler;
- mShellController = shellController;
- mTaskOrganizer = shellTaskOrganizer;
- mSyncQueue = syncQueue;
- mContext = context;
- mPackageManager = context.getPackageManager();
- mLauncherApps = context.getSystemService(LauncherApps.class);
- mRootTDAOrganizer = rootTDAOrganizer;
- mMainExecutor = mainExecutor;
- mDisplayController = displayController;
- mDisplayImeController = displayImeController;
- mDisplayInsetsController = displayInsetsController;
- mDragAndDropController = dragAndDropController;
- mTransitions = transitions;
- mTransactionPool = transactionPool;
- mIconProvider = iconProvider;
- mRecentTasksOptional = Optional.of(recentTasks);
- mLaunchAdjacentController = launchAdjacentController;
- mWindowDecorViewModel = Optional.of(windowDecorViewModel);
- mDesktopTasksController = Optional.of(desktopTasksController);
- mStageCoordinator = stageCoordinator;
- mSplitScreenShellCommandHandler = new SplitScreenShellCommandHandler(this);
- shellInit.addInitCallback(this::onInit, this);
- mAppsSupportingMultiInstance = appsSupportingMultiInstance;
}
public SplitScreen asSplitScreen() {
@@ -529,8 +480,8 @@
}
/** Move the specified task to fullscreen, regardless of focus state. */
- public void moveTaskToFullscreen(int taskId) {
- mStageCoordinator.moveTaskToFullscreen(taskId);
+ public void moveTaskToFullscreen(int taskId, int exitReason) {
+ mStageCoordinator.moveTaskToFullscreen(taskId, exitReason);
}
public boolean isLaunchToSplit(TaskInfo taskInfo) {
@@ -613,8 +564,8 @@
if (samePackage(packageName, getPackageName(reverseSplitPosition(position)),
user.getIdentifier(), getUserId(reverseSplitPosition(position)))) {
- if (supportsMultiInstanceSplit(getShortcutComponent(packageName, shortcutId, user,
- mLauncherApps))) {
+ if (mMultiInstanceHelpher.supportsMultiInstanceSplit(
+ getShortcutComponent(packageName, shortcutId, user, mLauncherApps))) {
activityOptions.setApplyMultipleTaskFlagForShortcut(true);
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
} else if (isSplitScreenVisible()) {
@@ -647,7 +598,7 @@
final int userId1 = shortcutInfo.getUserId();
final int userId2 = SplitScreenUtils.getUserId(taskId, mTaskOrganizer);
if (samePackage(packageName1, packageName2, userId1, userId2)) {
- if (supportsMultiInstanceSplit(shortcutInfo.getActivity())) {
+ if (mMultiInstanceHelpher.supportsMultiInstanceSplit(shortcutInfo.getActivity())) {
activityOptions.setApplyMultipleTaskFlagForShortcut(true);
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
} else {
@@ -679,7 +630,7 @@
final int userId1 = shortcutInfo.getUserId();
final int userId2 = SplitScreenUtils.getUserId(taskId, mTaskOrganizer);
if (samePackage(packageName1, packageName2, userId1, userId2)) {
- if (supportsMultiInstanceSplit(shortcutInfo.getActivity())) {
+ if (mMultiInstanceHelpher.supportsMultiInstanceSplit(shortcutInfo.getActivity())) {
activityOptions.setApplyMultipleTaskFlagForShortcut(true);
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
} else {
@@ -718,7 +669,7 @@
final String packageName2 = SplitScreenUtils.getPackageName(taskId, mTaskOrganizer);
final int userId2 = SplitScreenUtils.getUserId(taskId, mTaskOrganizer);
if (samePackage(packageName1, packageName2, userId1, userId2)) {
- if (supportsMultiInstanceSplit(getComponent(pendingIntent))) {
+ if (mMultiInstanceHelpher.supportsMultiInstanceSplit(getComponent(pendingIntent))) {
fillInIntent = new Intent();
fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
@@ -748,7 +699,7 @@
final int userId2 = SplitScreenUtils.getUserId(taskId, mTaskOrganizer);
boolean setSecondIntentMultipleTask = false;
if (samePackage(packageName1, packageName2, userId1, userId2)) {
- if (supportsMultiInstanceSplit(getComponent(pendingIntent))) {
+ if (mMultiInstanceHelpher.supportsMultiInstanceSplit(getComponent(pendingIntent))) {
setSecondIntentMultipleTask = true;
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
} else {
@@ -783,7 +734,7 @@
final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent1);
final String packageName2 = SplitScreenUtils.getPackageName(pendingIntent2);
if (samePackage(packageName1, packageName2, userId1, userId2)) {
- if (supportsMultiInstanceSplit(getComponent(pendingIntent1))) {
+ if (mMultiInstanceHelpher.supportsMultiInstanceSplit(getComponent(pendingIntent1))) {
fillInIntent1 = new Intent();
fillInIntent1.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
fillInIntent2 = new Intent();
@@ -820,7 +771,7 @@
? ActivityOptions.fromBundle(options2) : ActivityOptions.makeBasic();
boolean setSecondIntentMultipleTask = false;
if (samePackage(packageName1, packageName2, userId1, userId2)) {
- if (supportsMultiInstanceSplit(getComponent(pendingIntent1))) {
+ if (mMultiInstanceHelpher.supportsMultiInstanceSplit(getComponent(pendingIntent1))) {
fillInIntent1 = new Intent();
fillInIntent1.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
setSecondIntentMultipleTask = true;
@@ -882,7 +833,7 @@
return;
}
if (samePackage(packageName1, packageName2, userId1, userId2)) {
- if (supportsMultiInstanceSplit(getComponent(intent))) {
+ if (mMultiInstanceHelpher.supportsMultiInstanceSplit(getComponent(intent))) {
// Flag with MULTIPLE_TASK if this is launching the same activity into both sides of
// the split and there is no reusable background task.
fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
@@ -942,66 +893,6 @@
}
/**
- * Returns whether a specific component desires to be launched in multiple instances for
- * split screen.
- */
- @VisibleForTesting
- boolean supportsMultiInstanceSplit(@Nullable ComponentName componentName) {
- if (componentName == null || componentName.getPackageName() == null) {
- // TODO(b/262864589): Handle empty component case
- return false;
- }
-
- // Check the pre-defined allow list
- final String packageName = componentName.getPackageName();
- for (int i = 0; i < mAppsSupportingMultiInstance.length; i++) {
- if (mAppsSupportingMultiInstance[i].equals(packageName)) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
- "application=%s in allowlist supports multi-instance", packageName);
- return true;
- }
- }
-
- // Check the activity property first
- try {
- final PackageManager.Property activityProp = mPackageManager.getProperty(
- PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, componentName);
- // If the above call doesn't throw a NameNotFoundException, then the activity property
- // should override the application property value
- if (activityProp.isBoolean()) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
- "activity=%s supports multi-instance", componentName);
- return activityProp.getBoolean();
- } else {
- ProtoLog.w(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
- "Warning: property=%s for activity=%s has non-bool type=%d",
- PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, packageName,
- activityProp.getType());
- }
- } catch (PackageManager.NameNotFoundException nnfe) {
- // Not specified in the activity, fall through
- }
-
- // Check the application property otherwise
- try {
- final PackageManager.Property appProp = mPackageManager.getProperty(
- PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, packageName);
- if (appProp.isBoolean()) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
- "application=%s supports multi-instance", packageName);
- return appProp.getBoolean();
- } else {
- ProtoLog.w(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
- "Warning: property=%s for application=%s has non-bool type=%d",
- PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI, packageName, appProp.getType());
- }
- } catch (PackageManager.NameNotFoundException nnfe) {
- // Not specified in either application or activity
- }
- return false;
- }
-
- /**
* Determines whether the widgetIntent needs to be modified if multiple tasks of its
* corresponding package/app are supported. There are 4 possible paths:
* <li> We select a widget for second app which is the same as the first app </li>
@@ -1144,8 +1035,8 @@
return "CHILD_TASK_ENTER_PIP";
case EXIT_REASON_RECREATE_SPLIT:
return "RECREATE_SPLIT";
- case EXIT_REASON_ENTER_DESKTOP:
- return "ENTER_DESKTOP";
+ case EXIT_REASON_DESKTOP_MODE:
+ return "DESKTOP_MODE";
default:
return "unknown reason, reason int = " + exitReason;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java
index f4ab226..a0bf843 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitscreenEventLogger.java
@@ -25,7 +25,7 @@
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__DEVICE_FOLDED;
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__DRAG_DIVIDER;
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__FULLSCREEN_SHORTCUT;
-import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__ENTER_DESKTOP;
+import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__DESKTOP_MODE;
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__RECREATE_SPLIT;
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__RETURN_HOME;
import static com.android.internal.util.FrameworkStatsLog.SPLITSCREEN_UICHANGED__EXIT_REASON__ROOT_TASK_VANISHED;
@@ -43,7 +43,7 @@
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_CHILD_TASK_ENTER_PIP;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DEVICE_FOLDED;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER;
-import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_ENTER_DESKTOP;
+import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DESKTOP_MODE;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_FULLSCREEN_SHORTCUT;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_RECREATE_SPLIT;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_RETURN_HOME;
@@ -194,8 +194,8 @@
return SPLITSCREEN_UICHANGED__EXIT_REASON__RECREATE_SPLIT;
case EXIT_REASON_FULLSCREEN_SHORTCUT:
return SPLITSCREEN_UICHANGED__EXIT_REASON__FULLSCREEN_SHORTCUT;
- case EXIT_REASON_ENTER_DESKTOP:
- return SPLITSCREEN_UICHANGED__EXIT_REASON__ENTER_DESKTOP;
+ case EXIT_REASON_DESKTOP_MODE:
+ return SPLITSCREEN_UICHANGED__EXIT_REASON__DESKTOP_MODE;
case EXIT_REASON_UNKNOWN:
// Fall through
default:
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 70b2f21..fa14b4c 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
@@ -2973,7 +2973,7 @@
}
/** Move the specified task to fullscreen, regardless of focus state. */
- public void moveTaskToFullscreen(int taskId) {
+ public void moveTaskToFullscreen(int taskId, int exitReason) {
boolean leftOrTop;
if (mMainStage.containsTask(taskId)) {
leftOrTop = (mSideStagePosition == SPLIT_POSITION_TOP_OR_LEFT);
@@ -2982,7 +2982,7 @@
} else {
return;
}
- mSplitLayout.flingDividerToDismiss(!leftOrTop, EXIT_REASON_FULLSCREEN_SHORTCUT);
+ mSplitLayout.flingDividerToDismiss(!leftOrTop, exitReason);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java
index aec4d11..e330f3a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/tv/TvSplitScreenController.java
@@ -28,6 +28,7 @@
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.LaunchAdjacentController;
+import com.android.wm.shell.common.MultiInstanceHelper;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.SystemWindows;
@@ -79,6 +80,7 @@
IconProvider iconProvider,
Optional<RecentTasksController> recentTasks,
LaunchAdjacentController launchAdjacentController,
+ MultiInstanceHelper multiInstanceHelper,
ShellExecutor mainExecutor,
Handler mainHandler,
SystemWindows systemWindows) {
@@ -86,7 +88,7 @@
syncQueue, rootTDAOrganizer, displayController, displayImeController,
displayInsetsController, null, transitions, transactionPool,
iconProvider, recentTasks, launchAdjacentController, Optional.empty(),
- Optional.empty(), mainExecutor);
+ Optional.empty(), null /* stageCoordinator */, multiInstanceHelper, mainExecutor);
mTaskOrganizer = shellTaskOrganizer;
mSyncQueue = syncQueue;
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 7db3d38..c713a2e 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
@@ -339,7 +339,8 @@
final int id = v.getId();
if (id == R.id.close_window) {
if (isTaskInSplitScreen(mTaskId)) {
- mSplitScreenController.moveTaskToFullscreen(getOtherSplitTask(mTaskId).taskId);
+ mSplitScreenController.moveTaskToFullscreen(getOtherSplitTask(mTaskId).taskId,
+ SplitScreenController.EXIT_REASON_DESKTOP_MODE);
} else {
mTaskOperations.closeTask(mTaskToken);
}
@@ -365,7 +366,8 @@
} else if (id == R.id.fullscreen_button) {
decoration.closeHandleMenu();
if (isTaskInSplitScreen(mTaskId)) {
- mSplitScreenController.moveTaskToFullscreen(mTaskId);
+ mSplitScreenController.moveTaskToFullscreen(mTaskId,
+ SplitScreenController.EXIT_REASON_DESKTOP_MODE);
} else {
mDesktopTasksController.ifPresent(c ->
c.moveToFullscreen(mTaskId));
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
index 7c6e69e..5c69d55 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/VeiledResizeTaskPositioner.java
@@ -61,7 +61,6 @@
private int mCtrlType;
private boolean mIsResizingOrAnimatingResize;
@Surface.Rotation private int mRotation;
- private boolean mVeilIsVisible;
public VeiledResizeTaskPositioner(ShellTaskOrganizer taskOrganizer,
DesktopModeWindowDecoration windowDecoration,
@@ -119,10 +118,9 @@
if (isResizing() && DragPositioningCallbackUtility.changeBounds(mCtrlType,
mRepositionTaskBounds, mTaskBoundsAtDragStart, mStableBounds, delta,
mDisplayController, mDesktopWindowDecoration)) {
- mIsResizingOrAnimatingResize = true;
- if (!mVeilIsVisible) {
+ if (!mIsResizingOrAnimatingResize) {
mDesktopWindowDecoration.showResizeVeil(mRepositionTaskBounds);
- mVeilIsVisible = true;
+ mIsResizingOrAnimatingResize = true;
} else {
mDesktopWindowDecoration.updateResizeVeil(mRepositionTaskBounds);
}
@@ -148,11 +146,10 @@
final WindowContainerTransaction wct = new WindowContainerTransaction();
wct.setBounds(mDesktopWindowDecoration.mTaskInfo.token, mRepositionTaskBounds);
mTransitions.startTransition(TRANSIT_CHANGE, wct, this);
- } else if (mVeilIsVisible) {
+ } else {
// If bounds haven't changed, perform necessary veil reset here as startAnimation
// won't be called.
- mDesktopWindowDecoration.hideResizeVeil();
- mIsResizingOrAnimatingResize = false;
+ resetVeilIfVisible();
}
} else if (DragPositioningCallbackUtility.isBelowDisallowedArea(
mDisallowedAreaForEndBoundsHeight, mTaskBoundsAtDragStart, mRepositionStartPoint,
@@ -168,7 +165,6 @@
mCtrlType = CTRL_TYPE_UNDEFINED;
mTaskBoundsAtDragStart.setEmpty();
mRepositionStartPoint.set(0, 0);
- mVeilIsVisible = false;
return new Rect(mRepositionTaskBounds);
}
@@ -177,6 +173,13 @@
|| (mCtrlType & CTRL_TYPE_LEFT) != 0 || (mCtrlType & CTRL_TYPE_RIGHT) != 0;
}
+ private void resetVeilIfVisible() {
+ if (mIsResizingOrAnimatingResize) {
+ mDesktopWindowDecoration.hideResizeVeil();
+ mIsResizingOrAnimatingResize = false;
+ }
+ }
+
@Override
public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction startTransaction,
@@ -192,7 +195,7 @@
}
startTransaction.apply();
- mDesktopWindowDecoration.hideResizeVeil();
+ resetVeilIfVisible();
mCtrlType = CTRL_TYPE_UNDEFINED;
finishCallback.onTransitionFinished(null);
mIsResizingOrAnimatingResize = false;
diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt
index f3145c9..e6a2022 100644
--- a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt
@@ -113,7 +113,6 @@
subject.name.contains(primaryApp.toLayerName()) && subject.isVisible
}
.mapNotNull { primaryApp -> primaryApp.layer.visibleRegion }
- .toTypedArray()
val primaryAppRegionArea = RegionSubject(primaryAppRegions, it.timestamp)
it.visibleRegion(secondaryApp).notOverlaps(primaryAppRegionArea.region)
diff --git a/libs/WindowManager/Shell/tests/unittest/AndroidManifest.xml b/libs/WindowManager/Shell/tests/unittest/AndroidManifest.xml
index 47a116b..2ef425c 100644
--- a/libs/WindowManager/Shell/tests/unittest/AndroidManifest.xml
+++ b/libs/WindowManager/Shell/tests/unittest/AndroidManifest.xml
@@ -21,6 +21,7 @@
<uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
<uses-permission android:name="android.permission.VIBRATE"/>
+ <uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS"/>
<application android:debuggable="true" android:largeHeap="true">
<uses-library android:name="android.test.mock" />
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/MultiInstanceHelperTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/MultiInstanceHelperTest.kt
new file mode 100644
index 0000000..2f5fe11
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/MultiInstanceHelperTest.kt
@@ -0,0 +1,171 @@
+/*
+ * 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.common
+
+import android.app.ActivityTaskManager
+import android.content.ComponentName
+import android.content.pm.LauncherApps
+import android.content.pm.PackageManager
+import android.content.pm.ShortcutInfo
+import android.os.UserHandle
+import android.view.WindowManager.PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.wm.shell.ShellTestCase
+import org.junit.Assert.assertEquals
+import org.junit.Assume.assumeTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.doThrow
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+
+@RunWith(AndroidJUnit4::class)
+class MultiInstanceHelperTest : ShellTestCase() {
+
+ @Before
+ fun setup() {
+ assumeTrue(ActivityTaskManager.supportsSplitScreenMultiWindow(mContext))
+ }
+
+ @Test
+ fun getShortcutComponent_nullShortcuts() {
+ val launcherApps = mock<LauncherApps>()
+ whenever(launcherApps.getShortcuts(ArgumentMatchers.any(), ArgumentMatchers.any()))
+ .thenReturn(null)
+ assertEquals(null, MultiInstanceHelper.getShortcutComponent(TEST_PACKAGE,
+ TEST_SHORTCUT_ID, UserHandle.CURRENT, launcherApps))
+ }
+
+ @Test
+ fun getShortcutComponent_noShortcuts() {
+ val launcherApps = mock<LauncherApps>()
+ whenever(launcherApps.getShortcuts(ArgumentMatchers.any(), ArgumentMatchers.any()))
+ .thenReturn(ArrayList<ShortcutInfo>())
+ assertEquals(null, MultiInstanceHelper.getShortcutComponent(TEST_PACKAGE,
+ TEST_SHORTCUT_ID, UserHandle.CURRENT, launcherApps))
+ }
+
+ @Test
+ fun getShortcutComponent_validShortcut() {
+ val component = ComponentName(TEST_PACKAGE, TEST_ACTIVITY)
+ val shortcutInfo = ShortcutInfo.Builder(context, "id").setActivity(component).build()
+ val launcherApps = mock<LauncherApps>()
+ whenever(launcherApps.getShortcuts(ArgumentMatchers.any(), ArgumentMatchers.any()))
+ .thenReturn(arrayListOf(shortcutInfo))
+ assertEquals(component, MultiInstanceHelper.getShortcutComponent(TEST_PACKAGE,
+ TEST_SHORTCUT_ID, UserHandle.CURRENT, launcherApps))
+ }
+
+ @Test
+ fun supportsMultiInstanceSplit_inStaticAllowList() {
+ val allowList = arrayOf(TEST_PACKAGE)
+ val helper = MultiInstanceHelper(mContext, context.packageManager, allowList)
+ val component = ComponentName(TEST_PACKAGE, TEST_ACTIVITY)
+ assertEquals(true, helper.supportsMultiInstanceSplit(component))
+ }
+
+ @Test
+ fun supportsMultiInstanceSplit_notInStaticAllowList() {
+ val allowList = arrayOf(TEST_PACKAGE)
+ val helper = MultiInstanceHelper(mContext, context.packageManager, allowList)
+ val component = ComponentName(TEST_NOT_ALLOWED_PACKAGE, TEST_ACTIVITY)
+ assertEquals(false, helper.supportsMultiInstanceSplit(component))
+ }
+
+ @Test
+ @Throws(PackageManager.NameNotFoundException::class)
+ fun supportsMultiInstanceSplit_activityPropertyTrue() {
+ val component = ComponentName(TEST_PACKAGE, TEST_ACTIVITY)
+ val pm = mock<PackageManager>()
+ val activityProp = PackageManager.Property("", true, "", "")
+ whenever(pm.getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
+ eq(component)))
+ .thenReturn(activityProp)
+ val appProp = PackageManager.Property("", false, "", "")
+ whenever(pm.getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
+ eq(component.packageName)))
+ .thenReturn(appProp)
+
+ val helper = MultiInstanceHelper(mContext, pm, emptyArray())
+ // Expect activity property to override application property
+ assertEquals(true, helper.supportsMultiInstanceSplit(component))
+ }
+
+ @Test
+ @Throws(PackageManager.NameNotFoundException::class)
+ fun supportsMultiInstanceSplit_activityPropertyFalseApplicationPropertyTrue() {
+ val component = ComponentName(TEST_PACKAGE, TEST_ACTIVITY)
+ val pm = mock<PackageManager>()
+ val activityProp = PackageManager.Property("", false, "", "")
+ whenever(pm.getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
+ eq(component)))
+ .thenReturn(activityProp)
+ val appProp = PackageManager.Property("", true, "", "")
+ whenever(pm.getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
+ eq(component.packageName)))
+ .thenReturn(appProp)
+
+ val helper = MultiInstanceHelper(mContext, pm, emptyArray())
+ // Expect activity property to override application property
+ assertEquals(false, helper.supportsMultiInstanceSplit(component))
+ }
+
+ @Test
+ @Throws(PackageManager.NameNotFoundException::class)
+ fun supportsMultiInstanceSplit_noActivityPropertyApplicationPropertyTrue() {
+ val component = ComponentName(TEST_PACKAGE, TEST_ACTIVITY)
+ val pm = mock<PackageManager>()
+ whenever(pm.getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
+ eq(component)))
+ .thenThrow(PackageManager.NameNotFoundException())
+ val appProp = PackageManager.Property("", true, "", "")
+ whenever(pm.getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
+ eq(component.packageName)))
+ .thenReturn(appProp)
+
+ val helper = MultiInstanceHelper(mContext, pm, emptyArray())
+ // Expect fall through to app property
+ assertEquals(true, helper.supportsMultiInstanceSplit(component))
+ }
+
+ @Test
+ @Throws(PackageManager.NameNotFoundException::class)
+ fun supportsMultiInstanceSplit_noActivityOrAppProperty() {
+ val component = ComponentName(TEST_PACKAGE, TEST_ACTIVITY)
+ val pm = mock<PackageManager>()
+ whenever(pm.getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
+ eq(component)))
+ .thenThrow(PackageManager.NameNotFoundException())
+ whenever(pm.getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
+ eq(component.packageName)))
+ .thenThrow(PackageManager.NameNotFoundException())
+
+ val helper = MultiInstanceHelper(mContext, pm, emptyArray())
+ assertEquals(false, helper.supportsMultiInstanceSplit(component))
+ }
+
+ companion object {
+ val TEST_PACKAGE = "com.android.wm.shell.common"
+ val TEST_NOT_ALLOWED_PACKAGE = "com.android.wm.shell.common.fake";
+ val TEST_ACTIVITY = "TestActivity";
+ val TEST_SHORTCUT_ID = "test_shortcut_1"
+ }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitScreenUtilsTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitScreenUtilsTests.kt
deleted file mode 100644
index 955660c..0000000
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/SplitScreenUtilsTests.kt
+++ /dev/null
@@ -1,69 +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.wm.shell.common.split
-
-import android.content.ComponentName
-import android.content.pm.LauncherApps
-import android.content.pm.ShortcutInfo
-import android.os.UserHandle
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import com.android.wm.shell.ShellTestCase
-import org.junit.Assert.assertEquals
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.any
-import org.mockito.Mockito.mock
-import org.mockito.Mockito.`when`
-
-@RunWith(AndroidJUnit4::class)
-class SplitScreenUtilsTests : ShellTestCase() {
-
- @Test
- fun getShortcutComponent_nullShortcuts() {
- val launcherApps = mock(LauncherApps::class.java).also {
- `when`(it.getShortcuts(any(), any())).thenReturn(null)
- }
- assertEquals(null, SplitScreenUtils.getShortcutComponent(TEST_PACKAGE,
- TEST_SHORTCUT_ID, UserHandle.CURRENT, launcherApps))
- }
-
- @Test
- fun getShortcutComponent_noShortcuts() {
- val launcherApps = mock(LauncherApps::class.java).also {
- `when`(it.getShortcuts(any(), any())).thenReturn(ArrayList<ShortcutInfo>())
- }
- assertEquals(null, SplitScreenUtils.getShortcutComponent(TEST_PACKAGE,
- TEST_SHORTCUT_ID, UserHandle.CURRENT, launcherApps))
- }
-
- @Test
- fun getShortcutComponent_validShortcut() {
- val component = ComponentName(TEST_PACKAGE, TEST_ACTIVITY)
- val shortcutInfo = ShortcutInfo.Builder(context, "id").setActivity(component).build()
- val launcherApps = mock(LauncherApps::class.java).also {
- `when`(it.getShortcuts(any(), any())).thenReturn(arrayListOf(shortcutInfo))
- }
- assertEquals(component, SplitScreenUtils.getShortcutComponent(TEST_PACKAGE,
- TEST_SHORTCUT_ID, UserHandle.CURRENT, launcherApps))
- }
-
- companion object {
- val TEST_PACKAGE = "com.android.wm.shell.common.split"
- val TEST_ACTIVITY = "TestActivity";
- val TEST_SHORTCUT_ID = "test_shortcut_1"
- }
-}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java
index 9fe2cb1..81ba4b3 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/UserAspectRatioSettingsWindowManagerTest.java
@@ -113,8 +113,22 @@
mExecutor = new TestShellExecutor();
mTaskInfo = createTaskInfo(/* eligibleForUserAspectRatioButton= */
false, /* topActivityBoundsLetterboxed */ true, ACTION_MAIN, CATEGORY_LAUNCHER);
+ final DisplayInfo displayInfo = new DisplayInfo();
+ final int displayWidth = 1000;
+ final int displayHeight = 1200;
+ displayInfo.logicalWidth = displayWidth;
+ displayInfo.logicalHeight = displayHeight;
+ final DisplayLayout displayLayout = new DisplayLayout(displayInfo,
+ mContext.getResources(), /* hasNavigationBar= */ true, /* hasStatusBar= */ false);
+ InsetsState insetsState = new InsetsState();
+ insetsState.setDisplayFrame(new Rect(0, 0, displayWidth, displayHeight));
+ InsetsSource insetsSource = new InsetsSource(
+ InsetsSource.createId(null, 0, navigationBars()), navigationBars());
+ insetsSource.setFrame(0, displayHeight - 200, displayWidth, displayHeight);
+ insetsState.addSource(insetsSource);
+ displayLayout.setInsets(mContext.getResources(), insetsState);
mWindowManager = new UserAspectRatioSettingsWindowManager(mContext, mTaskInfo,
- mSyncTransactionQueue, mTaskListener, new DisplayLayout(), new CompatUIHintsState(),
+ mSyncTransactionQueue, mTaskListener, displayLayout, new CompatUIHintsState(),
mOnUserAspectRatioSettingsButtonClicked, mExecutor, flags -> 0,
mUserAspectRatioButtonShownChecker, s -> {});
spyOn(mWindowManager);
@@ -253,6 +267,31 @@
}
@Test
+ public void testEligibleButtonHiddenIfLetterboxBoundsEqualToStableBounds() {
+ TaskInfo taskInfo = createTaskInfo(/* eligibleForUserAspectRatioButton= */
+ true, /* topActivityBoundsLetterboxed */ true, ACTION_MAIN, CATEGORY_LAUNCHER);
+
+ final Rect stableBounds = mWindowManager.getTaskStableBounds();
+ final int stableHeight = stableBounds.height();
+
+ // Letterboxed activity bounds equal to stable bounds, layout shouldn't be inflated
+ taskInfo.appCompatTaskInfo.topActivityLetterboxHeight = stableHeight;
+ taskInfo.appCompatTaskInfo.topActivityLetterboxWidth = stableBounds.width();
+
+ mWindowManager.updateCompatInfo(taskInfo, mTaskListener, /* canShow= */ true);
+
+ verify(mWindowManager, never()).inflateLayout();
+
+ // Letterboxed activity bounds smaller than stable bounds, layout should be inflated
+ taskInfo.appCompatTaskInfo.topActivityLetterboxHeight = stableHeight - 100;
+
+ clearInvocations(mWindowManager);
+ mWindowManager.updateCompatInfo(taskInfo, mTaskListener, /* canShow= */ true);
+
+ verify(mWindowManager).inflateLayout();
+ }
+
+ @Test
public void testUpdateDisplayLayout() {
final DisplayInfo displayInfo = new DisplayInfo();
displayInfo.logicalWidth = 1000;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index 79634e6..cb64c52 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -373,7 +373,7 @@
assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
.isEqualTo(WINDOWING_MODE_FREEFORM)
verify(splitScreenController).prepareExitSplitScreen(any(), anyInt(),
- eq(SplitScreenController.EXIT_REASON_ENTER_DESKTOP)
+ eq(SplitScreenController.EXIT_REASON_DESKTOP_MODE)
)
}
@@ -385,7 +385,7 @@
assertThat(wct.changes[task.token.asBinder()]?.windowingMode)
.isEqualTo(WINDOWING_MODE_FREEFORM)
verify(splitScreenController, never()).prepareExitSplitScreen(any(), anyInt(),
- eq(SplitScreenController.EXIT_REASON_ENTER_DESKTOP)
+ eq(SplitScreenController.EXIT_REASON_DESKTOP_MODE)
)
}
@@ -734,6 +734,46 @@
shellExecutor.flushAll()
verify(launchAdjacentController).launchAdjacentEnabled = true
}
+ @Test
+ fun enterDesktop_fullscreenTaskIsMovedToDesktop() {
+ val task1 = setUpFullscreenTask()
+ val task2 = setUpFullscreenTask()
+ val task3 = setUpFullscreenTask()
+
+ task1.isFocused = true
+ task2.isFocused = false
+ task3.isFocused = false
+
+ controller.enterDesktop(DEFAULT_DISPLAY)
+
+ val wct = getLatestMoveToDesktopWct()
+ assertThat(wct.changes[task1.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FREEFORM)
+ }
+
+ @Test
+ fun enterDesktop_splitScreenTaskIsMovedToDesktop() {
+ val task1 = setUpSplitScreenTask()
+ val task2 = setUpFullscreenTask()
+ val task3 = setUpFullscreenTask()
+ val task4 = setUpSplitScreenTask()
+
+ task1.isFocused = true
+ task2.isFocused = false
+ task3.isFocused = false
+ task4.isFocused = true
+
+ task4.parentTaskId = task1.taskId
+
+ controller.enterDesktop(DEFAULT_DISPLAY)
+
+ val wct = getLatestMoveToDesktopWct()
+ assertThat(wct.changes[task4.token.asBinder()]?.windowingMode)
+ .isEqualTo(WINDOWING_MODE_FREEFORM)
+ verify(splitScreenController).prepareExitSplitScreen(any(), anyInt(),
+ eq(SplitScreenController.EXIT_REASON_DESKTOP_MODE)
+ )
+ }
private fun setUpFreeformTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
val task = createFreeformTask(displayId)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
index 7f3bfbb..315d97e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitScreenControllerTests.java
@@ -22,7 +22,6 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NO_USER_ACTION;
-import static android.view.WindowManager.PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
@@ -37,8 +36,6 @@
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
@@ -49,10 +46,8 @@
import android.app.ActivityTaskManager;
import android.app.PendingIntent;
import android.content.ComponentName;
-import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
import android.os.Bundle;
import androidx.test.annotation.UiThreadTest;
@@ -60,7 +55,6 @@
import androidx.test.filters.SmallTest;
import com.android.launcher3.icons.IconProvider;
-import com.android.wm.shell.R;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.ShellTestCase;
@@ -69,6 +63,7 @@
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.LaunchAdjacentController;
+import com.android.wm.shell.common.MultiInstanceHelper;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.TransactionPool;
@@ -90,6 +85,8 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.Optional;
+
/**
* Tests for {@link SplitScreenController}
*/
@@ -97,10 +94,6 @@
@RunWith(AndroidJUnit4.class)
public class SplitScreenControllerTests extends ShellTestCase {
- private static final String TEST_PACKAGE = "com.android.wm.shell.splitscreen";
- private static final String TEST_NOT_ALLOWED_PACKAGE = "com.android.wm.shell.splitscreen.fake";
- private static final String TEST_ACTIVITY = "TestActivity";
-
@Mock ShellInit mShellInit;
@Mock ShellCommandHandler mShellCommandHandler;
@Mock ShellTaskOrganizer mTaskOrganizer;
@@ -119,6 +112,7 @@
@Mock LaunchAdjacentController mLaunchAdjacentController;
@Mock WindowDecorViewModel mWindowDecorViewModel;
@Mock DesktopTasksController mDesktopTasksController;
+ @Mock MultiInstanceHelper mMultiInstanceHelper;
@Captor ArgumentCaptor<Intent> mIntentCaptor;
private ShellController mShellController;
@@ -128,17 +122,15 @@
public void setup() {
assumeTrue(ActivityTaskManager.supportsSplitScreenMultiWindow(mContext));
MockitoAnnotations.initMocks(this);
- String[] appsSupportingMultiInstance = mContext.getResources()
- .getStringArray(R.array.config_appsSupportMultiInstancesSplit);
mShellController = spy(new ShellController(mContext, mShellInit, mShellCommandHandler,
mMainExecutor));
mSplitScreenController = spy(new SplitScreenController(mContext, mShellInit,
mShellCommandHandler, mShellController, mTaskOrganizer, mSyncQueue,
mRootTDAOrganizer, mDisplayController, mDisplayImeController,
mDisplayInsetsController, mDragAndDropController, mTransitions, mTransactionPool,
- mIconProvider, mRecentTasks, mLaunchAdjacentController, mWindowDecorViewModel,
- mDesktopTasksController, mMainExecutor, mStageCoordinator,
- appsSupportingMultiInstance));
+ mIconProvider, Optional.of(mRecentTasks), mLaunchAdjacentController,
+ Optional.of(mWindowDecorViewModel), Optional.of(mDesktopTasksController),
+ mStageCoordinator, mMultiInstanceHelper, mMainExecutor));
}
@Test
@@ -213,7 +205,7 @@
@Test
public void startIntent_multiInstancesSupported_appendsMultipleTaskFag() {
- doReturn(true).when(mSplitScreenController).supportsMultiInstanceSplit(any());
+ doReturn(true).when(mMultiInstanceHelper).supportsMultiInstanceSplit(any());
Intent startIntent = createStartIntent("startActivity");
PendingIntent pendingIntent =
PendingIntent.getActivity(mContext, 0, startIntent, FLAG_IMMUTABLE);
@@ -250,13 +242,13 @@
verify(mStageCoordinator).startTask(anyInt(), eq(SPLIT_POSITION_TOP_OR_LEFT),
isNull());
- verify(mSplitScreenController, never()).supportsMultiInstanceSplit(any());
+ verify(mMultiInstanceHelper, never()).supportsMultiInstanceSplit(any());
verify(mStageCoordinator, never()).switchSplitPosition(any());
}
@Test
public void startIntent_multiInstancesSupported_startTaskInBackgroundAfterSplitActivated() {
- doReturn(true).when(mSplitScreenController).supportsMultiInstanceSplit(any());
+ doReturn(true).when(mMultiInstanceHelper).supportsMultiInstanceSplit(any());
doNothing().when(mSplitScreenController).startTask(anyInt(), anyInt(), any());
Intent startIntent = createStartIntent("startActivity");
PendingIntent pendingIntent =
@@ -273,14 +265,14 @@
mSplitScreenController.startIntent(pendingIntent, mContext.getUserId(), null,
SPLIT_POSITION_TOP_OR_LEFT, null);
- verify(mSplitScreenController, never()).supportsMultiInstanceSplit(any());
+ verify(mMultiInstanceHelper, never()).supportsMultiInstanceSplit(any());
verify(mStageCoordinator).startTask(anyInt(), eq(SPLIT_POSITION_TOP_OR_LEFT),
isNull());
}
@Test
public void startIntent_multiInstancesNotSupported_switchesPositionAfterSplitActivated() {
- doReturn(false).when(mSplitScreenController).supportsMultiInstanceSplit(any());
+ doReturn(false).when(mMultiInstanceHelper).supportsMultiInstanceSplit(any());
Intent startIntent = createStartIntent("startActivity");
PendingIntent pendingIntent =
PendingIntent.getActivity(mContext, 0, startIntent, FLAG_IMMUTABLE);
@@ -298,130 +290,6 @@
}
@Test
- public void supportsMultiInstanceSplit_inStaticAllowList() {
- String[] allowList = { TEST_PACKAGE };
- SplitScreenController controller = new SplitScreenController(mContext, mShellInit,
- mShellCommandHandler, mShellController, mTaskOrganizer, mSyncQueue,
- mRootTDAOrganizer, mDisplayController, mDisplayImeController,
- mDisplayInsetsController, mDragAndDropController, mTransitions, mTransactionPool,
- mIconProvider, mRecentTasks, mLaunchAdjacentController, mWindowDecorViewModel,
- mDesktopTasksController, mMainExecutor, mStageCoordinator,
- allowList);
- ComponentName component = new ComponentName(TEST_PACKAGE, TEST_ACTIVITY);
- assertEquals(true, controller.supportsMultiInstanceSplit(component));
- }
-
- @Test
- public void supportsMultiInstanceSplit_notInStaticAllowList() {
- String[] allowList = { TEST_PACKAGE };
- SplitScreenController controller = new SplitScreenController(mContext, mShellInit,
- mShellCommandHandler, mShellController, mTaskOrganizer, mSyncQueue,
- mRootTDAOrganizer, mDisplayController, mDisplayImeController,
- mDisplayInsetsController, mDragAndDropController, mTransitions, mTransactionPool,
- mIconProvider, mRecentTasks, mLaunchAdjacentController, mWindowDecorViewModel,
- mDesktopTasksController, mMainExecutor, mStageCoordinator,
- allowList);
- ComponentName component = new ComponentName(TEST_NOT_ALLOWED_PACKAGE, TEST_ACTIVITY);
- assertEquals(false, controller.supportsMultiInstanceSplit(component));
- }
-
- @Test
- public void supportsMultiInstanceSplit_activityPropertyTrue()
- throws PackageManager.NameNotFoundException {
- Context context = spy(mContext);
- ComponentName component = new ComponentName(TEST_PACKAGE, TEST_ACTIVITY);
- PackageManager pm = mock(PackageManager.class);
- doReturn(pm).when(context).getPackageManager();
- PackageManager.Property activityProp = new PackageManager.Property("", true, "", "");
- doReturn(activityProp).when(pm).getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
- eq(component));
- PackageManager.Property appProp = new PackageManager.Property("", false, "", "");
- doReturn(appProp).when(pm).getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
- eq(component.getPackageName()));
-
- SplitScreenController controller = new SplitScreenController(context, mShellInit,
- mShellCommandHandler, mShellController, mTaskOrganizer, mSyncQueue,
- mRootTDAOrganizer, mDisplayController, mDisplayImeController,
- mDisplayInsetsController, mDragAndDropController, mTransitions, mTransactionPool,
- mIconProvider, mRecentTasks, mLaunchAdjacentController, mWindowDecorViewModel,
- mDesktopTasksController, mMainExecutor, mStageCoordinator,
- new String[0]);
- // Expect activity property to override application property
- assertEquals(true, controller.supportsMultiInstanceSplit(component));
- }
-
- @Test
- public void supportsMultiInstanceSplit_activityPropertyFalseApplicationPropertyTrue()
- throws PackageManager.NameNotFoundException {
- Context context = spy(mContext);
- ComponentName component = new ComponentName(TEST_PACKAGE, TEST_ACTIVITY);
- PackageManager pm = mock(PackageManager.class);
- doReturn(pm).when(context).getPackageManager();
- PackageManager.Property activityProp = new PackageManager.Property("", false, "", "");
- doReturn(activityProp).when(pm).getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
- eq(component));
- PackageManager.Property appProp = new PackageManager.Property("", true, "", "");
- doReturn(appProp).when(pm).getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
- eq(component.getPackageName()));
-
- SplitScreenController controller = new SplitScreenController(context, mShellInit,
- mShellCommandHandler, mShellController, mTaskOrganizer, mSyncQueue,
- mRootTDAOrganizer, mDisplayController, mDisplayImeController,
- mDisplayInsetsController, mDragAndDropController, mTransitions, mTransactionPool,
- mIconProvider, mRecentTasks, mLaunchAdjacentController, mWindowDecorViewModel,
- mDesktopTasksController, mMainExecutor, mStageCoordinator,
- new String[0]);
- // Expect activity property to override application property
- assertEquals(false, controller.supportsMultiInstanceSplit(component));
- }
-
- @Test
- public void supportsMultiInstanceSplit_noActivityPropertyApplicationPropertyTrue()
- throws PackageManager.NameNotFoundException {
- Context context = spy(mContext);
- ComponentName component = new ComponentName(TEST_PACKAGE, TEST_ACTIVITY);
- PackageManager pm = mock(PackageManager.class);
- doReturn(pm).when(context).getPackageManager();
- doThrow(PackageManager.NameNotFoundException.class).when(pm).getProperty(
- eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI), eq(component));
- PackageManager.Property appProp = new PackageManager.Property("", true, "", "");
- doReturn(appProp).when(pm).getProperty(eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI),
- eq(component.getPackageName()));
-
- SplitScreenController controller = new SplitScreenController(context, mShellInit,
- mShellCommandHandler, mShellController, mTaskOrganizer, mSyncQueue,
- mRootTDAOrganizer, mDisplayController, mDisplayImeController,
- mDisplayInsetsController, mDragAndDropController, mTransitions, mTransactionPool,
- mIconProvider, mRecentTasks, mLaunchAdjacentController, mWindowDecorViewModel,
- mDesktopTasksController, mMainExecutor, mStageCoordinator,
- new String[0]);
- // Expect fall through to app property
- assertEquals(true, controller.supportsMultiInstanceSplit(component));
- }
-
- @Test
- public void supportsMultiInstanceSplit_noActivityOrAppProperty()
- throws PackageManager.NameNotFoundException {
- Context context = spy(mContext);
- ComponentName component = new ComponentName(TEST_PACKAGE, TEST_ACTIVITY);
- PackageManager pm = mock(PackageManager.class);
- doReturn(pm).when(context).getPackageManager();
- doThrow(PackageManager.NameNotFoundException.class).when(pm).getProperty(
- eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI), eq(component));
- doThrow(PackageManager.NameNotFoundException.class).when(pm).getProperty(
- eq(PROPERTY_SUPPORTS_MULTI_INSTANCE_SYSTEM_UI), eq(component.getPackageName()));
-
- SplitScreenController controller = new SplitScreenController(context, mShellInit,
- mShellCommandHandler, mShellController, mTaskOrganizer, mSyncQueue,
- mRootTDAOrganizer, mDisplayController, mDisplayImeController,
- mDisplayInsetsController, mDragAndDropController, mTransitions, mTransactionPool,
- mIconProvider, mRecentTasks, mLaunchAdjacentController, mWindowDecorViewModel,
- mDesktopTasksController, mMainExecutor, mStageCoordinator,
- new String[0]);
- assertEquals(false, controller.supportsMultiInstanceSplit(component));
- }
-
- @Test
public void testSwitchSplitPosition_checksIsSplitScreenVisible() {
final String reason = "test";
when(mSplitScreenController.isSplitScreenVisible()).thenReturn(true, false);
diff --git a/libs/androidfw/AssetManager2.cpp b/libs/androidfw/AssetManager2.cpp
index 8748dab..46f636e 100644
--- a/libs/androidfw/AssetManager2.cpp
+++ b/libs/androidfw/AssetManager2.cpp
@@ -117,6 +117,10 @@
return true;
}
+void AssetManager2::PresetApkAssets(ApkAssetsList apk_assets) {
+ BuildDynamicRefTable(apk_assets);
+}
+
bool AssetManager2::SetApkAssets(std::initializer_list<ApkAssetsPtr> apk_assets,
bool invalidate_caches) {
return SetApkAssets(ApkAssetsList(apk_assets.begin(), apk_assets.size()), invalidate_caches);
@@ -432,13 +436,18 @@
return false;
}
-void AssetManager2::SetConfigurations(std::vector<ResTable_config> configurations) {
+void AssetManager2::SetConfigurations(std::vector<ResTable_config> configurations,
+ bool force_refresh) {
int diff = 0;
- if (configurations_.size() != configurations.size()) {
+ if (force_refresh) {
diff = -1;
} else {
- for (int i = 0; i < configurations_.size(); i++) {
- diff |= configurations_[i].diff(configurations[i]);
+ if (configurations_.size() != configurations.size()) {
+ diff = -1;
+ } else {
+ for (int i = 0; i < configurations_.size(); i++) {
+ diff |= configurations_[i].diff(configurations[i]);
+ }
}
}
configurations_ = std::move(configurations);
@@ -775,8 +784,7 @@
bool has_locale = false;
if (result->config.locale == 0) {
if (default_locale_ != 0) {
- ResTable_config conf;
- conf.locale = default_locale_;
+ ResTable_config conf = {.locale = default_locale_};
// Since we know conf has a locale and only a locale, match will tell us if that locale
// matches
has_locale = conf.match(config);
diff --git a/libs/androidfw/include/androidfw/AssetManager2.h b/libs/androidfw/include/androidfw/AssetManager2.h
index d9ff35b..17a8ba6 100644
--- a/libs/androidfw/include/androidfw/AssetManager2.h
+++ b/libs/androidfw/include/androidfw/AssetManager2.h
@@ -124,6 +124,9 @@
// new resource IDs.
bool SetApkAssets(ApkAssetsList apk_assets, bool invalidate_caches = true);
bool SetApkAssets(std::initializer_list<ApkAssetsPtr> apk_assets, bool invalidate_caches = true);
+ // This one is an optimization - it skips all calculations for applying the currently set
+ // configuration, expecting a configuration update later with a forced refresh.
+ void PresetApkAssets(ApkAssetsList apk_assets);
const ApkAssetsPtr& GetApkAssets(ApkAssetsCookie cookie) const;
int GetApkAssetsCount() const {
@@ -156,7 +159,7 @@
// Sets/resets the configuration for this AssetManager. This will cause all
// caches that are related to the configuration change to be invalidated.
- void SetConfigurations(std::vector<ResTable_config> configurations);
+ void SetConfigurations(std::vector<ResTable_config> configurations, bool force_refresh = false);
inline const std::vector<ResTable_config>& GetConfigurations() const {
return configurations_;
diff --git a/libs/hostgraphics/OWNERS b/libs/hostgraphics/OWNERS
new file mode 100644
index 0000000..41977f6
--- /dev/null
+++ b/libs/hostgraphics/OWNERS
@@ -0,0 +1,4 @@
+include /libs/hwui/OWNERS
+
+diegoperez@google.com
+jgaillard@google.com
diff --git a/libs/hwui/HardwareBitmapUploader.cpp b/libs/hwui/HardwareBitmapUploader.cpp
index 71f7926..27ea150 100644
--- a/libs/hwui/HardwareBitmapUploader.cpp
+++ b/libs/hwui/HardwareBitmapUploader.cpp
@@ -378,10 +378,17 @@
break;
case kAlpha_8_SkColorType:
formatInfo.isSupported = HardwareBitmapUploader::hasAlpha8Support();
- formatInfo.bufferFormat = AHARDWAREBUFFER_FORMAT_R8_UNORM;
- formatInfo.format = GL_RED;
- formatInfo.type = GL_UNSIGNED_BYTE;
- formatInfo.vkFormat = VK_FORMAT_R8_UNORM;
+ if (formatInfo.isSupported) {
+ formatInfo.bufferFormat = AHARDWAREBUFFER_FORMAT_R8_UNORM;
+ formatInfo.format = GL_RED;
+ formatInfo.type = GL_UNSIGNED_BYTE;
+ formatInfo.vkFormat = VK_FORMAT_R8_UNORM;
+ } else {
+ formatInfo.type = GL_UNSIGNED_BYTE;
+ formatInfo.bufferFormat = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM;
+ formatInfo.vkFormat = VK_FORMAT_R8G8B8A8_UNORM;
+ formatInfo.format = GL_RGBA;
+ }
break;
default:
ALOGW("unable to create hardware bitmap of colortype: %d", skBitmap.info().colorType());
diff --git a/libs/hwui/hwui/Bitmap.cpp b/libs/hwui/hwui/Bitmap.cpp
index 8344a86..1854361 100644
--- a/libs/hwui/hwui/Bitmap.cpp
+++ b/libs/hwui/hwui/Bitmap.cpp
@@ -147,11 +147,7 @@
}
sk_sp<Bitmap> Bitmap::allocateHardwareBitmap(const SkBitmap& bitmap) {
-#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration
- if (bitmap.colorType() == kAlpha_8_SkColorType &&
- !uirenderer::HardwareBitmapUploader::hasAlpha8Support()) {
- return nullptr;
- }
+#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration
return uirenderer::HardwareBitmapUploader::allocateHardwareBitmap(bitmap);
#else
return Bitmap::allocateHeapBitmap(bitmap.info());
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 144b01a..4d5a33d 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -20,6 +20,7 @@
import static com.android.media.flags.Flags.FLAG_ENABLE_BUILT_IN_SPEAKER_ROUTE_SUITABILITY_STATUSES;
import static com.android.media.flags.Flags.FLAG_ENABLE_CROSS_USER_ROUTING_IN_MEDIA_ROUTER2;
import static com.android.media.flags.Flags.FLAG_ENABLE_GET_TRANSFERABLE_ROUTES;
+import static com.android.media.flags.Flags.FLAG_ENABLE_PRIVILEGED_ROUTING_FOR_MEDIA_ROUTING_CONTROL;
import static com.android.media.flags.Flags.FLAG_ENABLE_RLP_CALLBACKS_IN_MEDIA_ROUTER2;
import static com.android.media.flags.Flags.FLAG_ENABLE_SCREEN_OFF_SCANNING;
@@ -989,17 +990,24 @@
}
/**
- * Requests a volume change for the route asynchronously.
- * It may have no effect if the route is currently not selected.
+ * Sets the volume for a specific route.
*
- * <p>This will be no-op for non-system media routers.
+ * <p>The call may have no effect if the route is currently not selected.
+ *
+ * <p>This method is only supported by {@link #getInstance(Context, String) proxy MediaRouter2
+ * instances}. Use {@link RoutingController#setVolume(int) RoutingController#setVolume(int)}
+ * instead for {@link #getInstance(Context) local MediaRouter2 instances}.</p>
*
* @param volume The new volume value between 0 and {@link MediaRoute2Info#getVolumeMax}.
- * @see #getInstance(Context, String)
- * @hide
+ * @throws UnsupportedOperationException If called on a {@link #getInstance(Context) local
+ * router instance}.
*/
- @SystemApi
- @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL)
+ @FlaggedApi(FLAG_ENABLE_PRIVILEGED_ROUTING_FOR_MEDIA_ROUTING_CONTROL)
+ @RequiresPermission(
+ anyOf = {
+ Manifest.permission.MEDIA_CONTENT_CONTROL,
+ Manifest.permission.MEDIA_ROUTING_CONTROL
+ })
public void setRouteVolume(@NonNull MediaRoute2Info route, int volume) {
Objects.requireNonNull(route, "route must not be null");
@@ -3464,10 +3472,11 @@
return result;
}
- /** No-op. Local routers cannot modify the volume of specific routes. */
+ /** Local routers cannot modify the volume of specific routes. */
@Override
public void setRouteVolume(MediaRoute2Info route, int volume) {
- // Do nothing.
+ throw new UnsupportedOperationException(
+ "setRouteVolume is only supported by proxy routers. See javadoc.");
// If this API needs to be public, use IMediaRouterService#setRouteVolumeWithRouter2()
}
diff --git a/media/java/android/media/audiofx/Visualizer.java b/media/java/android/media/audiofx/Visualizer.java
index 2795cfe..f05ea9c 100644
--- a/media/java/android/media/audiofx/Visualizer.java
+++ b/media/java/android/media/audiofx/Visualizer.java
@@ -336,8 +336,9 @@
* This method must not be called when the Visualizer is enabled.
* @param size requested capture size
* @return {@link #SUCCESS} in case of success,
- * {@link #ERROR_BAD_VALUE} in case of failure.
- * @throws IllegalStateException
+ * {@link #ERROR_INVALID_OPERATION} if Visualizer effect enginer not enabled.
+ * @throws IllegalStateException if the effect is not in proper state.
+ * @throws IllegalArgumentException if the size parameter is invalid (out of supported range).
*/
public int setCaptureSize(int size)
throws IllegalStateException {
@@ -345,7 +346,13 @@
if (mState != STATE_INITIALIZED) {
throw(new IllegalStateException("setCaptureSize() called in wrong state: "+mState));
}
- return native_setCaptureSize(size);
+
+ int ret = native_setCaptureSize(size);
+ if (ret == ERROR_BAD_VALUE) {
+ throw(new IllegalArgumentException("setCaptureSize to " + size + " failed"));
+ }
+
+ return ret;
}
}
diff --git a/media/java/android/service/media/MediaBrowserService.java b/media/java/android/service/media/MediaBrowserService.java
index e92c7b3..d17b341 100644
--- a/media/java/android/service/media/MediaBrowserService.java
+++ b/media/java/android/service/media/MediaBrowserService.java
@@ -107,11 +107,15 @@
private final ServiceState mServiceState = new ServiceState();
+ // Holds the connection record associated with the currently executing callback operation, if
+ // any. See getCurrentBrowserInfo for an example. Must only be accessed on mHandler.
+ @Nullable private ConnectionRecord mCurrentConnectionOnHandler;
+
/**
* All the info about a connection.
*/
private static class ConnectionRecord implements IBinder.DeathRecipient {
- public final MediaBrowserService service;
+ public final ServiceState serviceState;
public final String pkg;
public final int pid;
public final int uid;
@@ -121,9 +125,14 @@
public final HashMap<String, List<Pair<IBinder, Bundle>>> subscriptions = new HashMap<>();
ConnectionRecord(
- MediaBrowserService service, String pkg, int pid, int uid, Bundle rootHints,
- IMediaBrowserServiceCallbacks callbacks, BrowserRoot root) {
- this.service = service;
+ ServiceState serviceState,
+ String pkg,
+ int pid,
+ int uid,
+ Bundle rootHints,
+ IMediaBrowserServiceCallbacks callbacks,
+ BrowserRoot root) {
+ this.serviceState = serviceState;
this.pkg = pkg;
this.pid = pid;
this.uid = uid;
@@ -134,12 +143,8 @@
@Override
public void binderDied() {
- service.mHandler.post(new Runnable() {
- @Override
- public void run() {
- service.mServiceState.mConnections.remove(callbacks.asBinder());
- }
- });
+ serviceState.postOnHandler(
+ () -> serviceState.mConnections.remove(callbacks.asBinder()));
}
}
@@ -461,12 +466,12 @@
* @see MediaBrowserService.BrowserRoot#EXTRA_SUGGESTED
*/
public final Bundle getBrowserRootHints() {
- ConnectionRecord curConnection = mServiceState.mCurConnection;
- if (curConnection == null) {
+ ConnectionRecord currentConnection = mCurrentConnectionOnHandler;
+ if (currentConnection == null) {
throw new IllegalStateException("This should be called inside of onGetRoot or"
+ " onLoadChildren or onLoadItem methods");
}
- return curConnection.rootHints == null ? null : new Bundle(curConnection.rootHints);
+ return currentConnection.rootHints == null ? null : new Bundle(currentConnection.rootHints);
}
/**
@@ -477,12 +482,13 @@
* @see MediaSessionManager#isTrustedForMediaControl(RemoteUserInfo)
*/
public final RemoteUserInfo getCurrentBrowserInfo() {
- ConnectionRecord curConnection = mServiceState.mCurConnection;
- if (curConnection == null) {
+ ConnectionRecord currentConnection = mCurrentConnectionOnHandler;
+ if (currentConnection == null) {
throw new IllegalStateException("This should be called inside of onGetRoot or"
+ " onLoadChildren or onLoadItem methods");
}
- return new RemoteUserInfo(curConnection.pkg, curConnection.pid, curConnection.uid);
+ return new RemoteUserInfo(
+ currentConnection.pkg, currentConnection.pid, currentConnection.uid);
}
/**
@@ -621,7 +627,6 @@
// Fields accessed from mHandler only.
@NonNull private final ArrayMap<IBinder, ConnectionRecord> mConnections = new ArrayMap<>();
- @Nullable private ConnectionRecord mCurConnection;
public void postOnHandler(Runnable runnable) {
mHandler.post(runnable);
@@ -704,9 +709,9 @@
// Temporarily sets a placeholder ConnectionRecord to make getCurrentBrowserInfo() work
// in onGetRoot().
- mCurConnection =
+ mCurrentConnectionOnHandler =
new ConnectionRecord(
- /* service= */ MediaBrowserService.this,
+ /* serviceState= */ this,
pkg,
pid,
uid,
@@ -714,7 +719,7 @@
callbacks,
/* root= */ null);
BrowserRoot root = onGetRoot(pkg, uid, rootHints);
- mCurConnection = null;
+ mCurrentConnectionOnHandler = null;
// If they didn't return something, don't allow this client.
if (root == null) {
@@ -728,7 +733,7 @@
try {
ConnectionRecord connection =
new ConnectionRecord(
- /* service= */ MediaBrowserService.this,
+ /* serviceState= */ this,
pkg,
pid,
uid,
@@ -830,13 +835,13 @@
}
};
- mCurConnection = connection;
+ mCurrentConnectionOnHandler = connection;
if (options == null) {
onLoadChildren(parentId, result);
} else {
onLoadChildren(parentId, result, options);
}
- mCurConnection = null;
+ mCurrentConnectionOnHandler = null;
if (!result.isDone()) {
throw new IllegalStateException(
@@ -885,9 +890,9 @@
}
};
- mCurConnection = connection;
+ mCurrentConnectionOnHandler = connection;
onLoadItem(itemId, result);
- mCurConnection = null;
+ mCurrentConnectionOnHandler = null;
if (!result.isDone()) {
throw new IllegalStateException(
diff --git a/media/jni/audioeffect/Visualizer.cpp b/media/jni/audioeffect/Visualizer.cpp
index 09c45ea..9ae5c99 100644
--- a/media/jni/audioeffect/Visualizer.cpp
+++ b/media/jni/audioeffect/Visualizer.cpp
@@ -25,7 +25,6 @@
#include <limits.h>
#include <audio_utils/fixedfft.h>
-#include <cutils/bitops.h>
#include <utils/Thread.h>
#include <android/content/AttributionSourceState.h>
@@ -59,8 +58,8 @@
status_t status = AudioEffect::set(
SL_IID_VISUALIZATION, nullptr, priority, cbf, user, sessionId, io, device, probe);
if (status == NO_ERROR || status == ALREADY_EXISTS) {
- initCaptureSize();
- initSampleRate();
+ status = initCaptureSize();
+ if (status == NO_ERROR) initSampleRate();
}
return status;
}
@@ -152,9 +151,8 @@
status_t Visualizer::setCaptureSize(uint32_t size)
{
- if (size > VISUALIZER_CAPTURE_SIZE_MAX ||
- size < VISUALIZER_CAPTURE_SIZE_MIN ||
- popcount(size) != 1) {
+ if (!isCaptureSizeValid(size)) {
+ ALOGE("%s with invalid capture size %u from HAL", __func__, size);
return BAD_VALUE;
}
@@ -172,7 +170,7 @@
*((int32_t *)p->data + 1)= size;
status_t status = setParameter(p);
- ALOGV("setCaptureSize size %d status %d p->status %d", size, status, p->status);
+ ALOGV("setCaptureSize size %u status %d p->status %d", size, status, p->status);
if (status == NO_ERROR) {
status = p->status;
@@ -257,8 +255,8 @@
if ((type != MEASUREMENT_MODE_PEAK_RMS)
// for peak+RMS measurement, the results are 2 int32_t values
|| (number != 2)) {
- ALOGE("Cannot retrieve int measurements, MEASUREMENT_MODE_PEAK_RMS returns 2 ints, not %d",
- number);
+ ALOGE("Cannot retrieve int measurements, MEASUREMENT_MODE_PEAK_RMS returns 2 ints, not %u",
+ number);
return BAD_VALUE;
}
@@ -390,7 +388,7 @@
}
}
-uint32_t Visualizer::initCaptureSize()
+status_t Visualizer::initCaptureSize()
{
uint32_t buf32[sizeof(effect_param_t) / sizeof(uint32_t) + 2];
effect_param_t *p = (effect_param_t *)buf32;
@@ -405,14 +403,20 @@
}
uint32_t size = 0;
- if (status == NO_ERROR) {
- size = *((int32_t *)p->data + 1);
+ if (status != NO_ERROR) {
+ ALOGE("%s getParameter failed status %d", __func__, status);
+ return status;
}
+
+ size = *((int32_t *)p->data + 1);
+ if (!isCaptureSizeValid(size)) {
+ ALOGE("%s with invalid capture size %u from HAL", __func__, size);
+ return BAD_VALUE;
+ }
+
mCaptureSize = size;
-
- ALOGV("initCaptureSize size %d status %d", mCaptureSize, status);
-
- return size;
+ ALOGV("%s size %u status %d", __func__, mCaptureSize, status);
+ return NO_ERROR;
}
void Visualizer::initSampleRate()
diff --git a/media/jni/audioeffect/Visualizer.h b/media/jni/audioeffect/Visualizer.h
index b38c01f..26d58d0 100644
--- a/media/jni/audioeffect/Visualizer.h
+++ b/media/jni/audioeffect/Visualizer.h
@@ -20,6 +20,8 @@
#include <media/AudioEffect.h>
#include <system/audio_effects/effect_visualizer.h>
#include <utils/Thread.h>
+#include <cstdint>
+#include <cutils/bitops.h>
#include "android/content/AttributionSourceState.h"
/**
@@ -170,8 +172,12 @@
status_t doFft(uint8_t *fft, uint8_t *waveform);
void periodicCapture();
- uint32_t initCaptureSize();
+ status_t initCaptureSize();
void initSampleRate();
+ static constexpr bool isCaptureSizeValid(uint32_t size) {
+ return size <= VISUALIZER_CAPTURE_SIZE_MAX && size >= VISUALIZER_CAPTURE_SIZE_MIN &&
+ popcount(size) == 1;
+ }
Mutex mCaptureLock;
uint32_t mCaptureRate = CAPTURE_RATE_DEF;
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestUtils.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestUtils.java
index 388a65d..00068bd 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestUtils.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/CameraTestUtils.java
@@ -1709,7 +1709,7 @@
* <p>
* Two images are strongly equal if and only if the data, formats, sizes,
* and timestamps are same. For {@link ImageFormat#PRIVATE PRIVATE} format
- * images, the image data is not not accessible thus the data comparison is
+ * images, the image data is not accessible thus the data comparison is
* effectively skipped as the number of planes is zero.
* </p>
* <p>
@@ -2049,7 +2049,7 @@
}
} else {
// Case 2.
- collector.expectEquals("Exif orientaiton should match requested orientation",
+ collector.expectEquals("Exif orientation should match requested orientation",
requestedOrientation, getExifOrientationInDegree(exifOrientation,
collector));
}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/StaticMetadata.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/StaticMetadata.java
index ed70ab9..5dcd1cb 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/StaticMetadata.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/helpers/StaticMetadata.java
@@ -1127,8 +1127,8 @@
* Get aeAvailableModes and do the validation check.
*
* <p>Depending on the check level this class has, for WAR or COLLECT levels,
- * If the aeMode list is invalid, return an empty mode array. The the caller doesn't
- * have to abort the execution even the aeMode list is invalid.</p>
+ * If the aeMode list is invalid, return an empty mode array. The caller doesn't
+ * have to abort the execution even if the aeMode list is invalid.</p>
* @return AE available modes
*/
public int[] getAeAvailableModesChecked() {
diff --git a/media/tests/mediatestutils/Android.bp b/media/tests/mediatestutils/Android.bp
index 15bc177..88938e2 100644
--- a/media/tests/mediatestutils/Android.bp
+++ b/media/tests/mediatestutils/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_media_framework_audio",
// 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"
diff --git a/media/tests/mediatestutils/tests/Android.bp b/media/tests/mediatestutils/tests/Android.bp
index 24a8360..f586f97f 100644
--- a/media/tests/mediatestutils/tests/Android.bp
+++ b/media/tests/mediatestutils/tests/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_media_framework_audio",
// 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"
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index 3302265..35e37b2 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -277,6 +277,7 @@
ASurfaceTransaction_setHdrMetadata_cta861_3; # introduced=29
ASurfaceTransaction_setHdrMetadata_smpte2086; # introduced=29
ASurfaceTransaction_setExtendedRangeBrightness; # introduced=UpsideDownCake
+ ASurfaceTransaction_setDesiredHdrHeadroom; # introduced=VanillaIceCream
ASurfaceTransaction_setOnComplete; # introduced=29
ASurfaceTransaction_setOnCommit; # introduced=31
ASurfaceTransaction_setPosition; # introduced=31
diff --git a/native/android/surface_control.cpp b/native/android/surface_control.cpp
index 4b63fbf..9b1330f 100644
--- a/native/android/surface_control.cpp
+++ b/native/android/surface_control.cpp
@@ -646,6 +646,24 @@
transaction->setExtendedRangeBrightness(surfaceControl, currentBufferRatio, desiredRatio);
}
+void ASurfaceTransaction_setDesiredHdrHeadroom(ASurfaceTransaction* aSurfaceTransaction,
+ ASurfaceControl* aSurfaceControl,
+ float desiredRatio) {
+ CHECK_NOT_NULL(aSurfaceTransaction);
+ CHECK_NOT_NULL(aSurfaceControl);
+
+ if (!isfinite(desiredRatio) || (desiredRatio < 1.0f && desiredRatio > 0.0f)) {
+ LOG_ALWAYS_FATAL("setDesiredHdrHeadroom, desiredRatio isn't finite && >= 1.0f or 0, got %f",
+ desiredRatio);
+ return;
+ }
+
+ sp<SurfaceControl> surfaceControl = ASurfaceControl_to_SurfaceControl(aSurfaceControl);
+ Transaction* transaction = ASurfaceTransaction_to_Transaction(aSurfaceTransaction);
+
+ transaction->setDesiredHdrHeadroom(surfaceControl, desiredRatio);
+}
+
void ASurfaceTransaction_setColor(ASurfaceTransaction* aSurfaceTransaction,
ASurfaceControl* aSurfaceControl,
float r, float g, float b, float alpha,
diff --git a/nfc-extras/Android.bp b/nfc-extras/Android.bp
index 1f187e8..699ad50 100644
--- a/nfc-extras/Android.bp
+++ b/nfc-extras/Android.bp
@@ -15,6 +15,7 @@
//
package {
+ default_team: "trendy_team_fwk_nfc",
// 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"
@@ -28,7 +29,7 @@
name: "com.android.nfc_extras",
srcs: ["java/**/*.java"],
libs: [
- "framework-nfc.impl"
+ "framework-nfc.impl",
],
api_packages: ["com.android.nfc_extras"],
dist_group: "android",
diff --git a/nfc-extras/tests/Android.bp b/nfc-extras/tests/Android.bp
index 4c1f2fb..e8214b0 100644
--- a/nfc-extras/tests/Android.bp
+++ b/nfc-extras/tests/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_fwk_nfc",
// 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"
diff --git a/nfc/Android.bp b/nfc/Android.bp
index 7136866..b6bc40d 100644
--- a/nfc/Android.bp
+++ b/nfc/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_fwk_nfc",
// 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"
diff --git a/nfc/api/current.txt b/nfc/api/current.txt
index 845a8f9..f111327 100644
--- a/nfc/api/current.txt
+++ b/nfc/api/current.txt
@@ -206,9 +206,9 @@
method public boolean registerAidsForService(android.content.ComponentName, String, java.util.List<java.lang.String>);
method @FlaggedApi("android.nfc.nfc_read_polling_loop") public boolean registerPollingLoopFilterForService(@NonNull android.content.ComponentName, @NonNull String);
method public boolean removeAidsForService(android.content.ComponentName, String);
+ method @FlaggedApi("android.nfc.nfc_observe_mode") public boolean setDefaultToObserveModeForService(@NonNull android.content.ComponentName, boolean);
method @NonNull @RequiresPermission(android.Manifest.permission.NFC) public boolean setOffHostForService(@NonNull android.content.ComponentName, @NonNull String);
method public boolean setPreferredService(android.app.Activity, android.content.ComponentName);
- method @FlaggedApi("android.nfc.nfc_observe_mode") public boolean setServiceObserveModeDefault(@NonNull android.content.ComponentName, boolean);
method public boolean supportsAidPrefixRegistration();
method @NonNull @RequiresPermission(android.Manifest.permission.NFC) public boolean unsetOffHostForService(@NonNull android.content.ComponentName);
method public boolean unsetPreferredService(android.app.Activity);
diff --git a/nfc/api/system-current.txt b/nfc/api/system-current.txt
index dd2e174..a72e539 100644
--- a/nfc/api/system-current.txt
+++ b/nfc/api/system-current.txt
@@ -26,6 +26,8 @@
method @FlaggedApi("android.nfc.nfc_vendor_cmd") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void unregisterNfcVendorNciCallback(@NonNull android.nfc.NfcAdapter.NfcVendorNciCallback);
method @FlaggedApi("android.nfc.enable_nfc_charging") public void unregisterWlcStateListener(@NonNull android.nfc.NfcAdapter.WlcStateListener);
field @FlaggedApi("android.nfc.enable_nfc_mainline") public static final String ACTION_REQUIRE_UNLOCK_FOR_NFC = "android.nfc.action.REQUIRE_UNLOCK_FOR_NFC";
+ field @FlaggedApi("android.nfc.enable_nfc_mainline") @RequiresPermission(android.Manifest.permission.SHOW_CUSTOMIZED_RESOLVER) public static final String ACTION_SHOW_NFC_RESOLVER = "android.nfc.action.SHOW_NFC_RESOLVER";
+ field @FlaggedApi("android.nfc.enable_nfc_mainline") public static final String EXTRA_RESOLVE_INFOS = "android.nfc.extra.RESOLVE_INFOS";
field @FlaggedApi("android.nfc.nfc_vendor_cmd") public static final int MESSAGE_TYPE_COMMAND = 1; // 0x1
field @FlaggedApi("android.nfc.nfc_vendor_cmd") public static final int SEND_VENDOR_NCI_STATUS_FAILED = 3; // 0x3
field @FlaggedApi("android.nfc.nfc_vendor_cmd") public static final int SEND_VENDOR_NCI_STATUS_MESSAGE_CORRUPTED = 2; // 0x2
diff --git a/nfc/java/android/nfc/INfcCardEmulation.aidl b/nfc/java/android/nfc/INfcCardEmulation.aidl
index 791bd8c..65d0625 100644
--- a/nfc/java/android/nfc/INfcCardEmulation.aidl
+++ b/nfc/java/android/nfc/INfcCardEmulation.aidl
@@ -30,7 +30,7 @@
boolean isDefaultServiceForAid(int userHandle, in ComponentName service, String aid);
boolean setDefaultServiceForCategory(int userHandle, in ComponentName service, String category);
boolean setDefaultForNextTap(int userHandle, in ComponentName service);
- boolean setServiceObserveModeDefault(int userId, in android.content.ComponentName service, boolean enable);
+ boolean setDefaultToObserveModeForService(int userId, in android.content.ComponentName service, boolean enable);
boolean registerAidGroupForService(int userHandle, in ComponentName service, in AidGroup aidGroup);
boolean registerPollingLoopFilterForService(int userHandle, in ComponentName service, in String pollingLoopFilter);
boolean setOffHostForService(int userHandle, in ComponentName service, in String offHostSecureElement);
diff --git a/nfc/java/android/nfc/NfcAdapter.java b/nfc/java/android/nfc/NfcAdapter.java
index 252f46f..c5b7582 100644
--- a/nfc/java/android/nfc/NfcAdapter.java
+++ b/nfc/java/android/nfc/NfcAdapter.java
@@ -16,6 +16,7 @@
package android.nfc;
+import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
@@ -35,6 +36,7 @@
import android.content.Context;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.nfc.tech.MifareClassic;
import android.nfc.tech.Ndef;
@@ -485,6 +487,25 @@
"android.nfc.action.REQUIRE_UNLOCK_FOR_NFC";
/**
+ * Intent action to start a NFC resolver activity in a customized share session with list of
+ * {@link ResolveInfo}.
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ @RequiresPermission(Manifest.permission.SHOW_CUSTOMIZED_RESOLVER)
+ public static final String ACTION_SHOW_NFC_RESOLVER = "android.nfc.action.SHOW_NFC_RESOLVER";
+
+ /**
+ * "Extras" key for an ArrayList of {@link ResolveInfo} records which are to be shown as the
+ * targets in the customized share session.
+ * @hide
+ */
+ @SystemApi
+ @FlaggedApi(Flags.FLAG_ENABLE_NFC_MAINLINE)
+ public static final String EXTRA_RESOLVE_INFOS = "android.nfc.extra.RESOLVE_INFOS";
+
+ /**
* The requested app is correctly added to the Tag intent app preference.
*
* @see #setTagIntentAppPreferenceForUser(int userId, String pkg, boolean allow)
diff --git a/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java b/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java
index f264b16..32dba5f 100644
--- a/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java
+++ b/nfc/java/android/nfc/cardemulation/ApduServiceInfo.java
@@ -105,7 +105,6 @@
*/
private final HashMap<String, AidGroup> mDynamicAidGroups;
- private final ArrayList<String> mPollingLoopFilters;
private final Map<String, Boolean> mAutoTransact;
@@ -181,7 +180,6 @@
this.mDescription = description;
this.mStaticAidGroups = new HashMap<String, AidGroup>();
this.mDynamicAidGroups = new HashMap<String, AidGroup>();
- this.mPollingLoopFilters = new ArrayList<String>();
this.mAutoTransact = new HashMap<String, Boolean>();
this.mOffHostName = offHost;
this.mStaticOffHostName = staticOffHost;
@@ -302,7 +300,6 @@
mStaticAidGroups = new HashMap<String, AidGroup>();
mDynamicAidGroups = new HashMap<String, AidGroup>();
- mPollingLoopFilters = new ArrayList<String>();
mAutoTransact = new HashMap<String, Boolean>();
mOnHost = onHost;
@@ -393,7 +390,6 @@
String plf =
a.getString(com.android.internal.R.styleable.PollingLoopFilter_name)
.toUpperCase(Locale.ROOT);
- mPollingLoopFilters.add(plf);
boolean autoTransact = a.getBoolean(
com.android.internal.R.styleable.PollingLoopFilter_autoTransact,
false);
@@ -461,7 +457,7 @@
@FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP)
@NonNull
public List<String> getPollingLoopFilters() {
- return mPollingLoopFilters;
+ return new ArrayList<>(mAutoTransact.keySet());
}
/**
@@ -672,12 +668,15 @@
/**
* Add a Polling Loop Filter. Custom NFC polling frames that match this filter will be
- * delivered to {@link HostApduService#processPollingFrames(List)}.
- * @param pollingLoopFilter this polling loop filter to add.
+ * delivered to {@link HostApduService#processPollingFrames(List)}. Adding a key with this or
+ * {@link ApduServiceInfo#addPollingLoopFilterToAutoTransact(String)} multiple times will
+ * cause the value to be overwritten each time.
+ * @param pollingLoopFilter the polling loop filter to add, must be a valide hexadecimal string
*/
@FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP)
public void addPollingLoopFilter(@NonNull String pollingLoopFilter) {
- mPollingLoopFilters.add(pollingLoopFilter.toUpperCase(Locale.ROOT));
+ mAutoTransact.put(pollingLoopFilter.toUpperCase(Locale.ROOT), false);
+
}
/**
@@ -685,13 +684,14 @@
* device to exit observe mode, just as if
* {@link android.nfc.NfcAdapter#setTransactionAllowed(boolean)} had been called with true,
* allowing transactions to proceed. The matching frame will also be delivered to
- * {@link HostApduService#processPollingFrames(List)}.
+ * {@link HostApduService#processPollingFrames(List)}. Adding a key with this or
+ * {@link ApduServiceInfo#addPollingLoopFilter(String)} multiple times will
+ * cause the value to be overwritten each time.
*
- * @param pollingLoopFilter this polling loop filter to add.
+ * @param pollingLoopFilter the polling loop filter to add, must be a valide hexadecimal string
*/
@FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP)
public void addPollingLoopFilterToAutoTransact(@NonNull String pollingLoopFilter) {
- mPollingLoopFilters.add(pollingLoopFilter.toUpperCase(Locale.ROOT));
mAutoTransact.put(pollingLoopFilter.toUpperCase(Locale.ROOT), true);
}
@@ -702,7 +702,7 @@
*/
@FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP)
public void removePollingLoopFilter(@NonNull String pollingLoopFilter) {
- mPollingLoopFilters.remove(pollingLoopFilter.toUpperCase(Locale.ROOT));
+ mAutoTransact.remove(pollingLoopFilter.toUpperCase(Locale.ROOT));
}
/**
diff --git a/nfc/java/android/nfc/cardemulation/CardEmulation.java b/nfc/java/android/nfc/cardemulation/CardEmulation.java
index 1f41b81..e681a85 100644
--- a/nfc/java/android/nfc/cardemulation/CardEmulation.java
+++ b/nfc/java/android/nfc/cardemulation/CardEmulation.java
@@ -338,19 +338,20 @@
}
}
/**
- * Sets whether the system should default to observe mode or not when the service is in the
- * foreground or the default payment service. The default is to not enable observe mode when
- * a service either the foreground default service or the default payment service so not
- * calling this method will preserve that behavior.
+ * Sets whether when this service becomes the preferred service, if the NFC stack
+ * should enable observe mode or disable observe mode. The default is to not enable observe
+ * mode when a service either the foreground default service or the default payment service so
+ * not calling this method will preserve that behavior.
*
* @param service The component name of the service
- * @param enable Whether the servic should default to observe mode or not
+ * @param enable Whether the service should default to observe mode or not
* @return whether the change was successful.
*/
@FlaggedApi(Flags.FLAG_NFC_OBSERVE_MODE)
- public boolean setServiceObserveModeDefault(@NonNull ComponentName service, boolean enable) {
+ public boolean setDefaultToObserveModeForService(@NonNull ComponentName service,
+ boolean enable) {
try {
- return sService.setServiceObserveModeDefault(mContext.getUser().getIdentifier(),
+ return sService.setDefaultToObserveModeForService(mContext.getUser().getIdentifier(),
service, enable);
} catch (RemoteException e) {
Log.e(TAG, "Failed to reach CardEmulationService.");
@@ -359,9 +360,14 @@
}
/**
- * Register a polling loop filter for a HostApduService.
- * @param service The HostApduService to register the filter for.
- * @param pollingLoopFilter The filter to register.
+ * Register a polling loop filter (PLF) for a HostApduService. The PLF can be sequence of an
+ * even number of hexadecimal numbers (0-9, A-F or a-f). When non-standard polling loop frame
+ * matches this sequence exactly, it may be delivered to
+ * {@link HostApduService#processPollingFrames(List)} if this service is currently
+ * preferred or there are no other services registered for this filter.
+ * @param service The HostApduService to register the filter for
+ * @param pollingLoopFilter The filter to register
+ * @return true if the filter was registered, false otherwise
*/
@FlaggedApi(Flags.FLAG_NFC_READ_POLLING_LOOP)
public boolean registerPollingLoopFilterForService(@NonNull ComponentName service,
diff --git a/nfc/java/android/nfc/cardemulation/HostApduService.java b/nfc/java/android/nfc/cardemulation/HostApduService.java
index 89b0322..363788e 100644
--- a/nfc/java/android/nfc/cardemulation/HostApduService.java
+++ b/nfc/java/android/nfc/cardemulation/HostApduService.java
@@ -470,7 +470,7 @@
}
/**
- * This method is called when a polling frame has been received from a
+ * This method is called when polling frames have been received from a
* remote device. If the device is in observe mode, the service should
* call {@link NfcAdapter#allowTransaction()} once it is ready to proceed
* with the transaction. If the device is not in observe mode, the service
diff --git a/nfc/tests/Android.bp b/nfc/tests/Android.bp
index 62566ee..6ebc03c 100644
--- a/nfc/tests/Android.bp
+++ b/nfc/tests/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_fwk_nfc",
// 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"
diff --git a/packages/CrashRecovery/services/java/com/android/server/RescueParty.java b/packages/CrashRecovery/services/java/com/android/server/RescueParty.java
index b5951e8..9217e70 100644
--- a/packages/CrashRecovery/services/java/com/android/server/RescueParty.java
+++ b/packages/CrashRecovery/services/java/com/android/server/RescueParty.java
@@ -75,6 +75,8 @@
*/
public class RescueParty {
@VisibleForTesting
+ static final String PROP_ENABLE_RESCUE = "persist.sys.enable_rescue";
+ @VisibleForTesting
static final int LEVEL_NONE = 0;
@VisibleForTesting
static final int LEVEL_RESET_SETTINGS_UNTRUSTED_DEFAULTS = 1;
@@ -123,7 +125,7 @@
private static boolean isDisabled() {
// Check if we're explicitly enabled for testing
- if (CrashRecoveryProperties.enableRescueParty().orElse(false)) {
+ if (SystemProperties.getBoolean(PROP_ENABLE_RESCUE, false)) {
return false;
}
@@ -176,6 +178,29 @@
return CrashRecoveryProperties.attemptingReboot().orElse(false);
}
+ protected static long getLastFactoryResetTimeMs() {
+ return CrashRecoveryProperties.lastFactoryResetTimeMs().orElse(0L);
+ }
+
+ protected static int getMaxRescueLevelAttempted() {
+ return CrashRecoveryProperties.maxRescueLevelAttempted().orElse(LEVEL_NONE);
+ }
+
+ protected static void setFactoryResetProperty(boolean value) {
+ CrashRecoveryProperties.attemptingFactoryReset(value);
+ }
+ protected static void setRebootProperty(boolean value) {
+ CrashRecoveryProperties.attemptingReboot(value);
+ }
+
+ protected static void setLastFactoryResetTimeMs(long value) {
+ CrashRecoveryProperties.lastFactoryResetTimeMs(value);
+ }
+
+ protected static void setMaxRescueLevelAttempted(int level) {
+ CrashRecoveryProperties.maxRescueLevelAttempted(level);
+ }
+
/**
* Called when {@code SettingsProvider} has been published, which is a good
* opportunity to reset any settings depending on our rescue level.
@@ -432,7 +457,7 @@
case LEVEL_WARM_REBOOT:
// Request the reboot from a separate thread to avoid deadlock on PackageWatchdog
// when device shutting down.
- CrashRecoveryProperties.attemptingReboot(true);
+ setRebootProperty(true);
runnable = () -> {
try {
PowerManager pm = context.getSystemService(PowerManager.class);
@@ -454,9 +479,9 @@
if (isRebootPropertySet()) {
break;
}
- CrashRecoveryProperties.attemptingFactoryReset(true);
+ setFactoryResetProperty(true);
long now = System.currentTimeMillis();
- CrashRecoveryProperties.lastFactoryResetTimeMs(now);
+ setLastFactoryResetTimeMs(now);
runnable = new Runnable() {
@Override
public void run() {
@@ -515,10 +540,10 @@
private static void resetAllSettingsIfNecessary(Context context, int mode,
int level) throws Exception {
// No need to reset Settings again if they are already reset in the current level once.
- if (CrashRecoveryProperties.maxRescueLevelAttempted().orElse(LEVEL_NONE) >= level) {
+ if (getMaxRescueLevelAttempted() >= level) {
return;
}
- CrashRecoveryProperties.maxRescueLevelAttempted(level);
+ setMaxRescueLevelAttempted(level);
// Try our best to reset all settings possible, and once finished
// rethrow any exception that we encountered
Exception res = null;
@@ -733,7 +758,7 @@
* Will return {@code false} if a factory reset was already offered recently.
*/
private boolean shouldThrottleReboot() {
- Long lastResetTime = CrashRecoveryProperties.lastFactoryResetTimeMs().orElse(0L);
+ Long lastResetTime = getLastFactoryResetTimeMs();
long now = System.currentTimeMillis();
long throttleDurationMin = SystemProperties.getLong(PROP_THROTTLE_DURATION_MIN_FLAG,
DEFAULT_FACTORY_RESET_THROTTLE_DURATION_MIN);
diff --git a/packages/PackageInstaller/Android.bp b/packages/PackageInstaller/Android.bp
index 98a5a67..b646da4 100644
--- a/packages/PackageInstaller/Android.bp
+++ b/packages/PackageInstaller/Android.bp
@@ -59,69 +59,5 @@
lint: {
error_checks: ["Recycle"],
},
-}
-
-android_app {
- name: "PackageInstaller_tablet",
- defaults: ["platform_app_defaults"],
-
- srcs: [
- "src/**/*.java",
- "src/**/*.kt",
- ],
-
- certificate: "platform",
- privileged: true,
- platform_apis: false,
- sdk_version: "system_current",
- rename_resources_package: false,
- overrides: ["PackageInstaller"],
-
- static_libs: [
- "xz-java",
- "androidx.leanback_leanback",
- "androidx.fragment_fragment",
- "androidx.lifecycle_lifecycle-livedata",
- "androidx.lifecycle_lifecycle-extensions",
- "android.content.pm.flags-aconfig-java",
- "android.os.flags-aconfig-java",
- ],
- aaptflags: ["--product tablet"],
-
- lint: {
- error_checks: ["Recycle"],
- },
-}
-
-android_app {
- name: "PackageInstaller_tv",
- defaults: ["platform_app_defaults"],
-
- srcs: [
- "src/**/*.java",
- "src/**/*.kt",
- ],
-
- certificate: "platform",
- privileged: true,
- platform_apis: false,
- sdk_version: "system_current",
- rename_resources_package: false,
- overrides: ["PackageInstaller"],
-
- static_libs: [
- "xz-java",
- "androidx.leanback_leanback",
- "androidx.annotation_annotation",
- "androidx.fragment_fragment",
- "androidx.lifecycle_lifecycle-livedata",
- "androidx.lifecycle_lifecycle-extensions",
- "android.content.pm.flags-aconfig-java",
- "android.os.flags-aconfig-java",
- ],
- aaptflags: ["--product tv"],
-
- lint: {
- error_checks: ["Recycle"],
- },
+ generate_product_characteristics_rro: true,
}
diff --git a/packages/PackageInstaller/res/values-night/themes.xml b/packages/PackageInstaller/res/values-night/themes.xml
index 18320f7..a5b82b3 100644
--- a/packages/PackageInstaller/res/values-night/themes.xml
+++ b/packages/PackageInstaller/res/values-night/themes.xml
@@ -20,6 +20,9 @@
<style name="Theme.AlertDialogActivity"
parent="@android:style/Theme.DeviceDefault.Dialog.Alert">
<item name="alertDialogStyle">@style/AlertDialog</item>
+ <item name="android:windowActionBar">false</item>
+ <item name="android:windowNoTitle">true</item>
+ <item name="android:windowAnimationStyle">@null</item>
</style>
</resources>
diff --git a/packages/SettingsLib/Spa/build.gradle.kts b/packages/SettingsLib/Spa/build.gradle.kts
index ec519ca..463e9be 100644
--- a/packages/SettingsLib/Spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/build.gradle.kts
@@ -29,7 +29,7 @@
allprojects {
extra["androidTop"] = androidTop
- extra["jetpackComposeVersion"] = "1.7.0-alpha01"
+ extra["jetpackComposeVersion"] = "1.7.0-alpha02"
}
subprojects {
diff --git a/packages/SettingsLib/Spa/gradle/libs.versions.toml b/packages/SettingsLib/Spa/gradle/libs.versions.toml
index f6fbc02..fe378c2 100644
--- a/packages/SettingsLib/Spa/gradle/libs.versions.toml
+++ b/packages/SettingsLib/Spa/gradle/libs.versions.toml
@@ -16,7 +16,7 @@
[versions]
agp = "8.2.2"
-compose-compiler = "1.5.8"
+compose-compiler = "1.5.9"
dexmaker-mockito = "2.28.3"
jvm = "17"
kotlin = "1.9.22"
diff --git a/packages/SettingsLib/Spa/spa/build.gradle.kts b/packages/SettingsLib/Spa/spa/build.gradle.kts
index 08a8797..2259bd74 100644
--- a/packages/SettingsLib/Spa/spa/build.gradle.kts
+++ b/packages/SettingsLib/Spa/spa/build.gradle.kts
@@ -57,13 +57,13 @@
api("androidx.slice:slice-builders:1.1.0-alpha02")
api("androidx.slice:slice-core:1.1.0-alpha02")
api("androidx.slice:slice-view:1.1.0-alpha02")
- api("androidx.compose.material3:material3:1.2.0-rc01")
+ api("androidx.compose.material3:material3:1.2.0")
api("androidx.compose.material:material-icons-extended:$jetpackComposeVersion")
api("androidx.compose.runtime:runtime-livedata:$jetpackComposeVersion")
api("androidx.compose.ui:ui-tooling-preview:$jetpackComposeVersion")
api("androidx.lifecycle:lifecycle-livedata-ktx")
api("androidx.lifecycle:lifecycle-runtime-compose")
- api("androidx.navigation:navigation-compose:2.8.0-alpha01")
+ api("androidx.navigation:navigation-compose:2.8.0-alpha02")
api("com.github.PhilJay:MPAndroidChart:v3.1.0-alpha")
api("com.google.android.material:material:1.7.0-alpha03")
debugApi("androidx.compose.ui:ui-tooling:$jetpackComposeVersion")
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/ListPreference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/ListPreference.kt
index a0149da..1a04bb8 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/ListPreference.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/ListPreference.kt
@@ -37,11 +37,13 @@
import androidx.compose.ui.semantics.Role
import com.android.settingslib.spa.framework.theme.SettingsDimension
import com.android.settingslib.spa.widget.dialog.SettingsDialog
+import com.android.settingslib.spa.widget.ui.SettingsBody
import com.android.settingslib.spa.widget.ui.SettingsDialogItem
data class ListPreferenceOption(
val id: Int,
val text: String,
+ val summary: String = String()
)
/**
@@ -129,6 +131,14 @@
) {
RadioButton(selected = selected, onClick = null, enabled = enabled)
Spacer(modifier = Modifier.width(SettingsDimension.itemPaddingEnd))
- SettingsDialogItem(text = option.text, enabled = enabled)
+ Column {
+ SettingsDialogItem(text = option.text, enabled = enabled)
+ if (option.summary != String()) {
+ SettingsBody(
+ body = option.summary,
+ maxLines = 1
+ )
+ }
+ }
}
}
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetPreference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetPreference.kt
index e36572f..3216e37 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetPreference.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetPreference.kt
@@ -24,7 +24,6 @@
import androidx.compose.foundation.layout.size
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
@@ -46,14 +45,14 @@
verticalAlignment = Alignment.CenterVertically,
) {
Box(modifier = Modifier.weight(1f)) {
- Preference(remember {
+ Preference(
object : PreferenceModel {
override val title = title
override val summary = summary
override val icon = icon
override val onClick = onClick
}
- })
+ )
}
PreferenceDivider()
widget()
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/ListPreferenceTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/ListPreferenceTest.kt
index 796ac48..417ce6e 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/ListPreferenceTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/ListPreferenceTest.kt
@@ -123,6 +123,26 @@
}
@Test
+ fun click_optionsNotEmptyAndItemHasSummary_itemShowSummary() {
+ composeTestRule.setContent {
+ ListPreference(remember {
+ object : ListPreferenceModel {
+ override val title = TITLE
+ override val options =
+ listOf(ListPreferenceOption(id = 1, text = "A", summary = "A_Summary"))
+ override val selectedId = mutableIntStateOf(1)
+ override val onIdSelected: (Int) -> Unit = {}
+ }
+ })
+ }
+
+ composeTestRule.onNodeWithText(TITLE).performClick()
+
+ composeTestRule.onDialogText(TITLE).assertIsDisplayed()
+ composeTestRule.onNodeWithText("A_Summary").assertIsDisplayed()
+ }
+
+ @Test
fun select() {
val selectedId = mutableIntStateOf(1)
composeTestRule.setContent {
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 1092a16..9588e50 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1129,7 +1129,7 @@
<!-- [CHAR_LIMIT=80] Label for battery level chart when charge been limited -->
<string name="power_charging_limited"><xliff:g id="level">%1$s</xliff:g> - Charging optimized</string>
<!-- [CHAR_LIMIT=80] Label for battery charging future pause -->
- <string name="power_charging_future_paused"><xliff:g id="level">%1$s</xliff:g> - Charging optimized</string>
+ <string name="power_charging_future_paused"><xliff:g id="level">%1$s</xliff:g> - Charging</string>
<!-- Battery Info screen. Value for a status item. Used for diagnostic info screens, precise translation isn't needed -->
<string name="battery_info_status_unknown">Unknown</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
index 249fa7f..e489bc5 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
@@ -1702,7 +1702,8 @@
}
public boolean isPrivateProfile() {
- return UserManager.USER_TYPE_PROFILE_PRIVATE.equals(mProfileType);
+ return android.os.Flags.allowPrivateProfile()
+ && UserManager.USER_TYPE_PROFILE_PRIVATE.equals(mProfileType);
}
/**
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index 581c7de..a376c1f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -92,13 +92,13 @@
}
}
- protected String mPackageName;
+ @NonNull protected final String mPackageName;
private MediaDevice mCurrentConnectedDevice;
private final LocalBluetoothManager mBluetoothManager;
private final Map<String, RouteListingPreference.Item> mPreferenceItemMap =
new ConcurrentHashMap<>();
- public InfoMediaManager(
+ /* package */ InfoMediaManager(
Context context,
@NonNull String packageName,
Notification notification,
@@ -112,7 +112,7 @@
/** Creates an instance of InfoMediaManager. */
public static InfoMediaManager createInstance(
Context context,
- String packageName,
+ @Nullable String packageName,
Notification notification,
LocalBluetoothManager localBluetoothManager) {
@@ -148,8 +148,7 @@
}
private void updateRouteListingPreference() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE
- && !TextUtils.isEmpty(mPackageName)) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
RouteListingPreference routeListingPreference =
getRouteListingPreference();
Api34Impl.onRouteListingPreferenceUpdated(routeListingPreference,
@@ -218,11 +217,7 @@
protected final void rebuildDeviceList() {
mMediaDevices.clear();
mCurrentConnectedDevice = null;
- if (TextUtils.isEmpty(mPackageName)) {
- buildAllRoutes();
- } else {
- buildAvailableRoutes();
- }
+ buildAvailableRoutes();
}
protected final void notifyCurrentConnectedDeviceChanged() {
@@ -250,12 +245,8 @@
return;
}
- if (TextUtils.isEmpty(mPackageName)) {
- connectDeviceWithoutPackageName(device);
- } else {
- device.setConnectedRecord();
- transferToRoute(device.mRouteInfo);
- }
+ device.setConnectedRecord();
+ transferToRoute(device.mRouteInfo);
}
/**
@@ -265,11 +256,6 @@
* @return If add device successful return {@code true}, otherwise return {@code false}
*/
boolean addDeviceToPlayMedia(MediaDevice device) {
- if (TextUtils.isEmpty(mPackageName)) {
- Log.w(TAG, "addDeviceToPlayMedia() package name is null or empty!");
- return false;
- }
-
final RoutingSessionInfo info = getRoutingSessionInfo();
if (info == null || !info.getSelectableRoutes().contains(device.mRouteInfo.getId())) {
Log.w(TAG, "addDeviceToPlayMedia() Ignoring selecting a non-selectable device : "
@@ -306,7 +292,6 @@
boolean preferRouteListingOrdering() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE
- && !TextUtils.isEmpty(mPackageName)
&& Api34Impl.preferRouteListingOrdering(getRouteListingPreference());
}
@@ -326,11 +311,6 @@
* @return If device stop successful return {@code true}, otherwise return {@code false}
*/
boolean removeDeviceFromPlayMedia(MediaDevice device) {
- if (TextUtils.isEmpty(mPackageName)) {
- Log.w(TAG, "removeDeviceFromMedia() package name is null or empty!");
- return false;
- }
-
final RoutingSessionInfo info = getRoutingSessionInfo();
if (info == null || !info.getSelectedRoutes().contains(device.mRouteInfo.getId())) {
Log.w(TAG, "removeDeviceFromMedia() Ignoring deselecting a non-deselectable device : "
@@ -346,11 +326,6 @@
* Release session to stop playing media on MediaDevice.
*/
boolean releaseSession() {
- if (TextUtils.isEmpty(mPackageName)) {
- Log.w(TAG, "releaseSession() package name is null or empty!");
- return false;
- }
-
final RoutingSessionInfo sessionInfo = getRoutingSessionInfo();
if (sessionInfo == null) {
Log.w(TAG, "releaseSession() Ignoring release session : " + mPackageName);
@@ -367,11 +342,6 @@
*/
@NonNull
List<MediaDevice> getSelectableMediaDevices() {
- if (TextUtils.isEmpty(mPackageName)) {
- Log.w(TAG, "getSelectableMediaDevices() package name is null or empty!");
- return Collections.emptyList();
- }
-
final RoutingSessionInfo info = getRoutingSessionInfo();
if (info == null) {
Log.w(TAG, "getSelectableMediaDevices() cannot find selectable MediaDevice from : "
@@ -394,11 +364,6 @@
*/
@NonNull
List<MediaDevice> getDeselectableMediaDevices() {
- if (TextUtils.isEmpty(mPackageName)) {
- Log.d(TAG, "getDeselectableMediaDevices() package name is null or empty!");
- return Collections.emptyList();
- }
-
final RoutingSessionInfo info = getRoutingSessionInfo();
if (info == null) {
Log.d(TAG, "getDeselectableMediaDevices() cannot find deselectable MediaDevice from : "
@@ -462,11 +427,6 @@
* @param volume the value of volume
*/
void adjustSessionVolume(int volume) {
- if (TextUtils.isEmpty(mPackageName)) {
- Log.w(TAG, "adjustSessionVolume() package name is null or empty!");
- return;
- }
-
final RoutingSessionInfo info = getRoutingSessionInfo();
if (info == null) {
Log.w(TAG, "adjustSessionVolume() can't found corresponding RoutingSession with : "
@@ -484,11 +444,6 @@
* @return maximum volume of the session, and return -1 if not found.
*/
public int getSessionVolumeMax() {
- if (TextUtils.isEmpty(mPackageName)) {
- Log.w(TAG, "getSessionVolumeMax() package name is null or empty!");
- return -1;
- }
-
final RoutingSessionInfo info = getRoutingSessionInfo();
if (info == null) {
Log.w(TAG, "getSessionVolumeMax() can't find corresponding RoutingSession with : "
@@ -505,11 +460,6 @@
* @return current volume of the session, and return -1 if not found.
*/
public int getSessionVolume() {
- if (TextUtils.isEmpty(mPackageName)) {
- Log.w(TAG, "getSessionVolume() package name is null or empty!");
- return -1;
- }
-
final RoutingSessionInfo info = getRoutingSessionInfo();
if (info == null) {
Log.w(TAG, "getSessionVolume() can't find corresponding RoutingSession with : "
@@ -521,11 +471,6 @@
}
CharSequence getSessionName() {
- if (TextUtils.isEmpty(mPackageName)) {
- Log.w(TAG, "Unable to get session name. The package name is null or empty!");
- return null;
- }
-
final RoutingSessionInfo info = getRoutingSessionInfo();
if (info == null) {
Log.w(TAG, "Unable to get session name for package: " + mPackageName);
@@ -548,20 +493,6 @@
// MediaRoute2Info.getType was made public on API 34, but exists since API 30.
@SuppressWarnings("NewApi")
- private void buildAllRoutes() {
- for (MediaRoute2Info route : getAllRoutes()) {
- if (DEBUG) {
- Log.d(TAG, "buildAllRoutes() route : " + route.getName() + ", volume : "
- + route.getVolume() + ", type : " + route.getType());
- }
- if (route.isSystemRoute()) {
- addMediaDevice(route);
- }
- }
- }
-
- // MediaRoute2Info.getType was made public on API 34, but exists since API 30.
- @SuppressWarnings("NewApi")
private synchronized void buildAvailableRoutes() {
for (MediaRoute2Info route : getAvailableRoutes()) {
if (DEBUG) {
@@ -594,8 +525,7 @@
infos.add(transferableRoute);
}
}
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE
- && !TextUtils.isEmpty(mPackageName)) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
RouteListingPreference routeListingPreference = getRouteListingPreference();
if (routeListingPreference != null) {
final List<RouteListingPreference.Item> preferenceRouteListing =
@@ -679,7 +609,7 @@
break;
}
- if (mediaDevice != null && !TextUtils.isEmpty(mPackageName)
+ if (mediaDevice != null
&& getRoutingSessionInfo().getSelectedRoutes().contains(route.getId())) {
mediaDevice.setState(STATE_SELECTED);
if (mCurrentConnectedDevice == null) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java
index 97bbf12..cf8906d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/ManagerInfoMediaManager.java
@@ -51,9 +51,9 @@
private final Executor mExecutor = Executors.newSingleThreadExecutor();
- public ManagerInfoMediaManager(
+ /* package */ ManagerInfoMediaManager(
Context context,
- String packageName,
+ @NonNull String packageName,
Notification notification,
LocalBluetoothManager localBluetoothManager) {
super(context, packageName, notification, localBluetoothManager);
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/NoOpInfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/NoOpInfoMediaManager.java
index 9d578bc..5b1c8ef 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/NoOpInfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/NoOpInfoMediaManager.java
@@ -41,7 +41,7 @@
NoOpInfoMediaManager(
Context context,
- String packageName,
+ @NonNull String packageName,
Notification notification,
LocalBluetoothManager localBluetoothManager) {
super(context, packageName, notification, localBluetoothManager);
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/RouterInfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/RouterInfoMediaManager.java
index aef09ac..c8c8b672 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/RouterInfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/RouterInfoMediaManager.java
@@ -65,9 +65,9 @@
};
// TODO (b/321969740): Plumb target UserHandle between UMO and RouterInfoMediaManager.
- public RouterInfoMediaManager(
+ /* package */ RouterInfoMediaManager(
Context context,
- String packageName,
+ @NonNull String packageName,
Notification notification,
LocalBluetoothManager localBluetoothManager)
throws PackageNotAvailableException {
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/ApplicationsStateTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/ApplicationsStateTest.java
index 213a66e..1ad7d49 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/ApplicationsStateTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/ApplicationsStateTest.java
@@ -22,21 +22,30 @@
import static org.mockito.Mockito.when;
import android.content.pm.ApplicationInfo;
+import android.os.Flags;
import android.os.UserManager;
+import android.platform.test.flag.junit.SetFlagsRule;
+
+import androidx.test.core.app.ApplicationProvider;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@RunWith(JUnit4.class)
public class ApplicationsStateTest {
+ private static final int APP_ENTRY_ID = 1;
private ApplicationsState.AppEntry mEntry;
+ @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Before
public void setUp() {
- mEntry = mock(ApplicationsState.AppEntry.class);
- mEntry.info = mock(ApplicationInfo.class);
+ mEntry = new ApplicationsState.AppEntry(
+ ApplicationProvider.getApplicationContext(),
+ mock(ApplicationInfo.class),
+ APP_ENTRY_ID);
}
@Test
@@ -310,6 +319,8 @@
@Test
public void testPrivateProfileFilterDisplaysCorrectApps() {
+ mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
+
mEntry.showInPersonalTab = true;
mEntry.mProfileType = UserManager.USER_TYPE_FULL_SYSTEM;
assertThat(ApplicationsState.FILTER_PERSONAL.filterApp(mEntry)).isTrue();
@@ -320,4 +331,14 @@
assertThat(ApplicationsState.FILTER_PERSONAL.filterApp(mEntry)).isFalse();
assertThat(ApplicationsState.FILTER_PRIVATE_PROFILE.filterApp(mEntry)).isTrue();
}
+
+ @Test
+ public void testPrivateProfileFilterDisplaysCorrectAppsWhenFlagDisabled() {
+ mSetFlagsRule.disableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
+
+ mEntry.showInPersonalTab = false;
+ mEntry.mProfileType = UserManager.USER_TYPE_PROFILE_PRIVATE;
+ assertThat(ApplicationsState.FILTER_PERSONAL.filterApp(mEntry)).isFalse();
+ assertThat(ApplicationsState.FILTER_PRIVATE_PROFILE.filterApp(mEntry)).isFalse();
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
index f0330c4..290e63ca 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
@@ -212,28 +212,6 @@
}
@Test
- public void onRouteAdded_buildAllRoutes_shouldAddMediaDevice() {
- final MediaRoute2Info info = mock(MediaRoute2Info.class);
- when(info.getId()).thenReturn(TEST_ID);
- when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
- when(info.isSystemRoute()).thenReturn(true);
-
- final List<MediaRoute2Info> routes = new ArrayList<>();
- routes.add(info);
- mShadowRouter2Manager.setAllRoutes(routes);
-
- final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID);
- assertThat(mediaDevice).isNull();
-
- mInfoMediaManager.mPackageName = "";
- mInfoMediaManager.mMediaRouterCallback.onRoutesUpdated();
-
- final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0);
- assertThat(infoDevice.getId()).isEqualTo(TEST_ID);
- assertThat(mInfoMediaManager.mMediaDevices).hasSize(routes.size());
- }
-
- @Test
public void onPreferredFeaturesChanged_samePackageName_shouldAddMediaDevice() {
final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
final RoutingSessionInfo sessionInfo = mock(RoutingSessionInfo.class);
@@ -436,29 +414,6 @@
}
@Test
- public void onRoutesChanged_buildAllRoutes_shouldAddMediaDevice() {
- final MediaRoute2Info info = mock(MediaRoute2Info.class);
- when(info.getId()).thenReturn(TEST_ID);
- when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
- when(info.isSystemRoute()).thenReturn(true);
- when(info.getDeduplicationIds()).thenReturn(Set.of());
-
- final List<MediaRoute2Info> routes = new ArrayList<>();
- routes.add(info);
- mShadowRouter2Manager.setAllRoutes(routes);
-
- final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID);
- assertThat(mediaDevice).isNull();
-
- mInfoMediaManager.mPackageName = "";
- mInfoMediaManager.mMediaRouterCallback.onRoutesUpdated();
-
- final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0);
- assertThat(infoDevice.getId()).isEqualTo(TEST_ID);
- assertThat(mInfoMediaManager.mMediaDevices).hasSize(routes.size());
- }
-
- @Test
public void hasPreferenceRouteListing_oldSdkVersion_returnsFalse() {
assertThat(mInfoMediaManager.preferRouteListingOrdering()).isFalse();
}
@@ -587,36 +542,6 @@
}
@Test
- public void onRoutesRemoved_buildAllRoutes_shouldAddMediaDevice() {
- final MediaRoute2Info info = mock(MediaRoute2Info.class);
- when(info.getId()).thenReturn(TEST_ID);
- when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
- when(info.isSystemRoute()).thenReturn(true);
-
- final List<MediaRoute2Info> routes = new ArrayList<>();
- routes.add(info);
- when(mRouterManager.getAllRoutes()).thenReturn(routes);
-
- final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID);
- assertThat(mediaDevice).isNull();
-
- mInfoMediaManager.mPackageName = "";
- mInfoMediaManager.mMediaRouterCallback.onRoutesUpdated();
-
- final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0);
- assertThat(infoDevice.getId()).isEqualTo(TEST_ID);
- assertThat(mInfoMediaManager.mMediaDevices).hasSize(routes.size());
- }
-
- @Test
- public void addDeviceToPlayMedia_packageNameIsNull_returnFalse() {
- mInfoMediaManager.mPackageName = null;
- final MediaDevice device = mock(MediaDevice.class);
-
- assertThat(mInfoMediaManager.addDeviceToPlayMedia(device)).isFalse();
- }
-
- @Test
public void addDeviceToPlayMedia_containSelectableRoutes_returnTrue() {
final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
final RoutingSessionInfo info = mock(RoutingSessionInfo.class);
@@ -660,14 +585,6 @@
}
@Test
- public void removeDeviceFromMedia_packageNameIsNull_returnFalse() {
- mInfoMediaManager.mPackageName = null;
- final MediaDevice device = mock(MediaDevice.class);
-
- assertThat(mInfoMediaManager.removeDeviceFromPlayMedia(device)).isFalse();
- }
-
- @Test
public void removeDeviceFromMedia_containSelectedRoutes_returnTrue() {
final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
final RoutingSessionInfo info = mock(RoutingSessionInfo.class);
@@ -711,13 +628,6 @@
}
@Test
- public void getSelectableMediaDevice_packageNameIsNull_returnFalse() {
- mInfoMediaManager.mPackageName = null;
-
- assertThat(mInfoMediaManager.getSelectableMediaDevices()).isEmpty();
- }
-
- @Test
public void getSelectableMediaDevice_notContainPackageName_returnEmpty() {
final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
final RoutingSessionInfo info = mock(RoutingSessionInfo.class);
@@ -730,13 +640,6 @@
}
@Test
- public void getDeselectableMediaDevice_packageNameIsNull_returnFalse() {
- mInfoMediaManager.mPackageName = null;
-
- assertThat(mInfoMediaManager.getDeselectableMediaDevices()).isEmpty();
- }
-
- @Test
public void getDeselectableMediaDevice_checkList() {
final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
final RoutingSessionInfo info = mock(RoutingSessionInfo.class);
@@ -761,20 +664,6 @@
}
@Test
- public void adjustSessionVolume_packageNameIsNull_noCrash() {
- mInfoMediaManager.mPackageName = null;
-
- mInfoMediaManager.adjustSessionVolume(10);
- }
-
- @Test
- public void getSessionVolumeMax_packageNameIsNull_returnNotFound() {
- mInfoMediaManager.mPackageName = null;
-
- assertThat(mInfoMediaManager.getSessionVolumeMax()).isEqualTo(-1);
- }
-
- @Test
public void getSessionVolumeMax_containPackageName_returnMaxVolume() {
final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
final RoutingSessionInfo info = mock(RoutingSessionInfo.class);
@@ -800,13 +689,6 @@
}
@Test
- public void getSessionVolume_packageNameIsNull_returnNotFound() {
- mInfoMediaManager.mPackageName = null;
-
- assertThat(mInfoMediaManager.getSessionVolume()).isEqualTo(-1);
- }
-
- @Test
public void getSessionVolume_containPackageName_returnMaxVolume() {
final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
final RoutingSessionInfo info = mock(RoutingSessionInfo.class);
@@ -841,13 +723,6 @@
}
@Test
- public void releaseSession_packageNameIsNull_returnFalse() {
- mInfoMediaManager.mPackageName = null;
-
- assertThat(mInfoMediaManager.releaseSession()).isFalse();
- }
-
- @Test
public void releaseSession_removeSuccessfully_returnTrue() {
final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
final RoutingSessionInfo info = mock(RoutingSessionInfo.class);
@@ -860,13 +735,6 @@
}
@Test
- public void getSessionName_packageNameIsNull_returnNull() {
- mInfoMediaManager.mPackageName = null;
-
- assertThat(mInfoMediaManager.getSessionName()).isNull();
- }
-
- @Test
public void getSessionName_routeSessionInfoIsNull_returnNull() {
final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
final RoutingSessionInfo info = null;
@@ -942,32 +810,6 @@
}
@Test
- public void onTransferred_buildAllRoutes_shouldAddMediaDevice() {
- final MediaRoute2Info info = mock(MediaRoute2Info.class);
- final RoutingSessionInfo sessionInfo = mock(RoutingSessionInfo.class);
- mInfoMediaManager.registerCallback(mCallback);
-
- when(info.getId()).thenReturn(TEST_ID);
- when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
- when(info.isSystemRoute()).thenReturn(true);
-
- final List<MediaRoute2Info> routes = new ArrayList<>();
- routes.add(info);
- mShadowRouter2Manager.setAllRoutes(routes);
-
- final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID);
- assertThat(mediaDevice).isNull();
-
- mInfoMediaManager.mPackageName = "";
- mInfoMediaManager.mMediaRouterCallback.onTransferred(sessionInfo, sessionInfo);
-
- final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0);
- assertThat(infoDevice.getId()).isEqualTo(TEST_ID);
- assertThat(mInfoMediaManager.mMediaDevices).hasSize(routes.size());
- verify(mCallback).onConnectedDeviceChanged(null);
- }
-
- @Test
public void onSessionUpdated_shouldDispatchDeviceListAdded() {
final MediaRoute2Info info = mock(MediaRoute2Info.class);
when(info.getId()).thenReturn(TEST_ID);
@@ -978,7 +820,6 @@
routes.add(info);
mShadowRouter2Manager.setAllRoutes(routes);
- mInfoMediaManager.mPackageName = "";
mInfoMediaManager.registerCallback(mCallback);
mInfoMediaManager.mMediaRouterCallback.onSessionUpdated(mock(RoutingSessionInfo.class));
diff --git a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowRouter2Manager.java b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowRouter2Manager.java
index fde378f..3adb204 100644
--- a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowRouter2Manager.java
+++ b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowRouter2Manager.java
@@ -27,12 +27,13 @@
import org.robolectric.annotation.Implements;
import org.robolectric.shadow.api.Shadow;
+import java.util.ArrayList;
import java.util.List;
@Implements(MediaRouter2Manager.class)
public class ShadowRouter2Manager {
- private List<MediaRoute2Info> mAvailableRoutes;
+ private List<MediaRoute2Info> mAvailableRoutes = new ArrayList<>();
private List<MediaRoute2Info> mAllRoutes;
private List<MediaRoute2Info> mDeselectableRoutes;
private List<RoutingSessionInfo> mRemoteSessions;
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java b/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java
index cd35f67..be480b9 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/GenerationRegistry.java
@@ -308,11 +308,8 @@
final long token = proto.start(GenerationRegistryProto.BACKING_STORES);
final int key = mKeyToBackingStoreMap.keyAt(i);
proto.write(BackingStoreProto.KEY, key);
- try {
- proto.write(BackingStoreProto.BACKING_STORE_SIZE,
- mKeyToBackingStoreMap.valueAt(i).size());
- } catch (IOException ignore) {
- }
+ proto.write(BackingStoreProto.BACKING_STORE_SIZE,
+ mKeyToBackingStoreMap.valueAt(i).size());
proto.write(BackingStoreProto.NUM_CACHED_ENTRIES,
mKeyToIndexMapMap.get(key).size());
final ArrayMap<String, Integer> indexMap = mKeyToIndexMapMap.get(key);
@@ -357,10 +354,7 @@
pw.print("_Backing store for type:"); pw.print(SettingsState.settingTypeToString(
SettingsState.getTypeFromKey(key)));
pw.print(" user:"); pw.print(SettingsState.getUserIdFromKey(key));
- try {
- pw.print(" size:" + mKeyToBackingStoreMap.valueAt(i).size());
- } catch (IOException ignore) {
- }
+ pw.print(" size:" + mKeyToBackingStoreMap.valueAt(i).size());
pw.println(" cachedEntries:" + mKeyToIndexMapMap.get(key).size());
final ArrayMap<String, Integer> indexMap = mKeyToIndexMapMap.get(key);
final MemoryIntArray backingStore = getBackingStoreLocked(key,
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/aconfig/Android.bp b/packages/SystemUI/accessibility/accessibilitymenu/aconfig/Android.bp
index 6d63409..f74e59a 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/aconfig/Android.bp
+++ b/packages/SystemUI/accessibility/accessibilitymenu/aconfig/Android.bp
@@ -1,3 +1,7 @@
+package {
+ default_team: "trendy_team_system_ui_please_use_a_more_specific_subteam_if_possible_",
+}
+
aconfig_declarations {
name: "com_android_a11y_menu_flags",
package: "com.android.systemui.accessibility.accessibilitymenu",
diff --git a/packages/SystemUI/aconfig/Android.bp b/packages/SystemUI/aconfig/Android.bp
index 5d0847c..76cb6bd 100644
--- a/packages/SystemUI/aconfig/Android.bp
+++ b/packages/SystemUI/aconfig/Android.bp
@@ -20,6 +20,7 @@
*/
package {
+ default_team: "trendy_team_system_ui_please_use_a_more_specific_subteam_if_possible_",
default_visibility: [
"//visibility:override",
"//frameworks/base/packages/SystemUI:__subpackages__",
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 6f34f05..4ed1965 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -29,6 +29,13 @@
}
flag {
+ name: "notification_color_update_logger"
+ namespace: "systemui"
+ description: "Enabled debug logging and dumping of notification color updates."
+ bug: "294347738"
+}
+
+flag {
name: "notifications_footer_view_refactor"
namespace: "systemui"
description: "Enables the refactored version of the footer view in the notification shade "
@@ -67,6 +74,16 @@
}
flag {
+ name: "notifications_background_media_icons"
+ namespace: "systemui"
+ description: "Updates icons for media notifications in the background."
+ bug: "315143160"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "nssl_falsing_fix"
namespace: "systemui"
description: "Minor touch changes to prevent falsing errors in NSSL"
@@ -295,12 +312,15 @@
}
flag {
- name: "centralized_status_bar_dimens_refactor"
+ name: "centralized_status_bar_height_fix"
namespace: "systemui"
description: "Refactors shade header and keyguard status bar to read status bar dimens from a"
" central place, instead of reading resources directly. This is to take into account display"
" cutouts and other special cases. "
- bug: "317199366"
+ bug: "317016114"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
}
flag {
@@ -431,6 +451,16 @@
}
flag {
+ name: "slice_manager_binder_call_background"
+ namespace: "systemui"
+ description: "Move the ISliceManager#getPinnedSpecs binder call to the background thread."
+ bug: "322745650"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "register_new_wallet_card_in_background"
namespace: "systemui"
description: "Decide whether the call to registerNewWalletCards method should be issued on background thread."
@@ -450,3 +480,12 @@
}
}
+flag {
+ name: "register_zen_mode_content_observer_background"
+ namespace: "systemui"
+ description: "Decide whether to register zen mode content observers in the background thread."
+ bug: "324515627"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewTransitionAnimatorController.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewTransitionAnimatorController.kt
index e4dc9be..5d5f12e 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewTransitionAnimatorController.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/GhostedViewTransitionAnimatorController.kt
@@ -123,7 +123,7 @@
val views = LinkedList<View>().apply { add(view) }
while (views.isNotEmpty()) {
- val v = views.removeFirst()
+ val v = views.removeAt(0)
if (v.background != null) {
return v.background
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/SplitShadeBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/SplitShadeBlueprint.kt
index 660fc5a..44fe883 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/SplitShadeBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/SplitShadeBlueprint.kt
@@ -127,7 +127,7 @@
}
with(notificationSection) {
val splitShadeTopMargin: Dp =
- if (Flags.centralizedStatusBarDimensRefactor()) {
+ if (Flags.centralizedStatusBarHeightFix()) {
largeScreenHeaderHelper.getLargeScreenHeaderHeight().dp
} else {
dimensionResource(
diff --git a/packages/SystemUI/compose/scene/Android.bp b/packages/SystemUI/compose/scene/Android.bp
index 3424085..af1172b 100644
--- a/packages/SystemUI/compose/scene/Android.bp
+++ b/packages/SystemUI/compose/scene/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_system_ui_please_use_a_more_specific_subteam_if_possible_",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_base_packages_SystemUI_license"
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
index 828e34d..2e781e6 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt
@@ -29,13 +29,17 @@
import androidx.compose.ui.graphics.CompositingStrategy
import androidx.compose.ui.graphics.drawscope.ContentDrawScope
import androidx.compose.ui.graphics.drawscope.scale
-import androidx.compose.ui.layout.IntermediateMeasureScope
+import androidx.compose.ui.layout.ApproachLayoutModifierNode
+import androidx.compose.ui.layout.ApproachMeasureScope
+import androidx.compose.ui.layout.LayoutCoordinates
+import androidx.compose.ui.layout.LookaheadScope
import androidx.compose.ui.layout.Measurable
+import androidx.compose.ui.layout.MeasureResult
import androidx.compose.ui.layout.Placeable
-import androidx.compose.ui.layout.intermediateLayout
import androidx.compose.ui.node.DrawModifierNode
import androidx.compose.ui.node.ModifierNodeElement
import androidx.compose.ui.platform.testTag
+import androidx.compose.ui.semantics.testTag
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.round
@@ -91,23 +95,7 @@
layoutImpl: SceneTransitionLayoutImpl,
scene: Scene,
key: ElementKey,
-): Modifier {
- return this.then(ElementModifier(layoutImpl, scene, key))
- // TODO(b/311132415): Move this into ElementNode once we can create a delegate
- // IntermediateLayoutModifierNode.
- .intermediateLayout { measurable, constraints ->
- // TODO(b/311132415): No need to fetch the element and sceneState from the map anymore
- // once this is merged into ElementNode.
- val element = layoutImpl.elements.getValue(key)
- val sceneState = element.sceneStates.getValue(scene.key)
-
- val placeable = measure(layoutImpl, scene, element, sceneState, measurable, constraints)
- layout(placeable.width, placeable.height) {
- place(layoutImpl, scene, element, sceneState, placeable, placementScope = this)
- }
- }
- .testTag(key.testTag)
-}
+): Modifier = this.then(ElementModifier(layoutImpl, scene, key)).testTag(key.testTag)
/**
* An element associated to [ElementNode]. Note that this element does not support updates as its
@@ -129,7 +117,7 @@
private var layoutImpl: SceneTransitionLayoutImpl,
private var scene: Scene,
private var key: ElementKey,
-) : Modifier.Node(), DrawModifierNode {
+) : Modifier.Node(), DrawModifierNode, ApproachLayoutModifierNode {
private var _element: Element? = null
private val element: Element
get() = _element!!
@@ -197,6 +185,31 @@
maybePruneMaps(layoutImpl, prevElement, prevSceneState)
}
+ override fun isMeasurementApproachComplete(lookaheadSize: IntSize): Boolean {
+ // TODO(b/324191441): Investigate whether making this check more complex (checking if this
+ // element is shared or transformed) would lead to better performance.
+ return layoutImpl.state.currentTransition == null
+ }
+
+ override fun Placeable.PlacementScope.isPlacementApproachComplete(
+ lookaheadCoordinates: LayoutCoordinates
+ ): Boolean {
+ // TODO(b/324191441): Investigate whether making this check more complex (checking if this
+ // element is shared or transformed) would lead to better performance.
+ return layoutImpl.state.currentTransition == null
+ }
+
+ @ExperimentalComposeUiApi
+ override fun ApproachMeasureScope.approachMeasure(
+ measurable: Measurable,
+ constraints: Constraints,
+ ): MeasureResult {
+ val placeable = measure(layoutImpl, scene, element, sceneState, measurable, constraints)
+ return layout(placeable.width, placeable.height) {
+ place(layoutImpl, scene, element, sceneState, placeable, placementScope = this)
+ }
+ }
+
override fun ContentDrawScope.draw() {
val drawScale = getDrawScale(layoutImpl, element, scene)
if (drawScale == Scale.Default) {
@@ -368,7 +381,7 @@
}
@OptIn(ExperimentalComposeUiApi::class)
-private fun IntermediateMeasureScope.measure(
+private fun ApproachMeasureScope.measure(
layoutImpl: SceneTransitionLayoutImpl,
scene: Scene,
element: Element,
@@ -431,7 +444,7 @@
}
@OptIn(ExperimentalComposeUiApi::class)
-private fun IntermediateMeasureScope.place(
+private fun ApproachMeasureScope.place(
layoutImpl: SceneTransitionLayoutImpl,
scene: Scene,
element: Element,
@@ -439,6 +452,8 @@
placeable: Placeable,
placementScope: Placeable.PlacementScope,
) {
+ this as LookaheadScope
+
with(placementScope) {
// Update the offset (relative to the SceneTransitionLayout) this element has in this scene
// when idle.
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt
index c8fbad4..76e7c95 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneGestureHandler.kt
@@ -348,6 +348,8 @@
// Compute the destination scene (and therefore offset) to settle in.
val offset = swipeTransition.dragOffset
val distance = swipeTransition.distance
+ var targetScene: Scene
+ var targetOffset: Float
if (
shouldCommitSwipe(
offset,
@@ -356,12 +358,24 @@
wasCommitted = swipeTransition._currentScene == toScene,
)
) {
- // Animate to the next scene
- animateTo(targetScene = toScene, targetOffset = distance)
+ targetScene = toScene
+ targetOffset = distance
} else {
- // Animate to the initial scene
- animateTo(targetScene = fromScene, targetOffset = 0f)
+ targetScene = fromScene
+ targetOffset = 0f
}
+
+ if (
+ targetScene != swipeTransition._currentScene &&
+ !layoutState.canChangeScene(targetScene.key)
+ ) {
+ // We wanted to change to a new scene but we are not allowed to, so we animate back
+ // to the current scene.
+ targetScene = swipeTransition._currentScene
+ targetOffset = if (targetScene == fromScene) 0f else distance
+ }
+
+ animateTo(targetScene = targetScene, targetOffset = targetOffset)
} else {
// We are doing an overscroll animation between scenes. In this case, we can also start
// from the idle position.
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
index 8c5a472..08399ff 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt
@@ -232,7 +232,12 @@
scene(state.transitionState.currentScene).userActions[Back]?.let { result ->
// TODO(b/290184746): Handle predictive back and use result.distance if
// specified.
- BackHandler { with(state) { coroutineScope.onChangeScene(result.toScene) } }
+ BackHandler {
+ val targetScene = result.toScene
+ if (state.canChangeScene(targetScene)) {
+ with(state) { coroutineScope.onChangeScene(targetScene) }
+ }
+ }
}
Box {
diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
index a8da551..662f33f 100644
--- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
+++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt
@@ -101,13 +101,30 @@
): TransitionState.Transition?
}
-/** Return a [MutableSceneTransitionLayoutState] initially idle at [initialScene]. */
+/**
+ * Return a [MutableSceneTransitionLayoutState] initially idle at [initialScene].
+ *
+ * @param initialScene the initial scene to which this state is initialized.
+ * @param transitions the [SceneTransitions] used when this state is transitioning between scenes.
+ * @param canChangeScene whether we can transition to the given scene. This is called when the user
+ * commits a transition to a new scene because of a [UserAction]. If [canChangeScene] returns
+ * `true`, then the gesture will be committed and we will animate to the other scene. Otherwise,
+ * the gesture will be cancelled and we will animate back to the current scene.
+ * @param stateLinks the [StateLink] connecting this [SceneTransitionLayoutState] to other
+ * [SceneTransitionLayoutState]s.
+ */
fun MutableSceneTransitionLayoutState(
initialScene: SceneKey,
transitions: SceneTransitions = SceneTransitions.Empty,
+ canChangeScene: (SceneKey) -> Boolean = { true },
stateLinks: List<StateLink> = emptyList(),
): MutableSceneTransitionLayoutState {
- return MutableSceneTransitionLayoutStateImpl(initialScene, transitions, stateLinks)
+ return MutableSceneTransitionLayoutStateImpl(
+ initialScene,
+ transitions,
+ canChangeScene,
+ stateLinks,
+ )
}
/**
@@ -120,18 +137,32 @@
* This is called when the user commits a transition to a new scene because of a [UserAction], for
* instance by triggering back navigation or by swiping to a new scene.
* @param transitions the definition of the transitions used to animate a change of scene.
+ * @param canChangeScene whether we can transition to the given scene. This is called when the user
+ * commits a transition to a new scene because of a [UserAction]. If [canChangeScene] returns
+ * `true`, then [onChangeScene] will be called right afterwards with the same [SceneKey]. If it
+ * returns `false`, the user action will be cancelled and we will animate back to the current
+ * scene.
+ * @param stateLinks the [StateLink] connecting this [SceneTransitionLayoutState] to other
+ * [SceneTransitionLayoutState]s.
*/
@Composable
fun updateSceneTransitionLayoutState(
currentScene: SceneKey,
onChangeScene: (SceneKey) -> Unit,
transitions: SceneTransitions = SceneTransitions.Empty,
+ canChangeScene: (SceneKey) -> Boolean = { true },
stateLinks: List<StateLink> = emptyList(),
): SceneTransitionLayoutState {
return remember {
- HoistedSceneTransitionLayoutScene(currentScene, transitions, onChangeScene, stateLinks)
+ HoistedSceneTransitionLayoutState(
+ currentScene,
+ transitions,
+ onChangeScene,
+ canChangeScene,
+ stateLinks,
+ )
}
- .apply { update(currentScene, onChangeScene, transitions, stateLinks) }
+ .apply { update(currentScene, onChangeScene, canChangeScene, transitions, stateLinks) }
}
@Stable
@@ -208,6 +239,9 @@
private val activeTransitionLinks = mutableMapOf<StateLink, LinkedTransition>()
+ /** Whether we can transition to the given [scene]. */
+ internal abstract fun canChangeScene(scene: SceneKey): Boolean
+
/**
* Called when the [current scene][TransitionState.currentScene] should be changed to [scene].
*
@@ -330,25 +364,30 @@
* A [SceneTransitionLayout] whose current scene/source of truth is hoisted (its current value comes
* from outside).
*/
-internal class HoistedSceneTransitionLayoutScene(
+internal class HoistedSceneTransitionLayoutState(
initialScene: SceneKey,
override var transitions: SceneTransitions,
private var changeScene: (SceneKey) -> Unit,
+ private var canChangeScene: (SceneKey) -> Boolean,
stateLinks: List<StateLink> = emptyList(),
) : BaseSceneTransitionLayoutState(initialScene, stateLinks) {
private val targetSceneChannel = Channel<SceneKey>(Channel.CONFLATED)
- override fun CoroutineScope.onChangeScene(scene: SceneKey) = changeScene(scene)
+ override fun canChangeScene(scene: SceneKey): Boolean = canChangeScene.invoke(scene)
+
+ override fun CoroutineScope.onChangeScene(scene: SceneKey) = changeScene.invoke(scene)
@Composable
fun update(
currentScene: SceneKey,
onChangeScene: (SceneKey) -> Unit,
+ canChangeScene: (SceneKey) -> Boolean,
transitions: SceneTransitions,
stateLinks: List<StateLink>,
) {
SideEffect {
this.changeScene = onChangeScene
+ this.canChangeScene = canChangeScene
this.transitions = transitions
this.stateLinks = stateLinks
@@ -361,7 +400,7 @@
// late.
val newKey = targetSceneChannel.tryReceive().getOrNull() ?: newKey
animateToScene(
- layoutState = this@HoistedSceneTransitionLayoutScene,
+ layoutState = this@HoistedSceneTransitionLayoutState,
target = newKey,
transitionKey = null,
)
@@ -374,6 +413,7 @@
internal class MutableSceneTransitionLayoutStateImpl(
initialScene: SceneKey,
override var transitions: SceneTransitions,
+ private val canChangeScene: (SceneKey) -> Boolean = { true },
stateLinks: List<StateLink> = emptyList(),
) : MutableSceneTransitionLayoutState, BaseSceneTransitionLayoutState(initialScene, stateLinks) {
override fun setTargetScene(
@@ -388,6 +428,8 @@
)
}
+ override fun canChangeScene(scene: SceneKey): Boolean = canChangeScene.invoke(scene)
+
override fun CoroutineScope.onChangeScene(scene: SceneKey) {
setTargetScene(scene, coroutineScope = this)
}
diff --git a/packages/SystemUI/compose/scene/tests/Android.bp b/packages/SystemUI/compose/scene/tests/Android.bp
index 13df35b..59cc63a 100644
--- a/packages/SystemUI/compose/scene/tests/Android.bp
+++ b/packages/SystemUI/compose/scene/tests/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_system_ui_please_use_a_more_specific_subteam_if_possible_",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_base_packages_SystemUI_license"
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
index c0de87a..33be1dc 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt
@@ -247,10 +247,9 @@
}
@Test
- fun elementIsReusedInSameSceneAndBetweenScenes() {
+ fun elementIsReusedBetweenScenes() {
var currentScene by mutableStateOf(TestScenes.SceneA)
var sceneCState by mutableStateOf(0)
- var sceneDState by mutableStateOf(0)
val key = TestElements.Foo
var nullableLayoutImpl: SceneTransitionLayoutImpl? = null
@@ -268,19 +267,6 @@
scene(TestScenes.SceneC) {
when (sceneCState) {
0 -> Row(Modifier.element(key)) {}
- 1 -> Column(Modifier.element(key)) {}
- else -> {
- /* Nothing */
- }
- }
- }
- scene(TestScenes.SceneD) {
- // We should be able to extract the modifier before assigning it to different
- // nodes.
- val childModifier = Modifier.element(key)
- when (sceneDState) {
- 0 -> Row(childModifier) {}
- 1 -> Column(childModifier) {}
else -> {
/* Nothing */
}
@@ -313,35 +299,10 @@
assertThat(layoutImpl.elements.getValue(key)).isSameInstanceAs(element)
assertThat(element.sceneStates.keys).containsExactly(TestScenes.SceneC)
- // Scene C, state 1: the same element is reused.
+ // Scene C, state 1: the element is removed from the map.
sceneCState = 1
rule.waitForIdle()
- assertThat(layoutImpl.elements.keys).containsExactly(key)
- assertThat(layoutImpl.elements.getValue(key)).isSameInstanceAs(element)
- assertThat(element.sceneStates.keys).containsExactly(TestScenes.SceneC)
-
- // Scene D, state 0: the same element is reused.
- currentScene = TestScenes.SceneD
- sceneDState = 0
- rule.waitForIdle()
-
- assertThat(layoutImpl.elements.keys).containsExactly(key)
- assertThat(layoutImpl.elements.getValue(key)).isSameInstanceAs(element)
- assertThat(element.sceneStates.keys).containsExactly(TestScenes.SceneD)
-
- // Scene D, state 1: the same element is reused.
- sceneDState = 1
- rule.waitForIdle()
-
- assertThat(layoutImpl.elements.keys).containsExactly(key)
- assertThat(layoutImpl.elements.getValue(key)).isSameInstanceAs(element)
- assertThat(element.sceneStates.keys).containsExactly(TestScenes.SceneD)
-
- // Scene D, state 2: the element is removed from the map.
- sceneDState = 2
- rule.waitForIdle()
-
assertThat(element.sceneStates).isEmpty()
assertThat(layoutImpl.elements).isEmpty()
}
diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt
index c91d298..fe53d5b 100644
--- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt
+++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneGestureHandlerTest.kt
@@ -51,8 +51,13 @@
private class TestGestureScope(
private val testScope: MonotonicClockTestScope,
) {
+ var canChangeScene: (SceneKey) -> Boolean = { true }
private val layoutState =
- MutableSceneTransitionLayoutStateImpl(SceneA, EmptyTestTransitions)
+ MutableSceneTransitionLayoutStateImpl(
+ SceneA,
+ EmptyTestTransitions,
+ canChangeScene = { canChangeScene(it) },
+ )
val mutableUserActionsA = mutableMapOf(Swipe.Up to SceneB, Swipe.Down to SceneC)
val mutableUserActionsB = mutableMapOf(Swipe.Up to SceneC, Swipe.Down to SceneA)
@@ -890,4 +895,41 @@
)
assertThat(transitionState).isNotSameInstanceAs(firstTransition)
}
+
+ @Test
+ fun blockTransition() = runGestureTest {
+ assertIdle(SceneA)
+
+ // Swipe up to scene B.
+ onDragStarted(overSlop = up(0.1f))
+ assertTransition(currentScene = SceneA, fromScene = SceneA, toScene = SceneB)
+
+ // Block the transition when the user release their finger.
+ canChangeScene = { false }
+ onDragStopped(velocity = -velocityThreshold)
+ advanceUntilIdle()
+ assertIdle(SceneA)
+ }
+
+ @Test
+ fun blockInterceptedTransition() = runGestureTest {
+ assertIdle(SceneA)
+
+ // Swipe up to B.
+ onDragStarted(overSlop = up(0.1f))
+ assertTransition(currentScene = SceneA, fromScene = SceneA, toScene = SceneB)
+ onDragStopped(velocity = -velocityThreshold)
+ assertTransition(currentScene = SceneB, fromScene = SceneA, toScene = SceneB)
+
+ // Intercept the transition and swipe down back to scene A.
+ assertThat(sceneGestureHandler.shouldImmediatelyIntercept(startedPosition = null)).isTrue()
+ onDragStartedImmediately()
+
+ // Block the transition when the user release their finger.
+ canChangeScene = { false }
+ onDragStopped(velocity = velocityThreshold)
+
+ advanceUntilIdle()
+ assertIdle(SceneB)
+ }
}
diff --git a/packages/SystemUI/compose/scene/tests/utils/Android.bp b/packages/SystemUI/compose/scene/tests/utils/Android.bp
index ff8fe75..9089e6a 100644
--- a/packages/SystemUI/compose/scene/tests/utils/Android.bp
+++ b/packages/SystemUI/compose/scene/tests/utils/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_system_ui_please_use_a_more_specific_subteam_if_possible_",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_base_packages_SystemUI_license"
diff --git a/packages/SystemUI/customization/tests/utils/Android.bp b/packages/SystemUI/customization/tests/utils/Android.bp
index 6db1410..e015455 100644
--- a/packages/SystemUI/customization/tests/utils/Android.bp
+++ b/packages/SystemUI/customization/tests/utils/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_system_ui_please_use_a_more_specific_subteam_if_possible_",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_base_packages_SystemUI_license"
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
index c6327ff..c385788 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPasswordViewControllerTest.kt
@@ -17,6 +17,7 @@
package com.android.keyguard
import android.testing.TestableLooper
+import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager
import android.widget.EditText
import android.widget.ImageView
@@ -51,10 +52,14 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
-@TestableLooper.RunWithLooper
+// collectFlow in KeyguardPinBasedInputViewController.onViewAttached calls JavaAdapter.CollectFlow,
+// which calls View.onRepeatWhenAttached, which requires being run on main thread.
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
class KeyguardPasswordViewControllerTest : SysuiTestCase() {
@Mock private lateinit var keyguardPasswordView: KeyguardPasswordView
@Mock private lateinit var passwordEntry: EditText
+ private var passwordEntryLayoutParams =
+ ViewGroup.LayoutParams(/* width = */ 0, /* height = */ 0)
@Mock lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@Mock lateinit var securityMode: KeyguardSecurityModel.SecurityMode
@Mock lateinit var lockPatternUtils: LockPatternUtils
@@ -92,7 +97,7 @@
whenever(keyguardPasswordView.findViewById<ImageView>(R.id.switch_ime_button))
.thenReturn(mock(ImageView::class.java))
`when`(keyguardPasswordView.resources).thenReturn(context.resources)
-
+ whenever(passwordEntry.layoutParams).thenReturn(passwordEntryLayoutParams)
val keyguardKeyboardInteractor = KeyguardKeyboardInteractor(FakeKeyboardRepository())
val fakeFeatureFlags = FakeFeatureFlags()
fakeFeatureFlags.set(Flags.LOCKSCREEN_ENABLE_LANDSCAPE, false)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
index f86342c..0054d13 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
@@ -24,6 +24,7 @@
import android.testing.TestableLooper.RunWithLooper;
import android.view.View;
+import android.view.ViewGroup;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -48,13 +49,17 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
-@RunWithLooper
+// collectFlow in KeyguardPinBasedInputViewController.onViewAttached calls JavaAdapter.CollectFlow,
+// which calls View.onRepeatWhenAttached, which requires being run on main thread.
+@RunWithLooper(setAsMainLooper = true)
public class KeyguardPinBasedInputViewControllerTest extends SysuiTestCase {
@Mock
private KeyguardPinBasedInputView mPinBasedInputView;
@Mock
private PasswordTextView mPasswordEntry;
+ private final ViewGroup.LayoutParams mPasswordEntryLayoutParams =
+ new ViewGroup.LayoutParams(/* width= */ 0, /* height= */ 0);
@Mock
private BouncerKeyguardMessageArea mKeyguardMessageArea;
@Mock
@@ -103,6 +108,7 @@
.thenReturn(mOkButton);
when(mPinBasedInputView.getResources()).thenReturn(getContext().getResources());
+ when(mPasswordEntry.getLayoutParams()).thenReturn(mPasswordEntryLayoutParams);
KeyguardKeyboardInteractor keyguardKeyboardInteractor =
new KeyguardKeyboardInteractor(new FakeKeyboardRepository());
FakeFeatureFlags featureFlags = new FakeFeatureFlags();
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
index 55cfcc2..ab55125 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsKeyguardViewLegacyControllerWithCoroutinesTest.kt
@@ -210,6 +210,32 @@
}
@Test
+ fun shouldHandleTouchesOnDetach() =
+ testScope.runTest {
+ val shouldHandleTouches by collectLastValue(mUdfpsOverlayInteractor.shouldHandleTouches)
+
+ // GIVEN view is attached + on the keyguard
+ mController.onViewAttached()
+ captureStatusBarStateListeners()
+ sendStatusBarStateChanged(StatusBarState.KEYGUARD)
+ whenever(mView.setPauseAuth(true)).thenReturn(true)
+ whenever(mView.unpausedAlpha).thenReturn(0)
+
+ // WHEN panelViewExpansion changes to expanded
+ val job = mController.listenForBouncerExpansion(this)
+ keyguardBouncerRepository.setPrimaryShow(true)
+ keyguardBouncerRepository.setPanelExpansion(KeyguardBouncerConstants.EXPANSION_VISIBLE)
+ runCurrent()
+
+ mController.onViewDetached()
+
+ // THEN UDFPS auth is paused and should not handle touches
+ assertThat(mController.shouldPauseAuth()).isTrue()
+ assertThat(shouldHandleTouches!!).isFalse()
+
+ job.cancel()
+ }
+ @Test
fun fadeFromDialogSuggestedAlpha() =
testScope.runTest {
// GIVEN view is attached and status bar expansion is 1f
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt
index 0aca16d..6b28319 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt
@@ -20,6 +20,7 @@
import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE
import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL
import android.app.admin.devicePolicyManager
+import android.appwidget.AppWidgetProviderInfo
import android.content.Intent
import android.content.pm.UserInfo
import android.platform.test.annotations.DisableFlags
@@ -136,6 +137,29 @@
)
}
+ @EnableFlags(FLAG_COMMUNAL_HUB)
+ @Test
+ fun hubShowsWidgetCategoriesSetByUser() =
+ testScope.runTest {
+ kosmos.fakeSettings.putIntForUser(
+ CommunalSettingsRepositoryImpl.GLANCEABLE_HUB_CONTENT_SETTING,
+ AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN,
+ PRIMARY_USER.id
+ )
+ val setting by collectLastValue(underTest.getWidgetCategories(PRIMARY_USER))
+ assertThat(setting?.categories)
+ .isEqualTo(AppWidgetProviderInfo.WIDGET_CATEGORY_HOME_SCREEN)
+ }
+
+ @EnableFlags(FLAG_COMMUNAL_HUB)
+ @Test
+ fun hubShowsKeyguardWidgetsByDefault() =
+ testScope.runTest {
+ val setting by collectLastValue(underTest.getWidgetCategories(PRIMARY_USER))
+ assertThat(setting?.categories)
+ .isEqualTo(AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD)
+ }
+
private fun setKeyguardFeaturesDisabled(user: UserInfo, disabledFlags: Int) {
whenever(kosmos.devicePolicyManager.getKeyguardDisabledFeatures(nullable(), eq(user.id)))
.thenReturn(disabledFlags)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
index cf727cf..ddb8582 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
@@ -30,6 +30,7 @@
import com.android.systemui.communal.data.repository.fakeCommunalTutorialRepository
import com.android.systemui.communal.data.repository.fakeCommunalWidgetRepository
import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.communal.shared.log.CommunalUiEvent
import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
@@ -81,6 +82,7 @@
underTest =
CommunalEditModeViewModel(
kosmos.communalInteractor,
+ kosmos.communalSettingsInteractor,
mediaHost,
uiEventLogger,
logcatLogBuffer("CommunalEditModeViewModelTest"),
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
index 0ebcf56..63abc8f 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorTest.kt
@@ -27,7 +27,6 @@
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.coroutines.collectValues
import com.android.systemui.dock.DockManager
import com.android.systemui.dock.DockManagerFake
import com.android.systemui.flags.FakeFeatureFlags
@@ -50,7 +49,6 @@
import com.android.systemui.res.R
import com.android.systemui.settings.UserFileManager
import com.android.systemui.settings.UserTracker
-import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.FakeSharedPreferences
@@ -59,7 +57,6 @@
import com.android.systemui.util.settings.FakeSettings
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
@@ -83,7 +80,6 @@
@Mock private lateinit var activityStarter: ActivityStarter
@Mock private lateinit var launchAnimator: DialogTransitionAnimator
@Mock private lateinit var devicePolicyManager: DevicePolicyManager
- @Mock private lateinit var shadeInteractor: ShadeInteractor
@Mock private lateinit var logger: KeyguardQuickAffordancesMetricsLogger
private lateinit var underTest: KeyguardQuickAffordanceInteractor
@@ -183,7 +179,6 @@
underTest =
KeyguardQuickAffordanceInteractor(
keyguardInteractor = withDeps.keyguardInteractor,
- shadeInteractor = shadeInteractor,
lockPatternUtils = lockPatternUtils,
keyguardStateController = keyguardStateController,
userTracker = userTracker,
@@ -198,8 +193,6 @@
backgroundDispatcher = testDispatcher,
appContext = context,
)
-
- whenever(shadeInteractor.anyExpansion).thenReturn(MutableStateFlow(0f))
}
@Test
@@ -346,25 +339,6 @@
}
@Test
- fun quickAffordance_updateOncePerShadeExpansion() =
- testScope.runTest {
- val shadeExpansion = MutableStateFlow(0f)
- whenever(shadeInteractor.anyExpansion).thenReturn(shadeExpansion)
-
- val collectedValue by
- collectValues(
- underTest.quickAffordance(KeyguardQuickAffordancePosition.BOTTOM_START)
- )
-
- val initialSize = collectedValue.size
- for (i in 0..10) {
- shadeExpansion.value = i / 10f
- }
-
- assertThat(collectedValue.size).isEqualTo(initialSize + 1)
- }
-
- @Test
fun quickAffordanceAlwaysVisible_notVisible_restrictedByPolicyManager() =
testScope.runTest {
whenever(devicePolicyManager.getKeyguardDisabledFeatures(null, userTracker.userId))
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModelTest.kt
index 837a9db..d33c10e 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModelTest.kt
@@ -20,6 +20,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.Flags as AConfigFlags
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
@@ -66,6 +67,25 @@
}
@Test
+ fun alpha_WhenNotGone_clockMigrationFlagIsOff_emitsKeyguardAlpha() =
+ testScope.runTest {
+ mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
+ val alpha by collectLastValue(underTest.alpha)
+
+ keyguardTransitionRepository.sendTransitionSteps(
+ from = KeyguardState.AOD,
+ to = KeyguardState.LOCKSCREEN,
+ testScope = testScope,
+ )
+
+ keyguardRepository.setKeyguardAlpha(0.5f)
+ assertThat(alpha).isEqualTo(0.5f)
+
+ keyguardRepository.setKeyguardAlpha(0.8f)
+ assertThat(alpha).isEqualTo(0.8f)
+ }
+
+ @Test
fun alpha_WhenGoneToAod() =
testScope.runTest {
val alpha by collectLastValue(underTest.alpha)
@@ -112,6 +132,7 @@
@Test
fun alpha_whenGone_equalsZero() =
testScope.runTest {
+ mSetFlagsRule.enableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
val alpha by collectLastValue(underTest.alpha)
keyguardTransitionRepository.sendTransitionStep(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
index 3455050..3104842 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
@@ -18,8 +18,6 @@
package com.android.systemui.keyguard.ui.viewmodel
-import android.content.pm.UserInfo
-import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -27,21 +25,20 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
-import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
+import com.android.systemui.communal.domain.interactor.communalInteractor
+import com.android.systemui.communal.domain.interactor.setCommunalAvailable
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
-import com.android.systemui.flags.Flags.COMMUNAL_SERVICE_ENABLED
-import com.android.systemui.flags.fakeFeatureFlagsClassic
import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationsPlaceholderViewModel
import com.android.systemui.testKosmos
-import com.android.systemui.user.data.repository.fakeUserRepository
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
@@ -85,35 +82,21 @@
@EnableFlags(FLAG_COMMUNAL_HUB)
@Test
- fun leftTransitionSceneKey_communalIsEnabled_communal() =
+ fun leftTransitionSceneKey_communalIsAvailable_communal() =
testScope.runTest {
- with(kosmos.fakeUserRepository) {
- setUserInfos(listOf(PRIMARY_USER))
- setSelectedUserInfo(PRIMARY_USER)
- }
- kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, true)
- val leftDestinationSceneKey by collectLastValue(underTest.leftDestinationSceneKey)
- assertThat(leftDestinationSceneKey).isEqualTo(SceneKey.Communal)
- }
-
- @DisableFlags(FLAG_COMMUNAL_HUB)
- @Test
- fun leftTransitionSceneKey_communalIsDisabled_null() =
- testScope.runTest {
- with(kosmos.fakeUserRepository) {
- setUserInfos(listOf(PRIMARY_USER))
- setSelectedUserInfo(PRIMARY_USER)
- }
- kosmos.fakeFeatureFlagsClassic.set(COMMUNAL_SERVICE_ENABLED, false)
val leftDestinationSceneKey by collectLastValue(underTest.leftDestinationSceneKey)
assertThat(leftDestinationSceneKey).isNull()
+
+ kosmos.setCommunalAvailable(true)
+ runCurrent()
+ assertThat(leftDestinationSceneKey).isEqualTo(SceneKey.Communal)
}
private fun createLockscreenSceneViewModel(): LockscreenSceneViewModel {
return LockscreenSceneViewModel(
applicationScope = testScope.backgroundScope,
deviceEntryInteractor = kosmos.deviceEntryInteractor,
- communalSettingsInteractor = kosmos.communalSettingsInteractor,
+ communalInteractor = kosmos.communalInteractor,
longPress =
KeyguardLongPressViewModel(
interactor = mock(),
@@ -121,9 +104,4 @@
notifications = kosmos.notificationsPlaceholderViewModel,
)
}
-
- private companion object {
- val PRIMARY_USER =
- UserInfo(/* id= */ 0, /* name= */ "primary user", /* flags= */ UserInfo.FLAG_MAIN)
- }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
index 7c30c7e..9f89d34 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt
@@ -40,7 +40,7 @@
import com.android.systemui.bouncer.ui.viewmodel.bouncerViewModel
import com.android.systemui.classifier.domain.interactor.falsingInteractor
import com.android.systemui.classifier.falsingCollector
-import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
+import com.android.systemui.communal.domain.interactor.communalInteractor
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository
import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor
@@ -130,7 +130,7 @@
private val sceneInteractor by lazy { kosmos.sceneInteractor }
private val authenticationInteractor by lazy { kosmos.authenticationInteractor }
private val deviceEntryInteractor by lazy { kosmos.deviceEntryInteractor }
- private val communalSettingsInteractor by lazy { kosmos.communalSettingsInteractor }
+ private val communalInteractor by lazy { kosmos.communalInteractor }
private val transitionState by lazy {
MutableStateFlow<ObservableTransitionState>(
@@ -155,7 +155,7 @@
LockscreenSceneViewModel(
applicationScope = testScope.backgroundScope,
deviceEntryInteractor = deviceEntryInteractor,
- communalSettingsInteractor = communalSettingsInteractor,
+ communalInteractor = communalInteractor,
longPress =
KeyguardLongPressViewModel(
interactor = mock(),
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerMainThreadTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerMainThreadTest.java
deleted file mode 100644
index d0e05fa..0000000
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerMainThreadTest.java
+++ /dev/null
@@ -1,614 +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.systemui.statusbar;
-
-import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED;
-import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS;
-import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS;
-import static android.provider.Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS;
-import static android.provider.Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS;
-
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertTrue;
-
-import static org.mockito.Mockito.clearInvocations;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.ActivityManager;
-import android.app.KeyguardManager;
-import android.app.Notification;
-import android.app.admin.DevicePolicyManager;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.UserInfo;
-import android.database.ContentObserver;
-import android.net.Uri;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.provider.Settings;
-import android.util.SparseArray;
-
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.filters.SmallTest;
-
-import com.android.internal.widget.LockPatternUtils;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.broadcast.BroadcastDispatcher;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FakeFeatureFlagsClassic;
-import com.android.systemui.flags.Flags;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.recents.OverviewProxyService;
-import com.android.systemui.settings.UserTracker;
-import com.android.systemui.statusbar.NotificationLockscreenUserManager.NotificationStateChangedListener;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
-import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
-import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
-import com.android.systemui.statusbar.policy.DeviceProvisionedController;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.util.settings.FakeSettings;
-
-import com.google.android.collect.Lists;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.concurrent.Executor;
-
-@SmallTest
-@RunWith(AndroidJUnit4.class)
-public class NotificationLockscreenUserManagerMainThreadTest extends SysuiTestCase {
- @Mock
- private NotificationPresenter mPresenter;
- @Mock
- private UserManager mUserManager;
- @Mock
- private UserTracker mUserTracker;
-
- // Dependency mocks:
- @Mock
- private NotificationVisibilityProvider mVisibilityProvider;
- @Mock
- private CommonNotifCollection mNotifCollection;
- @Mock
- private DevicePolicyManager mDevicePolicyManager;
- @Mock
- private NotificationClickNotifier mClickNotifier;
- @Mock
- private OverviewProxyService mOverviewProxyService;
- @Mock
- private KeyguardManager mKeyguardManager;
- @Mock
- private DeviceProvisionedController mDeviceProvisionedController;
- @Mock
- private StatusBarStateController mStatusBarStateController;
- @Mock
- private BroadcastDispatcher mBroadcastDispatcher;
- @Mock
- private KeyguardStateController mKeyguardStateController;
-
- private UserInfo mCurrentUser;
- private UserInfo mSecondaryUser;
- private UserInfo mWorkUser;
- private UserInfo mCommunalUser;
- private FakeSettings mSettings;
- private TestNotificationLockscreenUserManager mLockscreenUserManager;
- private NotificationEntry mCurrentUserNotif;
- private NotificationEntry mSecondaryUserNotif;
- private NotificationEntry mWorkProfileNotif;
- private final FakeFeatureFlagsClassic mFakeFeatureFlags = new FakeFeatureFlagsClassic();
- private Executor mMainExecutor = Runnable::run; // Direct executor
- private Executor mBackgroundExecutor = Runnable::run; // Direct executor
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
-
- mFakeFeatureFlags.set(Flags.NOTIF_LS_BACKGROUND_THREAD, false);
-
- int currentUserId = ActivityManager.getCurrentUser();
- when(mUserTracker.getUserId()).thenReturn(currentUserId);
- mSettings = new FakeSettings();
- mSettings.setUserId(ActivityManager.getCurrentUser());
- mCurrentUser = new UserInfo(currentUserId, "", 0);
- mSecondaryUser = new UserInfo(currentUserId + 1, "", 0);
- mWorkUser = new UserInfo(currentUserId + 2, "" /* name */, null /* iconPath */, 0,
- UserManager.USER_TYPE_PROFILE_MANAGED);
- mCommunalUser = new UserInfo(currentUserId + 3, "" /* name */, null /* iconPath */, 0,
- UserManager.USER_TYPE_PROFILE_COMMUNAL);
-
- when(mKeyguardManager.getPrivateNotificationsAllowed()).thenReturn(true);
- when(mUserManager.getProfiles(currentUserId)).thenReturn(Lists.newArrayList(
- mCurrentUser, mWorkUser));
- when(mUserManager.getProfilesIncludingCommunal(currentUserId)).thenReturn(
- Lists.newArrayList(mCurrentUser, mWorkUser, mCommunalUser));
- when(mUserManager.getProfiles(mSecondaryUser.id)).thenReturn(Lists.newArrayList(
- mSecondaryUser));
- when(mUserManager.getProfilesIncludingCommunal(mSecondaryUser.id)).thenReturn(
- Lists.newArrayList(mSecondaryUser, mCommunalUser));
-
- Notification notifWithPrivateVisibility = new Notification();
- notifWithPrivateVisibility.visibility = Notification.VISIBILITY_PRIVATE;
- mCurrentUserNotif = new NotificationEntryBuilder()
- .setNotification(notifWithPrivateVisibility)
- .setUser(new UserHandle(mCurrentUser.id))
- .build();
- mSecondaryUserNotif = new NotificationEntryBuilder()
- .setNotification(notifWithPrivateVisibility)
- .setUser(new UserHandle(mSecondaryUser.id))
- .build();
- mWorkProfileNotif = new NotificationEntryBuilder()
- .setNotification(notifWithPrivateVisibility)
- .setUser(new UserHandle(mWorkUser.id))
- .build();
-
- mLockscreenUserManager = new TestNotificationLockscreenUserManager(mContext);
- mLockscreenUserManager.setUpWithPresenter(mPresenter);
- }
-
- private void changeSetting(String setting) {
- final Collection<Uri> lockScreenUris = new ArrayList<>();
- lockScreenUris.add(Settings.Secure.getUriFor(setting));
- mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false,
- lockScreenUris, 0);
- }
-
- @Test
- public void testGetCurrentProfiles() {
- final SparseArray<UserInfo> expectedCurProfiles = new SparseArray<>();
- expectedCurProfiles.put(mCurrentUser.id, mCurrentUser);
- expectedCurProfiles.put(mWorkUser.id, mWorkUser);
- if (android.multiuser.Flags.supportCommunalProfile()) {
- expectedCurProfiles.put(mCommunalUser.id, mCommunalUser);
- }
- assertTrue(mLockscreenUserManager.getCurrentProfiles().contentEquals(expectedCurProfiles));
-
- mLockscreenUserManager.mUserChangedCallback.onUserChanging(mSecondaryUser.id, mContext);
-
- final SparseArray<UserInfo> expectedSecProfiles = new SparseArray<>();
- expectedSecProfiles.put(mSecondaryUser.id, mSecondaryUser);
- if (android.multiuser.Flags.supportCommunalProfile()) {
- expectedSecProfiles.put(mCommunalUser.id, mCommunalUser);
- }
- assertTrue(mLockscreenUserManager.getCurrentProfiles().contentEquals(expectedSecProfiles));
- }
-
- @Test
- public void testLockScreenShowNotificationsFalse() {
- mSettings.putInt(LOCK_SCREEN_SHOW_NOTIFICATIONS, 0);
- changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
- assertFalse(mLockscreenUserManager.shouldShowLockscreenNotifications());
- }
-
- @Test
- public void testLockScreenShowNotificationsTrue() {
- mSettings.putInt(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
- changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
- assertTrue(mLockscreenUserManager.shouldShowLockscreenNotifications());
- }
-
- @Test
- public void testLockScreenAllowPrivateNotificationsTrue() {
- mSettings.putInt(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1);
- changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
- assertTrue(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mCurrentUser.id));
- }
-
- @Test
- public void testLockScreenAllowPrivateNotificationsFalse() {
- mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
- mCurrentUser.id);
- changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
- assertFalse(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mCurrentUser.id));
- }
-
- @Test
- public void testLockScreenAllowsWorkPrivateNotificationsFalse() {
- mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
- mWorkUser.id);
- changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
- assertFalse(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mWorkUser.id));
- }
-
- @Test
- public void testLockScreenAllowsWorkPrivateNotificationsTrue() {
- mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
- mWorkUser.id);
- changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
- assertTrue(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mWorkUser.id));
- }
-
- @Test
- public void testCurrentUserPrivateNotificationsNotRedacted() {
- // GIVEN current user doesn't allow private notifications to show
- mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
- mCurrentUser.id);
- changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
-
- // THEN current user's notification is redacted
- assertTrue(mLockscreenUserManager.needsRedaction(mCurrentUserNotif));
- }
-
- @Test
- public void testCurrentUserPrivateNotificationsRedacted() {
- // GIVEN current user allows private notifications to show
- mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
- mCurrentUser.id);
- changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
-
- // THEN current user's notification isn't redacted
- assertFalse(mLockscreenUserManager.needsRedaction(mCurrentUserNotif));
- }
-
- @Test
- public void testWorkPrivateNotificationsRedacted() {
- // GIVEN work profile doesn't private notifications to show
- mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
- mWorkUser.id);
- changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
-
- // THEN work profile notification is redacted
- assertTrue(mLockscreenUserManager.needsRedaction(mWorkProfileNotif));
- assertFalse(mLockscreenUserManager.allowsManagedPrivateNotificationsInPublic());
- }
-
- @Test
- public void testWorkPrivateNotificationsNotRedacted() {
- // GIVEN work profile allows private notifications to show
- mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
- mWorkUser.id);
- changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
-
- // THEN work profile notification isn't redacted
- assertFalse(mLockscreenUserManager.needsRedaction(mWorkProfileNotif));
- assertTrue(mLockscreenUserManager.allowsManagedPrivateNotificationsInPublic());
- }
-
- @Test
- public void testWorkPrivateNotificationsNotRedacted_otherUsersRedacted() {
- // GIVEN work profile allows private notifications to show but the other users don't
- mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
- mWorkUser.id);
- mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
- mCurrentUser.id);
- mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
- mSecondaryUser.id);
- changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
- changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
- changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
-
- // THEN the work profile notification doesn't need to be redacted
- assertFalse(mLockscreenUserManager.needsRedaction(mWorkProfileNotif));
-
- // THEN the current user and secondary user notifications do need to be redacted
- assertTrue(mLockscreenUserManager.needsRedaction(mCurrentUserNotif));
- assertTrue(mLockscreenUserManager.needsRedaction(mSecondaryUserNotif));
- }
-
- @Test
- public void testWorkProfileRedacted_otherUsersNotRedacted() {
- // GIVEN work profile doesn't allow private notifications to show but the other users do
- mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
- mWorkUser.id);
- mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
- mCurrentUser.id);
- mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
- mSecondaryUser.id);
- changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
- changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
- changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
-
- // THEN the work profile notification needs to be redacted
- assertTrue(mLockscreenUserManager.needsRedaction(mWorkProfileNotif));
-
- // THEN the current user and secondary user notifications don't need to be redacted
- assertFalse(mLockscreenUserManager.needsRedaction(mCurrentUserNotif));
- assertFalse(mLockscreenUserManager.needsRedaction(mSecondaryUserNotif));
- }
-
- @Test
- public void testSecondaryUserNotRedacted_currentUserRedacted() {
- // GIVEN secondary profile allows private notifications to show but the current user
- // doesn't allow private notifications to show
- mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0,
- mCurrentUser.id);
- mSettings.putIntForUser(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1,
- mSecondaryUser.id);
- changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
- changeSetting(LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS);
-
- // THEN the secondary profile notification still needs to be redacted because the current
- // user's setting takes precedence
- assertTrue(mLockscreenUserManager.needsRedaction(mSecondaryUserNotif));
- }
-
- @Test
- public void testUserSwitchedCallsOnUserSwitching() {
- mLockscreenUserManager.getUserTrackerCallbackForTest().onUserChanging(mSecondaryUser.id,
- mContext);
- verify(mPresenter, times(1)).onUserSwitched(mSecondaryUser.id);
- }
-
- @Test
- public void testIsLockscreenPublicMode() {
- assertFalse(mLockscreenUserManager.isLockscreenPublicMode(mCurrentUser.id));
- mLockscreenUserManager.setLockscreenPublicMode(true, mCurrentUser.id);
- assertTrue(mLockscreenUserManager.isLockscreenPublicMode(mCurrentUser.id));
- }
-
- @Test
- public void testUpdateIsPublicMode() {
- when(mKeyguardStateController.isMethodSecure()).thenReturn(true);
-
- NotificationStateChangedListener listener = mock(NotificationStateChangedListener.class);
- mLockscreenUserManager.addNotificationStateChangedListener(listener);
- mLockscreenUserManager.mCurrentProfiles.append(0, mock(UserInfo.class));
-
- // first call explicitly sets user 0 to not public; notifies
- mLockscreenUserManager.updatePublicMode();
- assertFalse(mLockscreenUserManager.isLockscreenPublicMode(0));
- verify(listener).onNotificationStateChanged();
- clearInvocations(listener);
-
- // calling again has no changes; does not notify
- mLockscreenUserManager.updatePublicMode();
- assertFalse(mLockscreenUserManager.isLockscreenPublicMode(0));
- verify(listener, never()).onNotificationStateChanged();
-
- // Calling again with keyguard now showing makes user 0 public; notifies
- when(mKeyguardStateController.isShowing()).thenReturn(true);
- mLockscreenUserManager.updatePublicMode();
- assertTrue(mLockscreenUserManager.isLockscreenPublicMode(0));
- verify(listener).onNotificationStateChanged();
- clearInvocations(listener);
-
- // calling again has no changes; does not notify
- mLockscreenUserManager.updatePublicMode();
- assertTrue(mLockscreenUserManager.isLockscreenPublicMode(0));
- verify(listener, never()).onNotificationStateChanged();
- }
-
- @Test
- public void testDevicePolicyDoesNotAllowNotifications() {
- // User allows them
- mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id);
- changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
-
- // DevicePolicy hides notifs on lockscreen
- when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUser.id))
- .thenReturn(KEYGUARD_DISABLE_SECURE_NOTIFICATIONS);
-
- BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult(
- 0, null, null, 0, true, false, null, mCurrentUser.id, 0);
- mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr);
- mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext,
- new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED));
-
- assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mCurrentUser.id));
- }
-
- @Test
- public void testDevicePolicyDoesNotAllowNotifications_secondary() {
- Mockito.clearInvocations(mDevicePolicyManager);
- // User allows notifications
- mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mSecondaryUser.id);
- changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
-
- // DevicePolicy hides notifications
- when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mSecondaryUser.id))
- .thenReturn(KEYGUARD_DISABLE_SECURE_NOTIFICATIONS);
-
- BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult(
- 0, null, null, 0, true, false, null, mSecondaryUser.id, 0);
- mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr);
- mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext,
- new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED));
-
- assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mSecondaryUser.id));
- }
-
- @Test
- public void testDevicePolicy_noPrivateNotifications() {
- Mockito.clearInvocations(mDevicePolicyManager);
- // User allows notifications
- mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id);
- changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
-
- // DevicePolicy hides sensitive content
- when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUser.id))
- .thenReturn(KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS);
-
- BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult(
- 0, null, null, 0, true, false, null, mCurrentUser.id, 0);
- mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr);
- mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext,
- new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED));
-
- assertTrue(mLockscreenUserManager.needsRedaction(mCurrentUserNotif));
- }
-
- @Test
- public void testDevicePolicy_noPrivateNotifications_userAll() {
- // User allows notifications
- mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id);
- changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
-
- // DevicePolicy hides sensitive content
- when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUser.id))
- .thenReturn(KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS);
-
- BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult(
- 0, null, null, 0, true, false, null, mCurrentUser.id, 0);
- mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr);
- mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext,
- new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED));
-
- assertTrue(mLockscreenUserManager.needsRedaction(new NotificationEntryBuilder()
- .setNotification(new Notification())
- .setUser(UserHandle.ALL)
- .build()));
- }
-
- @Test
- public void testDevicePolicyPrivateNotifications_secondary() {
- Mockito.clearInvocations(mDevicePolicyManager);
- // User allows notifications
- mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mSecondaryUser.id);
- changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
-
- // DevicePolicy hides sensitive content
- when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mSecondaryUser.id))
- .thenReturn(KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS);
-
- BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult(
- 0, null, null, 0, true, false, null, mSecondaryUser.id, 0);
- mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr);
- mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext,
- new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED));
-
- mLockscreenUserManager.mUserChangedCallback.onUserChanging(mSecondaryUser.id, mContext);
- assertTrue(mLockscreenUserManager.needsRedaction(mSecondaryUserNotif));
- }
-
- @Test
- public void testHideNotifications_primary() {
- mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, mCurrentUser.id);
- changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
-
- assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mCurrentUser.id));
- }
-
- @Test
- public void testHideNotifications_secondary() {
- mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, mSecondaryUser.id);
- changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
-
- assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mSecondaryUser.id));
- }
-
- @Test
- public void testHideNotifications_secondary_userSwitch() {
- mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, mSecondaryUser.id);
- changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
-
- mLockscreenUserManager.mUserChangedCallback.onUserChanging(mSecondaryUser.id, mContext);
-
- assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mSecondaryUser.id));
- }
-
- @Test
- public void testShowNotifications_secondary_userSwitch() {
- mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mSecondaryUser.id);
- changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
-
- mLockscreenUserManager.mUserChangedCallback.onUserChanging(mSecondaryUser.id, mContext);
-
- assertTrue(mLockscreenUserManager.userAllowsNotificationsInPublic(mSecondaryUser.id));
- }
-
- @Test
- public void testUserAllowsNotificationsInPublic_keyguardManagerNoPrivateNotifications() {
- // DevicePolicy allows notifications
- when(mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUser.id))
- .thenReturn(0);
- BroadcastReceiver.PendingResult pr = new BroadcastReceiver.PendingResult(
- 0, null, null, 0, true, false, null, mCurrentUser.id, 0);
- mLockscreenUserManager.mAllUsersReceiver.setPendingResult(pr);
- mLockscreenUserManager.mAllUsersReceiver.onReceive(mContext,
- new Intent(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED));
-
- // KeyguardManager does not allow notifications
- when(mKeyguardManager.getPrivateNotificationsAllowed()).thenReturn(false);
-
- // User allows notifications
- mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id);
- // We shouldn't need to call this method, but getPrivateNotificationsAllowed has no
- // callback, so it's only updated when the setting is
- changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
-
- assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mCurrentUser.id));
- }
-
- @Test
- public void testUserAllowsNotificationsInPublic_settingsChange() {
- // User allows notifications
- mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 1, mCurrentUser.id);
- changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
-
- assertTrue(mLockscreenUserManager.userAllowsNotificationsInPublic(mCurrentUser.id));
-
- // User disables
- mSettings.putIntForUser(LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, mCurrentUser.id);
- changeSetting(LOCK_SCREEN_SHOW_NOTIFICATIONS);
-
- assertFalse(mLockscreenUserManager.userAllowsNotificationsInPublic(mCurrentUser.id));
- }
-
- private class TestNotificationLockscreenUserManager
- extends NotificationLockscreenUserManagerImpl {
- public TestNotificationLockscreenUserManager(Context context) {
- super(
- context,
- mBroadcastDispatcher,
- mDevicePolicyManager,
- mUserManager,
- mUserTracker,
- (() -> mVisibilityProvider),
- (() -> mNotifCollection),
- mClickNotifier,
- (() -> mOverviewProxyService),
- NotificationLockscreenUserManagerMainThreadTest.this.mKeyguardManager,
- mStatusBarStateController,
- mMainExecutor,
- mBackgroundExecutor,
- mDeviceProvisionedController,
- mKeyguardStateController,
- mSettings,
- mock(DumpManager.class),
- mock(LockPatternUtils.class),
- mFakeFeatureFlags);
- }
-
- public BroadcastReceiver getBaseBroadcastReceiverForTest() {
- return mBaseBroadcastReceiver;
- }
-
- public UserTracker.Callback getUserTrackerCallbackForTest() {
- return mUserChangedCallback;
- }
-
- public ContentObserver getLockscreenSettingsObserverForTest() {
- return mLockscreenSettingsObserver;
- }
-
- public ContentObserver getSettingsObserverForTest() {
- return mSettingsObserver;
- }
- }
-}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
index bcc0710..d505b27 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
@@ -172,8 +172,6 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
- mFakeFeatureFlags.set(Flags.NOTIF_LS_BACKGROUND_THREAD, true);
-
int currentUserId = ActivityManager.getCurrentUser();
when(mUserTracker.getUserId()).thenReturn(currentUserId);
mSettings = new FakeSettings();
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationContentDescriptionTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationContentDescriptionTest.kt
index f67c70c..12473cb 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationContentDescriptionTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationContentDescriptionTest.kt
@@ -62,36 +62,23 @@
assertThat(description).isEqualTo(createDescriptionText(n, ""))
}
- @Test
- fun nullNotification_descriptionIsAppName() {
- val description = contentDescForNotification(context, null)
- assertThat(description).isEqualTo(createDescriptionText(null, ""))
- }
-
private fun createNotification(
title: String? = null,
text: String? = null,
ticker: String? = null
): Notification =
- Notification.Builder(context)
+ Notification.Builder(context, "channel")
.setContentTitle(title)
.setContentText(text)
.setTicker(ticker)
.build()
private fun getTestAppName(): String {
- return getAppName(createNotification("", "", ""))
+ return createNotification("", "", "").loadHeaderAppName(mContext)
}
- private fun getAppName(n: Notification?) =
- n?.let {
- val builder = Notification.Builder.recoverBuilder(context, it)
- builder.loadHeaderAppName()
- }
- ?: ""
-
private fun createDescriptionText(n: Notification?, desc: String?): String {
- val appName = getAppName(n)
+ val appName = n?.loadHeaderAppName(mContext)
return context.getString(R.string.accessibility_desc_notification_icon, appName, desc)
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt
index 8b6880c..aa6b4ac 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt
@@ -86,7 +86,8 @@
var isRtl = false
var targetRotation = ROTATION_NONE
- var bounds = calculateInsetsForRotationWithRotatedResources(
+ var bounds =
+ calculateInsetsForRotationWithRotatedResources(
currentRotation,
targetRotation,
null,
@@ -97,29 +98,33 @@
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight)
+ statusBarContentHeight
+ )
var chipBounds = getPrivacyChipBoundingRectForInsets(bounds, dotWidth, chipWidth, isRtl)
- /* 1080 - 20 (rounded corner) - 30 (chip),
- * 0 (sb top)
- * 1080 - 20 (rounded corner) + 10 ( dot),
- * 100 (sb height portrait)
- */
+ /*
+ * 1080 - 20 (rounded corner) - 30 (chip),
+ * 0 (sb top)
+ * 1080 - 20 (rounded corner) + 10 ( dot),
+ * 100 (sb height portrait)
+ */
var expected = Rect(1030, 0, 1070, 100)
assertRects(expected, chipBounds, currentRotation, targetRotation)
isRtl = true
chipBounds = getPrivacyChipBoundingRectForInsets(bounds, dotWidth, chipWidth, isRtl)
- /* 0 + 20 (rounded corner) - 10 (dot),
- * 0 (sb top)
- * 0 + 20 (rounded corner) + 30 (chip),
- * 100 (sb height portrait)
- */
+ /*
+ * 0 + 20 (rounded corner) - 10 (dot),
+ * 0 (sb top)
+ * 0 + 20 (rounded corner) + 30 (chip),
+ * 100 (sb height portrait)
+ */
expected = Rect(10, 0, 50, 100)
assertRects(expected, chipBounds, currentRotation, targetRotation)
isRtl = false
targetRotation = ROTATION_LANDSCAPE
- bounds = calculateInsetsForRotationWithRotatedResources(
+ bounds =
+ calculateInsetsForRotationWithRotatedResources(
currentRotation,
targetRotation,
sysUICutout,
@@ -130,23 +135,26 @@
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight)
+ statusBarContentHeight
+ )
chipBounds = getPrivacyChipBoundingRectForInsets(bounds, dotWidth, chipWidth, isRtl)
- /* 2160 - 20 (rounded corner) - 30 (chip),
- * 0 (sb top)
- * 2160 - 20 (rounded corner) + 10 ( dot),
- * 60 (sb height landscape)
- */
+ /*
+ * 2160 - 20 (rounded corner) - 30 (chip),
+ * 0 (sb top)
+ * 2160 - 20 (rounded corner) + 10 ( dot),
+ * 60 (sb height landscape)
+ */
expected = Rect(2110, 0, 2150, 60)
assertRects(expected, chipBounds, currentRotation, targetRotation)
isRtl = true
chipBounds = getPrivacyChipBoundingRectForInsets(bounds, dotWidth, chipWidth, isRtl)
- /* 0 + 20 (rounded corner) - 10 (dot),
- * 0 (sb top)
- * 0 + 20 (rounded corner) + 30 (chip),
- * 60 (sb height landscape)
- */
+ /*
+ * 0 + 20 (rounded corner) - 10 (dot),
+ * 0 (sb top)
+ * 0 + 20 (rounded corner) + 30 (chip),
+ * 60 (sb height landscape)
+ */
expected = Rect(10, 0, 50, 60)
assertRects(expected, chipBounds, currentRotation, targetRotation)
}
@@ -157,10 +165,10 @@
val dotWidth = 10
val isRtl = false
val contentRect =
- Rect(/* left = */ 0, /* top = */ 10, /* right = */ 1000, /* bottom = */ 100)
+ Rect(/* left = */ 0, /* top = */ 10, /* right = */ 1000, /* bottom = */ 100)
val chipBounds =
- getPrivacyChipBoundingRectForInsets(contentRect, dotWidth, chipWidth, isRtl)
+ getPrivacyChipBoundingRectForInsets(contentRect, dotWidth, chipWidth, isRtl)
assertThat(chipBounds.top).isEqualTo(contentRect.top)
}
@@ -184,12 +192,11 @@
// THEN rotations which share a short side should use the greater value between rounded
// corner padding and the display cutout's size
var targetRotation = ROTATION_NONE
- var expectedBounds = Rect(dcBounds.right,
- 0,
- screenBounds.right - minRightPadding,
- sbHeightPortrait)
+ var expectedBounds =
+ Rect(dcBounds.right, 0, screenBounds.right - minRightPadding, sbHeightPortrait)
- var bounds = calculateInsetsForRotationWithRotatedResources(
+ var bounds =
+ calculateInsetsForRotationWithRotatedResources(
currentRotation,
targetRotation,
sysUICutout,
@@ -200,17 +207,17 @@
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight)
+ statusBarContentHeight
+ )
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
targetRotation = ROTATION_LANDSCAPE
- expectedBounds = Rect(dcBounds.height(),
- 0,
- screenBounds.height() - minRightPadding,
- sbHeightLandscape)
+ expectedBounds =
+ Rect(dcBounds.height(), 0, screenBounds.height() - minRightPadding, sbHeightLandscape)
- bounds = calculateInsetsForRotationWithRotatedResources(
+ bounds =
+ calculateInsetsForRotationWithRotatedResources(
currentRotation,
targetRotation,
sysUICutout,
@@ -221,19 +228,19 @@
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight)
+ statusBarContentHeight
+ )
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
// THEN the side that does NOT share a short side with the display cutout ignores the
// display cutout bounds
targetRotation = ROTATION_UPSIDE_DOWN
- expectedBounds = Rect(minLeftPadding,
- 0,
- screenBounds.width() - minRightPadding,
- sbHeightPortrait)
+ expectedBounds =
+ Rect(minLeftPadding, 0, screenBounds.width() - minRightPadding, sbHeightPortrait)
- bounds = calculateInsetsForRotationWithRotatedResources(
+ bounds =
+ calculateInsetsForRotationWithRotatedResources(
currentRotation,
targetRotation,
sysUICutout,
@@ -244,18 +251,23 @@
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight)
+ statusBarContentHeight
+ )
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
// Phone in portrait, seascape (rot_270) bounds
targetRotation = ROTATION_SEASCAPE
- expectedBounds = Rect(minLeftPadding,
+ expectedBounds =
+ Rect(
+ minLeftPadding,
0,
screenBounds.height() - dcBounds.height() - dotWidth,
- sbHeightLandscape)
+ sbHeightLandscape
+ )
- bounds = calculateInsetsForRotationWithRotatedResources(
+ bounds =
+ calculateInsetsForRotationWithRotatedResources(
currentRotation,
targetRotation,
sysUICutout,
@@ -266,7 +278,8 @@
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight)
+ statusBarContentHeight
+ )
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
}
@@ -292,12 +305,11 @@
// THEN rotations which share a short side should use the greater value between rounded
// corner padding, the display cutout's size, and the camera protections' size.
var targetRotation = ROTATION_NONE
- var expectedBounds = Rect(protectionBounds.right,
- 0,
- screenBounds.right - minRightPadding,
- sbHeightPortrait)
+ var expectedBounds =
+ Rect(protectionBounds.right, 0, screenBounds.right - minRightPadding, sbHeightPortrait)
- var bounds = calculateInsetsForRotationWithRotatedResources(
+ var bounds =
+ calculateInsetsForRotationWithRotatedResources(
currentRotation,
targetRotation,
sysUICutout,
@@ -308,17 +320,22 @@
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight)
+ statusBarContentHeight
+ )
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
targetRotation = ROTATION_LANDSCAPE
- expectedBounds = Rect(protectionBounds.bottom,
+ expectedBounds =
+ Rect(
+ protectionBounds.bottom,
0,
screenBounds.height() - minRightPadding,
- sbHeightLandscape)
+ sbHeightLandscape
+ )
- bounds = calculateInsetsForRotationWithRotatedResources(
+ bounds =
+ calculateInsetsForRotationWithRotatedResources(
currentRotation,
targetRotation,
sysUICutout,
@@ -329,19 +346,19 @@
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight)
+ statusBarContentHeight
+ )
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
// THEN the side that does NOT share a short side with the display cutout ignores the
// display cutout bounds
targetRotation = ROTATION_UPSIDE_DOWN
- expectedBounds = Rect(minLeftPadding,
- 0,
- screenBounds.width() - minRightPadding,
- sbHeightPortrait)
+ expectedBounds =
+ Rect(minLeftPadding, 0, screenBounds.width() - minRightPadding, sbHeightPortrait)
- bounds = calculateInsetsForRotationWithRotatedResources(
+ bounds =
+ calculateInsetsForRotationWithRotatedResources(
currentRotation,
targetRotation,
sysUICutout,
@@ -352,18 +369,23 @@
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight)
+ statusBarContentHeight
+ )
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
// Phone in portrait, seascape (rot_270) bounds
targetRotation = ROTATION_SEASCAPE
- expectedBounds = Rect(minLeftPadding,
+ expectedBounds =
+ Rect(
+ minLeftPadding,
0,
screenBounds.height() - protectionBounds.bottom - dotWidth,
- sbHeightLandscape)
+ sbHeightLandscape
+ )
- bounds = calculateInsetsForRotationWithRotatedResources(
+ bounds =
+ calculateInsetsForRotationWithRotatedResources(
currentRotation,
targetRotation,
sysUICutout,
@@ -374,7 +396,8 @@
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight)
+ statusBarContentHeight
+ )
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
}
@@ -428,7 +451,8 @@
bottom = sbHeightPortrait
)
- var bounds = calculateInsetsForRotationWithRotatedResources(
+ var bounds =
+ calculateInsetsForRotationWithRotatedResources(
currentRotation,
targetRotation,
sysUICutout,
@@ -439,7 +463,8 @@
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight)
+ statusBarContentHeight
+ )
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -453,7 +478,8 @@
bottom = sbHeightLandscape
)
- bounds = calculateInsetsForRotationWithRotatedResources(
+ bounds =
+ calculateInsetsForRotationWithRotatedResources(
currentRotation,
targetRotation,
sysUICutout,
@@ -464,7 +490,8 @@
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight)
+ statusBarContentHeight
+ )
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -478,7 +505,8 @@
bottom = sbHeightPortrait
)
- bounds = calculateInsetsForRotationWithRotatedResources(
+ bounds =
+ calculateInsetsForRotationWithRotatedResources(
currentRotation,
targetRotation,
sysUICutout,
@@ -489,7 +517,8 @@
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight)
+ statusBarContentHeight
+ )
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
@@ -503,7 +532,8 @@
bottom = sbHeightLandscape
)
- bounds = calculateInsetsForRotationWithRotatedResources(
+ bounds =
+ calculateInsetsForRotationWithRotatedResources(
currentRotation,
targetRotation,
sysUICutout,
@@ -514,7 +544,8 @@
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight)
+ statusBarContentHeight
+ )
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
}
@@ -541,7 +572,8 @@
var expectedBounds =
Rect(minLeftPadding, 0, protectionBounds.left - dotWidth, sbHeightPortrait)
- var bounds = calculateInsetsForRotationWithRotatedResources(
+ var bounds =
+ calculateInsetsForRotationWithRotatedResources(
currentRotation,
targetRotation,
sysUICutout,
@@ -552,17 +584,22 @@
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight)
+ statusBarContentHeight
+ )
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
targetRotation = ROTATION_LANDSCAPE
- expectedBounds = Rect(protectionBounds.bottom,
+ expectedBounds =
+ Rect(
+ protectionBounds.bottom,
0,
screenBounds.height() - minRightPadding,
- sbHeightLandscape)
+ sbHeightLandscape
+ )
- bounds = calculateInsetsForRotationWithRotatedResources(
+ bounds =
+ calculateInsetsForRotationWithRotatedResources(
currentRotation,
targetRotation,
sysUICutout,
@@ -573,19 +610,19 @@
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight)
+ statusBarContentHeight
+ )
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
// THEN the side that does NOT share a short side with the display cutout ignores the
// display cutout bounds
targetRotation = ROTATION_UPSIDE_DOWN
- expectedBounds = Rect(minLeftPadding,
- 0,
- screenBounds.width() - minRightPadding,
- sbHeightPortrait)
+ expectedBounds =
+ Rect(minLeftPadding, 0, screenBounds.width() - minRightPadding, sbHeightPortrait)
- bounds = calculateInsetsForRotationWithRotatedResources(
+ bounds =
+ calculateInsetsForRotationWithRotatedResources(
currentRotation,
targetRotation,
sysUICutout,
@@ -596,18 +633,23 @@
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight)
+ statusBarContentHeight
+ )
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
// Phone in portrait, seascape (rot_270) bounds
targetRotation = ROTATION_SEASCAPE
- expectedBounds = Rect(minLeftPadding,
+ expectedBounds =
+ Rect(
+ minLeftPadding,
0,
screenBounds.height() - protectionBounds.bottom - dotWidth,
- sbHeightLandscape)
+ sbHeightLandscape
+ )
- bounds = calculateInsetsForRotationWithRotatedResources(
+ bounds =
+ calculateInsetsForRotationWithRotatedResources(
currentRotation,
targetRotation,
sysUICutout,
@@ -618,7 +660,8 @@
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight)
+ statusBarContentHeight
+ )
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
}
@@ -627,7 +670,8 @@
fun calculateInsetsForRotationWithRotatedResources_bottomAlignedMarginDisabled_noTopInset() {
setNoCutout()
- val bounds = calculateInsetsForRotationWithRotatedResources(
+ val bounds =
+ calculateInsetsForRotationWithRotatedResources(
currentRotation = ROTATION_NONE,
targetRotation = ROTATION_NONE,
sysUICutout = sysUICutout,
@@ -638,7 +682,8 @@
isRtl = false,
dotWidth = 10,
bottomAlignedMargin = BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight = 15)
+ statusBarContentHeight = 15
+ )
assertThat(bounds.top).isEqualTo(0)
}
@@ -647,7 +692,8 @@
fun calculateInsetsForRotationWithRotatedResources_bottomAlignedMargin_topBasedOnMargin() {
whenever(dc.boundingRects).thenReturn(emptyList())
- val bounds = calculateInsetsForRotationWithRotatedResources(
+ val bounds =
+ calculateInsetsForRotationWithRotatedResources(
currentRotation = ROTATION_NONE,
targetRotation = ROTATION_NONE,
sysUICutout = sysUICutout,
@@ -658,7 +704,8 @@
isRtl = false,
dotWidth = 10,
bottomAlignedMargin = 5,
- statusBarContentHeight = 15)
+ statusBarContentHeight = 15
+ )
// Content in the status bar is centered vertically. To achieve the bottom margin we want,
// we need to "shrink" the height of the status bar until the centered content has the
@@ -694,12 +741,11 @@
// THEN only the landscape/seascape rotations should avoid the cutout area because of the
// potential letterboxing
var targetRotation = ROTATION_NONE
- var expectedBounds = Rect(minLeftPadding,
- 0,
- screenBounds.right - minRightPadding,
- sbHeightPortrait)
+ var expectedBounds =
+ Rect(minLeftPadding, 0, screenBounds.right - minRightPadding, sbHeightPortrait)
- var bounds = calculateInsetsForRotationWithRotatedResources(
+ var bounds =
+ calculateInsetsForRotationWithRotatedResources(
currentRotation,
targetRotation,
sysUICutout = sysUICutout,
@@ -710,17 +756,17 @@
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight)
+ statusBarContentHeight
+ )
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
targetRotation = ROTATION_LANDSCAPE
- expectedBounds = Rect(dcBounds.height(),
- 0,
- screenBounds.height() - minRightPadding,
- sbHeightLandscape)
+ expectedBounds =
+ Rect(dcBounds.height(), 0, screenBounds.height() - minRightPadding, sbHeightLandscape)
- bounds = calculateInsetsForRotationWithRotatedResources(
+ bounds =
+ calculateInsetsForRotationWithRotatedResources(
currentRotation,
targetRotation,
sysUICutout = sysUICutout,
@@ -731,17 +777,17 @@
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight)
+ statusBarContentHeight
+ )
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
targetRotation = ROTATION_UPSIDE_DOWN
- expectedBounds = Rect(minLeftPadding,
- 0,
- screenBounds.right - minRightPadding,
- sbHeightPortrait)
+ expectedBounds =
+ Rect(minLeftPadding, 0, screenBounds.right - minRightPadding, sbHeightPortrait)
- bounds = calculateInsetsForRotationWithRotatedResources(
+ bounds =
+ calculateInsetsForRotationWithRotatedResources(
currentRotation,
targetRotation,
sysUICutout = sysUICutout,
@@ -752,17 +798,22 @@
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight)
+ statusBarContentHeight
+ )
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
targetRotation = ROTATION_SEASCAPE
- expectedBounds = Rect(minLeftPadding,
+ expectedBounds =
+ Rect(
+ minLeftPadding,
0,
screenBounds.height() - dcBounds.height() - dotWidth,
- sbHeightLandscape)
+ sbHeightLandscape
+ )
- bounds = calculateInsetsForRotationWithRotatedResources(
+ bounds =
+ calculateInsetsForRotationWithRotatedResources(
currentRotation,
targetRotation,
sysUICutout = sysUICutout,
@@ -773,7 +824,8 @@
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight)
+ statusBarContentHeight
+ )
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
}
@@ -793,12 +845,11 @@
// THEN content insets should only use rounded corner padding
var targetRotation = ROTATION_NONE
- var expectedBounds = Rect(minLeftPadding,
- 0,
- screenBounds.right - minRightPadding,
- sbHeightPortrait)
+ var expectedBounds =
+ Rect(minLeftPadding, 0, screenBounds.right - minRightPadding, sbHeightPortrait)
- var bounds = calculateInsetsForRotationWithRotatedResources(
+ var bounds =
+ calculateInsetsForRotationWithRotatedResources(
currentRotation,
targetRotation,
null, /* no cutout */
@@ -809,16 +860,16 @@
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight)
+ statusBarContentHeight
+ )
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
targetRotation = ROTATION_LANDSCAPE
- expectedBounds = Rect(minLeftPadding,
- 0,
- screenBounds.height() - minRightPadding,
- sbHeightLandscape)
+ expectedBounds =
+ Rect(minLeftPadding, 0, screenBounds.height() - minRightPadding, sbHeightLandscape)
- bounds = calculateInsetsForRotationWithRotatedResources(
+ bounds =
+ calculateInsetsForRotationWithRotatedResources(
currentRotation,
targetRotation,
null, /* no cutout */
@@ -829,16 +880,16 @@
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight)
+ statusBarContentHeight
+ )
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
targetRotation = ROTATION_UPSIDE_DOWN
- expectedBounds = Rect(minLeftPadding,
- 0,
- screenBounds.width() - minRightPadding,
- sbHeightPortrait)
+ expectedBounds =
+ Rect(minLeftPadding, 0, screenBounds.width() - minRightPadding, sbHeightPortrait)
- bounds = calculateInsetsForRotationWithRotatedResources(
+ bounds =
+ calculateInsetsForRotationWithRotatedResources(
currentRotation,
targetRotation,
null, /* no cutout */
@@ -849,16 +900,16 @@
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight)
+ statusBarContentHeight
+ )
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
targetRotation = ROTATION_LANDSCAPE
- expectedBounds = Rect(minLeftPadding,
- 0,
- screenBounds.height() - minRightPadding,
- sbHeightLandscape)
+ expectedBounds =
+ Rect(minLeftPadding, 0, screenBounds.height() - minRightPadding, sbHeightLandscape)
- bounds = calculateInsetsForRotationWithRotatedResources(
+ bounds =
+ calculateInsetsForRotationWithRotatedResources(
currentRotation,
targetRotation,
null, /* no cutout */
@@ -869,7 +920,8 @@
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight)
+ statusBarContentHeight
+ )
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
}
@@ -891,12 +943,11 @@
// THEN left should be set to the display cutout width, and right should use the minRight
val targetRotation = ROTATION_NONE
- val expectedBounds = Rect(dcBounds.right,
- 0,
- screenBounds.right - minRightPadding,
- sbHeightPortrait)
+ val expectedBounds =
+ Rect(dcBounds.right, 0, screenBounds.right - minRightPadding, sbHeightPortrait)
- val bounds = calculateInsetsForRotationWithRotatedResources(
+ val bounds =
+ calculateInsetsForRotationWithRotatedResources(
currentRotation,
targetRotation,
sysUICutout,
@@ -907,7 +958,8 @@
isRtl,
dotWidth,
BOTTOM_ALIGNED_MARGIN_NONE,
- statusBarContentHeight)
+ statusBarContentHeight
+ )
assertRects(expectedBounds, bounds, currentRotation, targetRotation)
}
@@ -915,8 +967,14 @@
@Test
fun testDisplayChanged_returnsUpdatedInsets() {
// GIVEN: get insets on the first display and switch to the second display
- val provider = StatusBarContentInsetsProvider(contextMock, configurationController,
- mock<DumpManager>(), mock<CommandRegistry>(), mock<SysUICutoutProvider>())
+ val provider =
+ StatusBarContentInsetsProvider(
+ contextMock,
+ configurationController,
+ mock<DumpManager>(),
+ mock<CommandRegistry>(),
+ mock<SysUICutoutProvider>()
+ )
configuration.windowConfiguration.setMaxBounds(Rect(0, 0, 1080, 2160))
val firstDisplayInsets = provider.getStatusBarContentAreaForRotation(ROTATION_NONE)
@@ -934,12 +992,17 @@
fun testDisplayChangedAndReturnedBack_returnsTheSameInsets() {
// GIVEN: get insets on the first display, switch to the second display,
// get insets and switch back
- val provider = StatusBarContentInsetsProvider(contextMock, configurationController,
- mock<DumpManager>(), mock<CommandRegistry>(), mock<SysUICutoutProvider>())
+ val provider =
+ StatusBarContentInsetsProvider(
+ contextMock,
+ configurationController,
+ mock<DumpManager>(),
+ mock<CommandRegistry>(),
+ mock<SysUICutoutProvider>()
+ )
configuration.windowConfiguration.setMaxBounds(Rect(0, 0, 1080, 2160))
- val firstDisplayInsetsFirstCall = provider
- .getStatusBarContentAreaForRotation(ROTATION_NONE)
+ val firstDisplayInsetsFirstCall = provider.getStatusBarContentAreaForRotation(ROTATION_NONE)
configuration.windowConfiguration.setMaxBounds(Rect(0, 0, 800, 600))
provider.getStatusBarContentAreaForRotation(ROTATION_NONE)
@@ -947,8 +1010,8 @@
configuration.windowConfiguration.setMaxBounds(Rect(0, 0, 1080, 2160))
// WHEN: get insets on the first display again
- val firstDisplayInsetsSecondCall = provider
- .getStatusBarContentAreaForRotation(ROTATION_NONE)
+ val firstDisplayInsetsSecondCall =
+ provider.getStatusBarContentAreaForRotation(ROTATION_NONE)
// THEN: insets for the first and second calls for the first display are the same
assertThat(firstDisplayInsetsFirstCall).isEqualTo(firstDisplayInsetsSecondCall)
@@ -960,15 +1023,22 @@
// Start out with an existing configuration with bounds
configuration.windowConfiguration.setMaxBounds(0, 0, 100, 100)
configurationController.onConfigurationChanged(configuration)
- val provider = StatusBarContentInsetsProvider(contextMock, configurationController,
- mock<DumpManager>(), mock<CommandRegistry>(), mock<SysUICutoutProvider>())
- val listener = object : StatusBarContentInsetsChangedListener {
- var triggered = false
+ val provider =
+ StatusBarContentInsetsProvider(
+ contextMock,
+ configurationController,
+ mock<DumpManager>(),
+ mock<CommandRegistry>(),
+ mock<SysUICutoutProvider>()
+ )
+ val listener =
+ object : StatusBarContentInsetsChangedListener {
+ var triggered = false
- override fun onStatusBarContentInsetsChanged() {
- triggered = true
+ override fun onStatusBarContentInsetsChanged() {
+ triggered = true
+ }
}
- }
provider.addCallback(listener)
// WHEN the config is updated with new bounds
@@ -982,15 +1052,22 @@
@Test
fun onDensityOrFontScaleChanged_listenerNotified() {
configuration.densityDpi = 12
- val provider = StatusBarContentInsetsProvider(contextMock, configurationController,
- mock<DumpManager>(), mock<CommandRegistry>(), mock<SysUICutoutProvider>())
- val listener = object : StatusBarContentInsetsChangedListener {
- var triggered = false
+ val provider =
+ StatusBarContentInsetsProvider(
+ contextMock,
+ configurationController,
+ mock<DumpManager>(),
+ mock<CommandRegistry>(),
+ mock<SysUICutoutProvider>()
+ )
+ val listener =
+ object : StatusBarContentInsetsChangedListener {
+ var triggered = false
- override fun onStatusBarContentInsetsChanged() {
- triggered = true
+ override fun onStatusBarContentInsetsChanged() {
+ triggered = true
+ }
}
- }
provider.addCallback(listener)
// WHEN the config is updated
@@ -1003,15 +1080,22 @@
@Test
fun onThemeChanged_listenerNotified() {
- val provider = StatusBarContentInsetsProvider(contextMock, configurationController,
- mock<DumpManager>(), mock<CommandRegistry>(), mock<SysUICutoutProvider>())
- val listener = object : StatusBarContentInsetsChangedListener {
- var triggered = false
+ val provider =
+ StatusBarContentInsetsProvider(
+ contextMock,
+ configurationController,
+ mock<DumpManager>(),
+ mock<CommandRegistry>(),
+ mock<SysUICutoutProvider>()
+ )
+ val listener =
+ object : StatusBarContentInsetsChangedListener {
+ var triggered = false
- override fun onStatusBarContentInsetsChanged() {
- triggered = true
+ override fun onStatusBarContentInsetsChanged() {
+ triggered = true
+ }
}
- }
provider.addCallback(listener)
configurationController.notifyThemeChanged()
@@ -1027,7 +1111,7 @@
@Rotation targetRotation: Int
) {
assertTrue(
- "Rects must match. currentRotation=${RotationUtils.toString(currentRotation)}" +
+ "Rects must match. currentRotation=${RotationUtils.toString(currentRotation)}" +
" targetRotation=${RotationUtils.toString(targetRotation)}" +
" expected=$expected actual=$actual",
expected.equals(actual)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
similarity index 99%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
rename to packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
index 4c824c0..87d25dd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/policy/BaseHeadsUpManagerTest.java
@@ -46,12 +46,12 @@
import android.graphics.Region;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
-import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.test.filters.SmallTest;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.logging.testing.UiEventLoggerFake;
@@ -75,8 +75,8 @@
import org.mockito.junit.MockitoRule;
@SmallTest
-@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
+@RunWith(AndroidJUnit4.class)
public class BaseHeadsUpManagerTest extends SysuiTestCase {
@Rule
public MockitoRule rule = MockitoJUnit.rule();
diff --git a/packages/SystemUI/shared/biometrics/Android.bp b/packages/SystemUI/shared/biometrics/Android.bp
index 2bd7d97..63de81d 100644
--- a/packages/SystemUI/shared/biometrics/Android.bp
+++ b/packages/SystemUI/shared/biometrics/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_system_ui_please_use_a_more_specific_subteam_if_possible_",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_base_packages_SystemUI_license"
diff --git a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/BiometricUserInfo.kt b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/BiometricUserInfo.kt
index fdac37b..b0d6611 100644
--- a/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/BiometricUserInfo.kt
+++ b/packages/SystemUI/shared/biometrics/src/com/android/systemui/biometrics/shared/model/BiometricUserInfo.kt
@@ -21,8 +21,12 @@
*
* If the user's fallback credential is owned by another profile user the [deviceCredentialOwnerId]
* will differ from the user's [userId].
+ *
+ * If prompt requests to use the user's parent profile for device credential,
+ * [userIdForPasswordEntry] might differ from the user's [userId].
*/
data class BiometricUserInfo(
val userId: Int,
val deviceCredentialOwnerId: Int = userId,
+ val userIdForPasswordEntry: Int = userId,
)
diff --git a/packages/SystemUI/shared/keyguard/Android.bp b/packages/SystemUI/shared/keyguard/Android.bp
index 2181439..2b3c77a 100644
--- a/packages/SystemUI/shared/keyguard/Android.bp
+++ b/packages/SystemUI/shared/keyguard/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_system_ui_please_use_a_more_specific_subteam_if_possible_",
// See: http://go/android-license-faq
// A large-scale-change added 'default_applicable_licenses' to import
// all of the 'license_kinds' from "frameworks_base_packages_SystemUI_license"
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java
index a9928d8..63088aa 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceViewController.java
@@ -20,6 +20,7 @@
import android.app.PendingIntent;
import android.net.Uri;
+import android.os.Handler;
import android.os.Trace;
import android.provider.Settings;
import android.util.Log;
@@ -39,6 +40,9 @@
import com.android.keyguard.dagger.KeyguardStatusViewScope;
import com.android.systemui.Dumpable;
+import com.android.systemui.Flags;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.KeyguardSliceProvider;
import com.android.systemui.plugins.ActivityStarter;
@@ -60,6 +64,8 @@
Dumpable {
private static final String TAG = "KeyguardSliceViewCtrl";
+ private final Handler mHandler;
+ private final Handler mBgHandler;
private final ActivityStarter mActivityStarter;
private final ConfigurationController mConfigurationController;
private final TunerService mTunerService;
@@ -105,6 +111,8 @@
@Inject
public KeyguardSliceViewController(
+ @Main Handler handler,
+ @Background Handler bgHandler,
KeyguardSliceView keyguardSliceView,
ActivityStarter activityStarter,
ConfigurationController configurationController,
@@ -112,6 +120,8 @@
DumpManager dumpManager,
DisplayTracker displayTracker) {
super(keyguardSliceView);
+ mHandler = handler;
+ mBgHandler = bgHandler;
mActivityStarter = activityStarter;
mConfigurationController = configurationController;
mTunerService = tunerService;
@@ -182,24 +192,34 @@
* Update contents of the view.
*/
public void refresh() {
- Slice slice;
+
Trace.beginSection("KeyguardSliceViewController#refresh");
- // We can optimize performance and avoid binder calls when we know that we're bound
- // to a Slice on the same process.
- if (KeyguardSliceProvider.KEYGUARD_SLICE_URI.equals(mKeyguardSliceUri.toString())) {
- KeyguardSliceProvider instance = KeyguardSliceProvider.getAttachedInstance();
- if (instance != null) {
- slice = instance.onBindSlice(mKeyguardSliceUri);
+ try {
+ Slice slice;
+ if (KeyguardSliceProvider.KEYGUARD_SLICE_URI.equals(mKeyguardSliceUri.toString())) {
+ KeyguardSliceProvider instance = KeyguardSliceProvider.getAttachedInstance();
+ if (instance != null) {
+ if (Flags.sliceManagerBinderCallBackground()) {
+ mBgHandler.post(() -> {
+ Slice _slice = instance.onBindSlice(mKeyguardSliceUri);
+ mHandler.post(() -> mObserver.onChanged(_slice));
+ });
+ return;
+ }
+ slice = instance.onBindSlice(mKeyguardSliceUri);
+ } else {
+ Log.w(TAG, "Keyguard slice not bound yet?");
+ slice = null;
+ }
} else {
- Log.w(TAG, "Keyguard slice not bound yet?");
- slice = null;
+ // TODO: Make SliceViewManager injectable
+ slice = SliceViewManager.getInstance(mView.getContext()).bindSlice(
+ mKeyguardSliceUri);
}
- } else {
- // TODO: Make SliceViewManager injectable
- slice = SliceViewManager.getInstance(mView.getContext()).bindSlice(mKeyguardSliceUri);
+ mObserver.onChanged(slice);
+ } finally {
+ Trace.endSection();
}
- mObserver.onChanged(slice);
- Trace.endSection();
}
void showSlice(Slice slice) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt
index f5603ed..c3e7818 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.kt
@@ -132,13 +132,13 @@
override fun onViewAttached() {
dialogManager.registerListener(dialogListener)
dumpManager.registerDumpable(dumpTag, this)
- udfpsOverlayInteractor.setHandleTouches(shouldHandle = true)
+ udfpsOverlayInteractor.setHandleTouches(shouldHandle = !shouldPauseAuth())
}
override fun onViewDetached() {
dialogManager.unregisterListener(dialogListener)
dumpManager.unregisterDumpable(dumpTag)
- udfpsOverlayInteractor.setHandleTouches(shouldHandle = true)
+ udfpsOverlayInteractor.setHandleTouches(shouldHandle = !shouldPauseAuth())
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
index 018d92e..ec54e4ce 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
@@ -378,7 +378,7 @@
}
}
- override fun onViewDetached() {
+ public override fun onViewDetached() {
super.onViewDetached()
alternateBouncerInteractor.setAlternateBouncerUIAvailable(false, uniqueIdentifier)
faceDetectRunning = false
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractor.kt
index 191533c..23afb7c 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractor.kt
@@ -7,9 +7,9 @@
import com.android.internal.widget.LockPatternUtils
import com.android.internal.widget.LockscreenCredential
import com.android.internal.widget.VerifyCredentialResponse
-import com.android.systemui.res.R
import com.android.systemui.biometrics.domain.model.BiometricPromptRequest
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.res.R
import com.android.systemui.util.time.SystemClock
import javax.inject.Inject
import kotlinx.coroutines.delay
@@ -29,6 +29,9 @@
/** Get the effective user id (profile owner, if one exists) */
fun getCredentialOwnerOrSelfId(userId: Int): Int
+ /** Get parent user profile (if exists) */
+ fun getParentProfileIdOrSelfId(userId: Int): Int
+
/**
* Verifies a credential and returns a stream of results.
*
@@ -58,6 +61,9 @@
override fun getCredentialOwnerOrSelfId(userId: Int): Int =
userManager.getCredentialOwnerProfile(userId)
+ override fun getParentProfileIdOrSelfId(userId: Int): Int =
+ userManager.getProfileParent(userId).id
+
override fun verifyCredential(
request: BiometricPromptRequest.Credential,
credential: LockscreenCredential,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractor.kt b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractor.kt
index 359e2e7..e3facff 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/domain/interactor/PromptCredentialInteractor.kt
@@ -75,20 +75,32 @@
PromptKind.Pin ->
BiometricPromptRequest.Credential.Pin(
info = promptInfo,
- userInfo = userInfo(userId),
+ userInfo =
+ userInfo(
+ userId,
+ promptInfo.shouldUseParentProfileForDeviceCredential()
+ ),
operationInfo = operationInfo(challenge)
)
PromptKind.Pattern ->
BiometricPromptRequest.Credential.Pattern(
info = promptInfo,
- userInfo = userInfo(userId),
+ userInfo =
+ userInfo(
+ userId,
+ promptInfo.shouldUseParentProfileForDeviceCredential()
+ ),
operationInfo = operationInfo(challenge),
stealthMode = credentialInteractor.isStealthModeActive(userId)
)
PromptKind.Password ->
BiometricPromptRequest.Credential.Password(
info = promptInfo,
- userInfo = userInfo(userId),
+ userInfo =
+ userInfo(
+ userId,
+ promptInfo.shouldUseParentProfileForDeviceCredential()
+ ),
operationInfo = operationInfo(challenge)
)
else -> null
@@ -96,10 +108,17 @@
}
.distinctUntilChanged()
- private fun userInfo(userId: Int): BiometricUserInfo =
+ private fun userInfo(
+ userId: Int,
+ useParentProfileForDeviceCredential: Boolean
+ ): BiometricUserInfo =
BiometricUserInfo(
userId = userId,
- deviceCredentialOwnerId = credentialInteractor.getCredentialOwnerOrSelfId(userId)
+ deviceCredentialOwnerId = credentialInteractor.getCredentialOwnerOrSelfId(userId),
+ userIdForPasswordEntry =
+ if (useParentProfileForDeviceCredential)
+ credentialInteractor.getParentProfileIdOrSelfId(userId)
+ else credentialInteractor.getCredentialOwnerOrSelfId(userId),
)
private fun operationInfo(challenge: Long): BiometricOperationInfo =
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialPasswordViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialPasswordViewBinder.kt
index 0ad07ba..4ed786b 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialPasswordViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/CredentialPasswordViewBinder.kt
@@ -12,12 +12,12 @@
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
-import com.android.systemui.res.R
import com.android.systemui.biometrics.ui.CredentialPasswordView
import com.android.systemui.biometrics.ui.CredentialView
import com.android.systemui.biometrics.ui.IPinPad
import com.android.systemui.biometrics.ui.viewmodel.CredentialViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.res.R
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.firstOrNull
@@ -42,7 +42,7 @@
view.repeatWhenAttached {
// the header info never changes - do it early
val header = viewModel.header.first()
- passwordField.setTextOperationUser(UserHandle.of(header.user.deviceCredentialOwnerId))
+ passwordField.setTextOperationUser(UserHandle.of(header.user.userIdForPasswordEntry))
viewModel.inputFlags.firstOrNull()?.let { flags -> passwordField.inputType = flags }
if (requestFocusForInput) {
passwordField.requestFocus()
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalWidgetCategories.kt b/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalWidgetCategories.kt
new file mode 100644
index 0000000..03f54c8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/model/CommunalWidgetCategories.kt
@@ -0,0 +1,31 @@
+/*
+ * 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.communal.data.model
+
+import android.appwidget.AppWidgetProviderInfo
+
+/**
+ * The widget categories to display on communal hub (where categories is a bitfield with values that
+ * match those in {@link AppWidgetProviderInfo}).
+ */
+@JvmInline
+value class CommunalWidgetCategories(
+ // The default is keyguard widgets.
+ val categories: Int = AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD
+) {
+ fun contains(category: Int) = (categories and category) == category
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt
index 201b049..7c65d21 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt
@@ -18,11 +18,13 @@
import android.app.admin.DevicePolicyManager
import android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL
+import android.appwidget.AppWidgetProviderInfo
import android.content.IntentFilter
import android.content.pm.UserInfo
import com.android.systemui.Flags.communalHub
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.communal.data.model.CommunalEnabledState
+import com.android.systemui.communal.data.model.CommunalWidgetCategories
import com.android.systemui.communal.data.model.DisabledReason
import com.android.systemui.communal.data.model.DisabledReason.DISABLED_REASON_DEVICE_POLICY
import com.android.systemui.communal.data.model.DisabledReason.DISABLED_REASON_FLAG
@@ -48,6 +50,12 @@
interface CommunalSettingsRepository {
/** A [CommunalEnabledState] for the specified user. */
fun getEnabledState(user: UserInfo): Flow<CommunalEnabledState>
+
+ /**
+ * A flow that reports the widget categories to show on the hub as selected by the user in
+ * Settings.
+ */
+ fun getWidgetCategories(user: UserInfo): Flow<CommunalWidgetCategories>
}
@SysUISingleton
@@ -89,6 +97,23 @@
.flowOn(bgDispatcher)
}
+ override fun getWidgetCategories(user: UserInfo): Flow<CommunalWidgetCategories> =
+ secureSettings
+ .observerFlow(userId = user.id, names = arrayOf(GLANCEABLE_HUB_CONTENT_SETTING))
+ // Force an update
+ .onStart { emit(Unit) }
+ .map {
+ CommunalWidgetCategories(
+ // The default is to show only keyguard widgets.
+ secureSettings.getIntForUser(
+ GLANCEABLE_HUB_CONTENT_SETTING,
+ AppWidgetProviderInfo.WIDGET_CATEGORY_KEYGUARD,
+ user.id
+ )
+ )
+ }
+ .flowOn(bgDispatcher)
+
private fun getEnabledByUser(user: UserInfo): Flow<Boolean> =
secureSettings
.observerFlow(userId = user.id, names = arrayOf(GLANCEABLE_HUB_ENABLED))
@@ -114,6 +139,7 @@
companion object {
const val GLANCEABLE_HUB_ENABLED = "glanceable_hub_enabled"
+ const val GLANCEABLE_HUB_CONTENT_SETTING = "glanceable_hub_content_setting"
private const val ENABLED_SETTING_DEFAULT = 1
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt
index 0b096ce..20f60b7 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt
@@ -17,6 +17,7 @@
package com.android.systemui.communal.domain.interactor
import com.android.systemui.communal.data.model.CommunalEnabledState
+import com.android.systemui.communal.data.model.CommunalWidgetCategories
import com.android.systemui.communal.data.repository.CommunalSettingsRepository
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
@@ -55,4 +56,16 @@
.map { model -> model.enabled }
// Start this eagerly since the value is accessed synchronously in many places.
.stateIn(scope = bgScope, started = SharingStarted.Eagerly, initialValue = false)
+
+ /** What widget categories to show on the hub. */
+ val communalWidgetCategories: StateFlow<Int> =
+ userInteractor.selectedUserInfo
+ .flatMapLatest { user -> repository.getWidgetCategories(user) }
+ .map { categories -> categories.categories }
+ .stateIn(
+ scope = bgScope,
+ // Start this eagerly since the value can be accessed synchronously.
+ started = SharingStarted.Eagerly,
+ initialValue = CommunalWidgetCategories().categories
+ )
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
index 0b355cc..efbb11e 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
@@ -18,6 +18,7 @@
import com.android.internal.logging.UiEventLogger
import com.android.systemui.communal.domain.interactor.CommunalInteractor
+import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.communal.shared.log.CommunalUiEvent
import com.android.systemui.dagger.SysUISingleton
@@ -40,6 +41,7 @@
@Inject
constructor(
private val communalInteractor: CommunalInteractor,
+ private val communalSettingsInteractor: CommunalSettingsInteractor,
@Named(MediaModule.COMMUNAL_HUB) mediaHost: MediaHost,
private val uiEventLogger: UiEventLogger,
@CommunalLog logBuffer: LogBuffer,
@@ -84,6 +86,10 @@
uiEventLogger.log(CommunalUiEvent.COMMUNAL_HUB_REORDER_WIDGET_CANCEL)
}
+ /** Returns the widget categories to show on communal hub. */
+ val getCommunalWidgetCategories: Int
+ get() = communalSettingsInteractor.communalWidgetCategories.value
+
/** Sets whether edit mode is currently open */
fun setEditModeOpen(isOpen: Boolean) = communalInteractor.setEditModeOpen(isOpen)
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
index 337a98a..a5a390d 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt
@@ -16,6 +16,7 @@
package com.android.systemui.communal.widgets
+import android.appwidget.AppWidgetManager
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Bundle
@@ -137,6 +138,10 @@
R.dimen.communal_widget_picker_desired_height
)
)
+ putExtra(
+ AppWidgetManager.EXTRA_CATEGORY_FILTER,
+ communalViewModel.getCommunalWidgetCategories
+ )
}
)
} catch (e: Exception) {
diff --git a/packages/SystemUI/src/com/android/systemui/dump/DumpHandler.kt b/packages/SystemUI/src/com/android/systemui/dump/DumpHandler.kt
index 7150d69e..9876fe4 100644
--- a/packages/SystemUI/src/com/android/systemui/dump/DumpHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/dump/DumpHandler.kt
@@ -222,13 +222,18 @@
val buffers = dumpManager.getLogBuffers()
val tableBuffers = dumpManager.getTableLogBuffers()
- targets.forEach { target ->
- findTargetInCollection(target, dumpables, buffers, tableBuffers)?.dump(pw, args)
- }
+ val matches =
+ if (args.matchAll) {
+ findAllMatchesInCollection(targets, dumpables, buffers, tableBuffers)
+ } else {
+ findBestMatchesInCollection(targets, dumpables, buffers, tableBuffers)
+ }
+ matches.forEach { it.dump(pw, args) }
} else {
if (args.listOnly) {
val dumpables = dumpManager.getDumpables()
val buffers = dumpManager.getLogBuffers()
+ val tableBuffers = dumpManager.getTableLogBuffers()
pw.println("Dumpables:")
listTargetNames(dumpables, pw)
@@ -236,18 +241,23 @@
pw.println("Buffers:")
listTargetNames(buffers, pw)
+ pw.println()
+
+ pw.println("TableBuffers:")
+ listTargetNames(tableBuffers, pw)
} else {
pw.println("Nothing to dump :(")
}
}
}
+ /** Finds the best match for a particular target */
private fun findTargetInCollection(
target: String,
dumpables: Collection<DumpableEntry>,
logBuffers: Collection<LogBufferEntry>,
tableBuffers: Collection<TableLogBufferEntry>,
- ) =
+ ): DumpsysEntry? =
sequence {
findBestTargetMatch(dumpables, target)?.let { yield(it) }
findBestTargetMatch(logBuffers, target)?.let { yield(it) }
@@ -256,6 +266,31 @@
.sortedBy { it.name }
.minByOrNull { it.name.length }
+ /** Finds the best match for each target, if any, in the order of the targets */
+ private fun findBestMatchesInCollection(
+ targets: List<String>,
+ dumpables: Collection<DumpableEntry>,
+ logBuffers: Collection<LogBufferEntry>,
+ tableBuffers: Collection<TableLogBufferEntry>,
+ ): List<DumpsysEntry> =
+ targets.mapNotNull { target ->
+ findTargetInCollection(target, dumpables, logBuffers, tableBuffers)
+ }
+
+ /** Finds all matches for any target, returning in the --list order. */
+ private fun findAllMatchesInCollection(
+ targets: List<String>,
+ dumpables: Collection<DumpableEntry>,
+ logBuffers: Collection<LogBufferEntry>,
+ tableBuffers: Collection<TableLogBufferEntry>,
+ ): List<DumpsysEntry> =
+ sequence {
+ yieldAll(dumpables.filter { it.matchesAny(targets) })
+ yieldAll(logBuffers.filter { it.matchesAny(targets) })
+ yieldAll(tableBuffers.filter { it.matchesAny(targets) })
+ }
+ .sortedBy { it.name }.toList()
+
private fun dumpConfig(pw: PrintWriter) {
config.dump(pw, arrayOf())
}
@@ -272,6 +307,11 @@
pw.println("etc.")
pw.println()
+ pw.println("Print all matches, instead of the best match:")
+ pw.println("$ <invocation> --all <targets>")
+ pw.println("$ <invocation> --all Log")
+ pw.println()
+
pw.println("Special commands:")
pw.println("$ <invocation> dumpables")
pw.println("$ <invocation> buffers")
@@ -325,9 +365,10 @@
"--help" -> {
pArgs.command = "help"
}
- // This flag is passed as part of the proto dump in Bug reports, we can ignore
- // it because this is our default behavior.
- "-a" -> {}
+ "-a",
+ "--all" -> {
+ pArgs.matchAll = true
+ }
else -> {
throw ArgParseException("Unknown flag: $arg")
}
@@ -386,15 +427,19 @@
const val DUMPSYS_DUMPABLE_DIVIDER =
"----------------------------------------------------------------------------"
+ private fun DumpsysEntry.matches(target: String) = name.endsWith(target)
+ private fun DumpsysEntry.matchesAny(targets: Collection<String>) =
+ targets.any { matches(it) }
+
private fun findBestTargetMatch(c: Collection<DumpsysEntry>, target: String) =
- c.asSequence().filter { it.name.endsWith(target) }.minByOrNull { it.name.length }
+ c.asSequence().filter { it.matches(target) }.minByOrNull { it.name.length }
private fun findBestProtoTargetMatch(
c: Collection<DumpableEntry>,
target: String
): ProtoDumpable? =
c.asSequence()
- .filter { it.name.endsWith(target) }
+ .filter { it.matches(target) }
.filter { it.dumpable is ProtoDumpable }
.minByOrNull { it.name.length }
?.dumpable as? ProtoDumpable
@@ -440,40 +485,34 @@
}
/**
- * Utility to write a [DumpableEntry] to the given [PrintWriter] in a
- * dumpsys-appropriate format.
+ * Utility to write a [DumpableEntry] to the given [PrintWriter] in a dumpsys-appropriate
+ * format.
*/
private fun dumpDumpable(
- entry: DumpableEntry,
- pw: PrintWriter,
- args: Array<String> = arrayOf(),
- ) = pw.wrapSection(entry) {
- entry.dumpable.dump(pw, args)
- }
+ entry: DumpableEntry,
+ pw: PrintWriter,
+ args: Array<String> = arrayOf(),
+ ) = pw.wrapSection(entry) { entry.dumpable.dump(pw, args) }
/**
- * Utility to write a [LogBufferEntry] to the given [PrintWriter] in a
- * dumpsys-appropriate format.
+ * Utility to write a [LogBufferEntry] to the given [PrintWriter] in a dumpsys-appropriate
+ * format.
*/
private fun dumpBuffer(
- entry: LogBufferEntry,
- pw: PrintWriter,
- tailLength: Int = 0,
- ) = pw.wrapSection(entry) {
- entry.buffer.dump(pw, tailLength)
- }
+ entry: LogBufferEntry,
+ pw: PrintWriter,
+ tailLength: Int = 0,
+ ) = pw.wrapSection(entry) { entry.buffer.dump(pw, tailLength) }
/**
* Utility to write a [TableLogBufferEntry] to the given [PrintWriter] in a
* dumpsys-appropriate format.
*/
private fun dumpTableBuffer(
- entry: TableLogBufferEntry,
- pw: PrintWriter,
- args: Array<String> = arrayOf(),
- ) = pw.wrapSection(entry) {
- entry.table.dump(pw, args)
- }
+ entry: TableLogBufferEntry,
+ pw: PrintWriter,
+ args: Array<String> = arrayOf(),
+ ) = pw.wrapSection(entry) { entry.table.dump(pw, args) }
/**
* Zero-arg utility to write a [DumpsysEntry] to the given [PrintWriter] in a
@@ -513,6 +552,7 @@
var tailLength: Int = 0
var command: String? = null
var listOnly = false
+ var matchAll = false
var proto = false
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 6eff792..56162ae 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -102,11 +102,6 @@
default = true
)
- // TODO(b/301955929)
- @JvmField
- val NOTIF_LS_BACKGROUND_THREAD =
- releasedFlag("notification_lockscreen_mgr_bg_thread")
-
// 200 - keyguard/lockscreen
// ** Flag retired **
// public static final BooleanFlag KEYGUARD_LAYOUT =
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/shared/model/StickyKey.kt b/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/shared/model/StickyKey.kt
index 72a81cb..0f1cc99 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/shared/model/StickyKey.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/stickykeys/shared/model/StickyKey.kt
@@ -20,8 +20,8 @@
value class Locked(val locked: Boolean)
enum class ModifierKey(val displayedText: String) {
- ALT("ALT LEFT"),
- ALT_GR("ALT RIGHT"),
+ ALT("ALT"),
+ ALT_GR("ALT"),
CTRL("CTRL"),
META("ACTION"),
SHIFT("SHIFT"),
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
index 8eb1a50..b0a3881 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractor.kt
@@ -46,7 +46,6 @@
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.res.R
import com.android.systemui.settings.UserTracker
-import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.shared.customization.data.content.CustomizationProviderContract as Contract
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -56,7 +55,6 @@
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
@@ -68,7 +66,6 @@
@Inject
constructor(
private val keyguardInteractor: KeyguardInteractor,
- private val shadeInteractor: ShadeInteractor,
private val lockPatternUtils: LockPatternUtils,
private val keyguardStateController: KeyguardStateController,
private val userTracker: UserTracker,
@@ -103,10 +100,9 @@
quickAffordanceAlwaysVisible(position),
keyguardInteractor.isDozing,
keyguardInteractor.isKeyguardShowing,
- shadeInteractor.anyExpansion.map { it < 1.0f }.distinctUntilChanged(),
biometricSettingsRepository.isCurrentUserInLockdown,
- ) { affordance, isDozing, isKeyguardShowing, isQuickSettingsVisible, isUserInLockdown ->
- if (!isDozing && isKeyguardShowing && isQuickSettingsVisible && !isUserInLockdown) {
+ ) { affordance, isDozing, isKeyguardShowing, isUserInLockdown ->
+ if (!isDozing && isKeyguardShowing && !isUserInLockdown) {
affordance
} else {
KeyguardQuickAffordanceModel.Hidden
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
index 54a7ca4..a22671d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSection.kt
@@ -205,7 +205,7 @@
fun getDimen(context: Context, name: String): Int {
val res = context.packageManager.getResourcesForApplication(context.packageName)
val id = res.getIdentifier(name, "dimen", context.packageName)
- return res.getDimensionPixelSize(id)
+ return if (id == 0) 0 else res.getDimensionPixelSize(id)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt
index 218af29..6a3b920 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultNotificationStackScrollLayoutSection.kt
@@ -24,7 +24,7 @@
import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
import androidx.constraintlayout.widget.ConstraintSet.START
import androidx.constraintlayout.widget.ConstraintSet.TOP
-import com.android.systemui.Flags.centralizedStatusBarDimensRefactor
+import com.android.systemui.Flags.centralizedStatusBarHeightFix
import com.android.systemui.Flags.migrateClocksToBlueprint
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.res.R
@@ -80,7 +80,7 @@
val useLargeScreenHeader =
context.resources.getBoolean(R.bool.config_use_large_screen_shade_header)
val marginTopLargeScreen =
- if (centralizedStatusBarDimensRefactor()) {
+ if (centralizedStatusBarHeightFix()) {
largeScreenHeaderHelperLazy.get().getLargeScreenHeaderHeight()
} else {
context.resources.getDimensionPixelSize(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
index ab0d489..f65f376 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/ClockSizeTransition.kt
@@ -34,6 +34,7 @@
import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
import com.android.systemui.res.R
import com.android.systemui.shared.R as sharedR
+import com.google.android.material.math.MathUtils
import kotlin.math.abs
internal fun View.setRect(rect: Rect) =
@@ -59,15 +60,6 @@
override fun captureEndValues(transition: TransitionValues) = captureValues(transition)
override fun captureStartValues(transition: TransitionValues) = captureValues(transition)
override fun getTransitionProperties(): Array<String> = TRANSITION_PROPERTIES
- open fun mutateBounds(
- view: View,
- fromVis: Int,
- toVis: Int,
- fromBounds: Rect,
- toBounds: Rect,
- fromSSBounds: Rect?,
- toSSBounds: Rect?
- ) {}
private fun captureValues(transition: TransitionValues) {
val view = transition.view
@@ -81,6 +73,16 @@
transition.values[SMARTSPACE_BOUNDS] = Rect(ss.left, ss.top, ss.right, ss.bottom)
}
+ open fun mutateBounds(
+ view: View,
+ fromIsVis: Boolean,
+ toIsVis: Boolean,
+ fromBounds: Rect,
+ toBounds: Rect,
+ fromSSBounds: Rect?,
+ toSSBounds: Rect?
+ ) {}
+
override fun createAnimator(
sceenRoot: ViewGroup,
startValues: TransitionValues?,
@@ -88,7 +90,6 @@
): Animator? {
if (startValues == null || endValues == null) return null
- val fromView = startValues.view
var fromVis = startValues.values[PROP_VISIBILITY] as Int
var fromIsVis = fromVis == View.VISIBLE
var fromAlpha = startValues.values[PROP_ALPHA] as Float
@@ -111,7 +112,7 @@
fromVis = View.INVISIBLE
}
- mutateBounds(toView, fromVis, toVis, fromBounds, toBounds, fromSSBounds, toSSBounds)
+ mutateBounds(toView, fromIsVis, toIsVis, fromBounds, toBounds, fromSSBounds, toSSBounds)
if (fromIsVis == toIsVis && fromBounds.equals(toBounds)) {
if (DEBUG) {
Log.w(
@@ -127,7 +128,7 @@
val sendToBack = fromIsVis && !toIsVis
fun lerp(start: Int, end: Int, fract: Float): Int =
- (start * (1f - fract) + end * fract).toInt()
+ MathUtils.lerp(start.toFloat(), end.toFloat(), fract).toInt()
fun computeBounds(fract: Float): Rect =
Rect(
lerp(fromBounds.left, toBounds.left, fract),
@@ -136,9 +137,14 @@
lerp(fromBounds.bottom, toBounds.bottom, fract)
)
- fun assignAnimValues(src: String, alpha: Float, fract: Float, vis: Int? = null) {
+ fun assignAnimValues(src: String, fract: Float, vis: Int? = null) {
val bounds = computeBounds(fract)
- if (DEBUG) Log.i(TAG, "$src: $toView; alpha=$alpha; vis=$vis; bounds=$bounds;")
+ val alpha = MathUtils.lerp(fromAlpha, toAlpha, fract)
+ if (DEBUG)
+ Log.i(
+ TAG,
+ "$src: $toView; fract=$fract; alpha=$alpha; vis=$vis; bounds=$bounds;"
+ )
toView.setVisibility(vis ?: View.VISIBLE)
toView.setAlpha(alpha)
toView.setRect(bounds)
@@ -154,12 +160,12 @@
)
}
- return ValueAnimator.ofFloat(fromAlpha, toAlpha).also { anim ->
+ return ValueAnimator.ofFloat(0f, 1f).also { anim ->
// We enforce the animation parameters on the target view every frame using a
// predraw listener. This is suboptimal but prevents issues with layout passes
// overwriting the animation for individual frames.
val predrawCallback = OnPreDrawListener {
- assignAnimValues("predraw", anim.animatedValue as Float, anim.animatedFraction)
+ assignAnimValues("predraw", anim.animatedFraction)
return@OnPreDrawListener true
}
@@ -169,16 +175,18 @@
anim.addListener(
object : AnimatorListenerAdapter() {
override fun onAnimationStart(anim: Animator) {
- assignAnimValues("start", fromAlpha, 0f)
+ assignAnimValues("start", 0f, fromVis)
}
override fun onAnimationEnd(anim: Animator) {
- assignAnimValues("end", toAlpha, 1f, toVis)
+ assignAnimValues("end", 1f, toVis)
if (sendToBack) toView.translationZ = 0f
toView.viewTreeObserver.removeOnPreDrawListener(predrawCallback)
}
}
)
+
+ assignAnimValues("init", 0f, fromVis)
toView.viewTreeObserver.addOnPreDrawListener(predrawCallback)
}
}
@@ -216,13 +224,16 @@
override fun mutateBounds(
view: View,
- fromVis: Int,
- toVis: Int,
+ fromIsVis: Boolean,
+ toIsVis: Boolean,
fromBounds: Rect,
toBounds: Rect,
fromSSBounds: Rect?,
toSSBounds: Rect?
) {
+ // Move normally if clock is not changing visibility
+ if (fromIsVis == toIsVis) return
+
fromBounds.left = toBounds.left
fromBounds.right = toBounds.right
if (viewModel.useLargeClock) {
@@ -271,13 +282,16 @@
override fun mutateBounds(
view: View,
- fromVis: Int,
- toVis: Int,
+ fromIsVis: Boolean,
+ toIsVis: Boolean,
fromBounds: Rect,
toBounds: Rect,
fromSSBounds: Rect?,
toSSBounds: Rect?
) {
+ // Move normally if clock is not changing visibility
+ if (fromIsVis == toIsVis) return
+
toBounds.left = fromBounds.left
toBounds.right = fromBounds.right
if (!viewModel.useLargeClock) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/DefaultClockSteppingTransition.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/DefaultClockSteppingTransition.kt
index 60ab40c..f601155 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/DefaultClockSteppingTransition.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/transitions/DefaultClockSteppingTransition.kt
@@ -78,8 +78,8 @@
}
companion object {
- private const val PROP_BOUNDS_LEFT = "splitShadeTransitionAdapter:boundsLeft"
- private const val PROP_X_IN_WINDOW = "splitShadeTransitionAdapter:xInWindow"
+ private const val PROP_BOUNDS_LEFT = "DefaultClockSteppingTransition:boundsLeft"
+ private const val PROP_X_IN_WINDOW = "DefaultClockSteppingTransition:xInWindow"
private val TRANSITION_PROPERTIES = arrayOf(PROP_BOUNDS_LEFT, PROP_X_IN_WINDOW)
private const val KEYGUARD_STATUS_VIEW_CUSTOM_CLOCK_MOVE_DURATION_MS = 1000L
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModel.kt
index f208e85..8a3b57b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModel.kt
@@ -18,7 +18,9 @@
package com.android.systemui.keyguard.ui.viewmodel
+import com.android.systemui.Flags.migrateClocksToBlueprint
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
@@ -38,6 +40,7 @@
keyguardTransitionInteractor: KeyguardTransitionInteractor,
goneToAodTransitionViewModel: GoneToAodTransitionViewModel,
goneToDozingTransitionViewModel: GoneToDozingTransitionViewModel,
+ keyguardInteractor: KeyguardInteractor,
) {
/** The alpha level for the entire lockscreen while in AOD. */
@@ -46,7 +49,8 @@
keyguardTransitionInteractor.transitions,
goneToAodTransitionViewModel.enterFromTopAnimationAlpha.onStart { emit(0f) },
goneToDozingTransitionViewModel.lockscreenAlpha.onStart { emit(0f) },
- ) { step, goneToAodAlpha, goneToDozingAlpha ->
+ keyguardInteractor.keyguardAlpha.onStart { emit(1f) },
+ ) { step, goneToAodAlpha, goneToDozingAlpha, keyguardAlpha ->
if (step.to == GONE) {
// When transitioning to GONE, only emit a value when complete as other
// transitions may be controlling the alpha fade
@@ -57,6 +61,8 @@
emit(goneToAodAlpha)
} else if (step.from == GONE && step.to == DOZING) {
emit(goneToDozingAlpha)
+ } else if (!migrateClocksToBlueprint()) {
+ emit(keyguardAlpha)
}
}
.distinctUntilChanged()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt
index 9fa1423..a0a77fb 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AodToLockscreenTransitionViewModel.kt
@@ -64,26 +64,21 @@
/** Ensure alpha is set to be visible */
fun lockscreenAlpha(viewState: ViewStateAccessor): Flow<Float> {
- var startAlpha: Float? = null
+ var startAlpha = 1f
return transitionAnimation.sharedFlow(
duration = 500.milliseconds,
- onStep = {
- if (startAlpha == null) {
- startAlpha = viewState.alpha()
- }
- MathUtils.lerp(startAlpha!!, 1f, it)
- },
- onFinish = {
- startAlpha = null
- 1f
- },
- onCancel = {
- startAlpha = null
- 1f
- },
+ onStart = { startAlpha = viewState.alpha() },
+ onStep = { MathUtils.lerp(startAlpha, 1f, it) },
)
}
+ val notificationAlpha: Flow<Float> =
+ transitionAnimation.sharedFlow(
+ duration = 500.milliseconds,
+ onStep = { it },
+ onCancel = { 1f },
+ )
+
val shortcutsAlpha: Flow<Float> =
transitionAnimation.sharedFlow(
duration = 167.milliseconds,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt
index 9fc759b..3540bec 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/GoneToAodTransitionViewModel.kt
@@ -55,11 +55,18 @@
duration = 500.milliseconds,
onStep = { translatePx + it * -translatePx },
onFinish = { 0f },
- onCancel = { 0f },
interpolator = EMPHASIZED_DECELERATE,
)
}
+ val notificationAlpha: Flow<Float> =
+ transitionAnimation.sharedFlow(
+ duration = 200.milliseconds,
+ onStep = { 1f - it },
+ // Needs to be 1f in order for HUNs to appear on AOD
+ onFinish = { 1f },
+ )
+
/** alpha animation upon entering AOD */
val enterFromTopAnimationAlpha: Flow<Float> =
transitionAnimation.sharedFlow(
@@ -67,7 +74,6 @@
duration = 400.milliseconds,
onStep = { it },
onFinish = { 1f },
- onCancel = { 1f },
)
val deviceEntryBackgroundViewAlpha: Flow<Float> =
transitionAnimation.immediatelyTransitionTo(0f)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
index fa18557..9afe8fc 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModel.kt
@@ -16,7 +16,7 @@
package com.android.systemui.keyguard.ui.viewmodel
-import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
+import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
@@ -36,7 +36,7 @@
constructor(
@Application applicationScope: CoroutineScope,
deviceEntryInteractor: DeviceEntryInteractor,
- communalSettingsInteractor: CommunalSettingsInteractor,
+ communalInteractor: CommunalInteractor,
val longPress: KeyguardLongPressViewModel,
val notifications: NotificationsPlaceholderViewModel,
) {
@@ -56,7 +56,7 @@
/** The key of the scene we should switch to when swiping left. */
val leftDestinationSceneKey: StateFlow<SceneKey?> =
- communalSettingsInteractor.isCommunalEnabled
+ communalInteractor.isCommunalAvailable
.map { available -> if (available) SceneKey.Communal else null }
.stateIn(
scope = applicationScope,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt
index 15459f4..4e6aa03 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToGoneTransitionViewModel.kt
@@ -55,23 +55,13 @@
)
fun lockscreenAlpha(viewState: ViewStateAccessor): Flow<Float> {
- var startAlpha: Float? = null
+ var startAlpha = 1f
return transitionAnimation.sharedFlow(
duration = 200.milliseconds,
- onStep = {
- if (startAlpha == null) {
- startAlpha = viewState.alpha()
- }
- MathUtils.lerp(startAlpha!!, 0f, it)
- },
- onFinish = {
- startAlpha = null
- 0f
- },
- onCancel = {
- startAlpha = null
- 1f
- },
+ onStart = { startAlpha = viewState.alpha() },
+ onStep = { MathUtils.lerp(startAlpha, 0f, it) },
+ onFinish = { 0f },
+ onCancel = { 1f },
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index c0d9644..4ee2db7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -18,7 +18,7 @@
import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS;
-import static com.android.systemui.Flags.centralizedStatusBarDimensRefactor;
+import static com.android.systemui.Flags.centralizedStatusBarHeightFix;
import android.content.Context;
import android.graphics.Canvas;
@@ -194,7 +194,7 @@
int topPadding = QSUtils.getQsHeaderSystemIconsAreaHeight(mContext);
if (!LargeScreenUtils.shouldUseLargeScreenShadeHeader(mContext.getResources())) {
topPadding =
- centralizedStatusBarDimensRefactor()
+ centralizedStatusBarHeightFix()
? LargeScreenHeaderHelper.getLargeScreenHeaderHeight(mContext)
: mContext.getResources()
.getDimensionPixelSize(
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
index 1b504a8..24b2d8a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
@@ -52,7 +52,7 @@
import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
-import com.android.systemui.qs.tiles.dialog.InternetDialogFactory;
+import com.android.systemui.qs.tiles.dialog.InternetDialogManager;
import com.android.systemui.statusbar.connectivity.AccessPointController;
import com.android.systemui.statusbar.connectivity.IconState;
import com.android.systemui.statusbar.connectivity.MobileDataIndicators;
@@ -83,7 +83,7 @@
private int mLastTileState = LAST_STATE_UNKNOWN;
protected final InternetSignalCallback mSignalCallback = new InternetSignalCallback();
- private final InternetDialogFactory mInternetDialogFactory;
+ private final InternetDialogManager mInternetDialogManager;
final Handler mHandler;
@Inject
@@ -99,11 +99,11 @@
QSLogger qsLogger,
NetworkController networkController,
AccessPointController accessPointController,
- InternetDialogFactory internetDialogFactory
+ InternetDialogManager internetDialogManager
) {
super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
- mInternetDialogFactory = internetDialogFactory;
+ mInternetDialogManager = internetDialogManager;
mHandler = mainHandler;
mController = networkController;
mAccessPointController = accessPointController;
@@ -125,7 +125,7 @@
@Override
protected void handleClick(@Nullable View view) {
- mHandler.post(() -> mInternetDialogFactory.create(true,
+ mHandler.post(() -> mInternetDialogManager.create(true,
mAccessPointController.canConfigMobileData(),
mAccessPointController.canConfigWifi(), view));
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt
index 13271c3..357743b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTileNewImpl.kt
@@ -33,7 +33,7 @@
import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.logging.QSLogger
import com.android.systemui.qs.tileimpl.QSTileImpl
-import com.android.systemui.qs.tiles.dialog.InternetDialogFactory
+import com.android.systemui.qs.tiles.dialog.InternetDialogManager
import com.android.systemui.res.R
import com.android.systemui.statusbar.connectivity.AccessPointController
import com.android.systemui.statusbar.pipeline.shared.ui.binder.InternetTileBinder
@@ -44,18 +44,18 @@
class InternetTileNewImpl
@Inject
constructor(
- host: QSHost,
- uiEventLogger: QsEventLogger,
- @Background backgroundLooper: Looper,
- @Main private val mainHandler: Handler,
- falsingManager: FalsingManager,
- metricsLogger: MetricsLogger,
- statusBarStateController: StatusBarStateController,
- activityStarter: ActivityStarter,
- qsLogger: QSLogger,
- viewModel: InternetTileViewModel,
- private val internetDialogFactory: InternetDialogFactory,
- private val accessPointController: AccessPointController,
+ host: QSHost,
+ uiEventLogger: QsEventLogger,
+ @Background backgroundLooper: Looper,
+ @Main private val mainHandler: Handler,
+ falsingManager: FalsingManager,
+ metricsLogger: MetricsLogger,
+ statusBarStateController: StatusBarStateController,
+ activityStarter: ActivityStarter,
+ qsLogger: QSLogger,
+ viewModel: InternetTileViewModel,
+ private val internetDialogManager: InternetDialogManager,
+ private val accessPointController: AccessPointController,
) :
QSTileImpl<QSTile.BooleanState>(
host,
@@ -86,7 +86,7 @@
override fun handleClick(view: View?) {
mainHandler.post {
- internetDialogFactory.create(
+ internetDialogManager.create(
aboveStatusBar = true,
accessPointController.canConfigMobileData(),
accessPointController.canConfigWifi(),
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java
similarity index 82%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java
index 03e0c1e..0dd0a60 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegate.java
@@ -61,7 +61,6 @@
import com.android.systemui.Prefs;
import com.android.systemui.accessibility.floatingmenu.AnnotationLinkSpan;
import com.android.systemui.animation.DialogTransitionAnimator;
-import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.res.R;
@@ -72,23 +71,30 @@
import java.util.List;
import java.util.concurrent.Executor;
-import javax.inject.Inject;
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
/**
* Dialog for showing mobile network, connected Wi-Fi network and Wi-Fi networks.
*/
-@SysUISingleton
-public class InternetDialog extends SystemUIDialog implements
- InternetDialogController.InternetDialogCallback, Window.Callback {
+public class InternetDialogDelegate implements
+ SystemUIDialog.Delegate,
+ InternetDialogController.InternetDialogCallback {
private static final String TAG = "InternetDialog";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- static final long PROGRESS_DELAY_MS = 1500L;
+ private static final String ABOVE_STATUS_BAR = "above_status_bar";
+ private static final String CAN_CONFIG_MOBILE_DATA = "can_config_mobile_data";
+ private static final String CAN_CONFIG_WIFI = "can_config_wifi";
+
static final int MAX_NETWORK_COUNT = 4;
private final Handler mHandler;
private final Executor mBackgroundExecutor;
private final DialogTransitionAnimator mDialogTransitionAnimator;
+ private final boolean mAboveStatusBar;
+ private final SystemUIDialog.Factory mSystemUIDialogFactory;
@VisibleForTesting
protected InternetAdapter mAdapter;
@@ -97,19 +103,16 @@
@VisibleForTesting
protected boolean mCanConfigWifi;
- private InternetDialogFactory mInternetDialogFactory;
- private SubscriptionManager mSubscriptionManager;
+ private final InternetDialogManager mInternetDialogManager;
private TelephonyManager mTelephonyManager;
@Nullable
private AlertDialog mAlertDialog;
- private UiEventLogger mUiEventLogger;
- private Context mContext;
- private InternetDialogController mInternetDialogController;
+ private final UiEventLogger mUiEventLogger;
+ private final InternetDialogController mInternetDialogController;
private TextView mInternetDialogTitle;
private TextView mInternetDialogSubTitle;
private View mDivider;
private ProgressBar mProgressBar;
- private LinearLayout mInternetDialogLayout;
private LinearLayout mConnectedWifListLayout;
private LinearLayout mMobileNetworkLayout;
private LinearLayout mSecondaryMobileNetworkLayout;
@@ -127,8 +130,6 @@
private ImageView mSignalIcon;
private TextView mMobileTitleText;
private TextView mMobileSummaryText;
- private TextView mSecondaryMobileTitleText;
- private TextView mSecondaryMobileSummaryText;
private TextView mAirplaneModeSummaryText;
private Switch mMobileDataToggle;
private View mMobileToggleDivider;
@@ -139,12 +140,12 @@
protected Button mShareWifiButton;
private Button mAirplaneModeButton;
private Drawable mBackgroundOn;
- private KeyguardStateController mKeyguard;
+ private final KeyguardStateController mKeyguard;
@Nullable
private Drawable mBackgroundOff = null;
- private int mDefaultDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
- private boolean mCanConfigMobileData;
- private boolean mCanChangeWifiState;
+ private int mDefaultDataSubId;
+ private final boolean mCanConfigMobileData;
+ private final boolean mCanChangeWifiState;
// Wi-Fi entries
private int mWifiNetworkHeight;
@Nullable
@@ -157,26 +158,41 @@
// Wi-Fi scanning progress bar
protected boolean mIsProgressBarVisible;
+ private SystemUIDialog mDialog;
- @Inject
- public InternetDialog(Context context, InternetDialogFactory internetDialogFactory,
- InternetDialogController internetDialogController, boolean canConfigMobileData,
- boolean canConfigWifi, boolean aboveStatusBar, UiEventLogger uiEventLogger,
+ @AssistedFactory
+ public interface Factory {
+ InternetDialogDelegate create(
+ @Assisted(ABOVE_STATUS_BAR) boolean aboveStatusBar,
+ @Assisted(CAN_CONFIG_MOBILE_DATA) boolean canConfigMobileData,
+ @Assisted(CAN_CONFIG_WIFI) boolean canConfigWifi);
+ }
+
+ @AssistedInject
+ public InternetDialogDelegate(
+ Context context,
+ InternetDialogManager internetDialogManager,
+ InternetDialogController internetDialogController,
+ @Assisted(ABOVE_STATUS_BAR) boolean canConfigMobileData,
+ @Assisted(CAN_CONFIG_MOBILE_DATA) boolean canConfigWifi,
+ @Assisted(CAN_CONFIG_WIFI) boolean aboveStatusBar,
+ UiEventLogger uiEventLogger,
DialogTransitionAnimator dialogTransitionAnimator,
- @Main Handler handler, @Background Executor executor,
- KeyguardStateController keyguardStateController) {
- super(context);
+ @Main Handler handler,
+ @Background Executor executor,
+ KeyguardStateController keyguardStateController,
+ SystemUIDialog.Factory systemUIDialogFactory) {
+ mAboveStatusBar = aboveStatusBar;
+ mSystemUIDialogFactory = systemUIDialogFactory;
if (DEBUG) {
Log.d(TAG, "Init InternetDialog");
}
// Save the context that is wrapped with our theme.
- mContext = getContext();
mHandler = handler;
mBackgroundExecutor = executor;
- mInternetDialogFactory = internetDialogFactory;
+ mInternetDialogManager = internetDialogManager;
mInternetDialogController = internetDialogController;
- mSubscriptionManager = mInternetDialogController.getSubscriptionManager();
mDefaultDataSubId = mInternetDialogController.getDefaultDataSubscriptionId();
mTelephonyManager = mInternetDialogController.getTelephonyManager();
mCanConfigMobileData = canConfigMobileData;
@@ -187,31 +203,42 @@
mUiEventLogger = uiEventLogger;
mDialogTransitionAnimator = dialogTransitionAnimator;
mAdapter = new InternetAdapter(mInternetDialogController);
- if (!aboveStatusBar) {
- getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
- }
}
@Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
+ public SystemUIDialog createDialog() {
+ SystemUIDialog dialog = mSystemUIDialogFactory.create(this);
+ if (!mAboveStatusBar) {
+ dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
+ }
+
+ if (mDialog != null) {
+ mDialog.dismiss();
+ }
+ mDialog = dialog;
+
+ return dialog;
+ }
+
+ @Override
+ public void onCreate(SystemUIDialog dialog, Bundle savedInstanceState) {
if (DEBUG) {
Log.d(TAG, "onCreate");
}
+ Context context = dialog.getContext();
mUiEventLogger.log(InternetDialogEvent.INTERNET_DIALOG_SHOW);
- mDialogView = LayoutInflater.from(mContext).inflate(R.layout.internet_connectivity_dialog,
- null);
+ mDialogView = LayoutInflater.from(context).inflate(
+ R.layout.internet_connectivity_dialog, null);
mDialogView.setAccessibilityPaneTitle(
- mContext.getText(R.string.accessibility_desc_quick_settings));
- final Window window = getWindow();
+ context.getText(R.string.accessibility_desc_quick_settings));
+ final Window window = dialog.getWindow();
window.setContentView(mDialogView);
window.setWindowAnimations(R.style.Animation_InternetDialog);
- mWifiNetworkHeight = mContext.getResources()
+ mWifiNetworkHeight = context.getResources()
.getDimensionPixelSize(R.dimen.internet_dialog_wifi_network_height);
- mInternetDialogLayout = mDialogView.requireViewById(R.id.internet_connectivity_dialog);
mInternetDialogTitle = mDialogView.requireViewById(R.id.internet_dialog_title);
mInternetDialogSubTitle = mDialogView.requireViewById(R.id.internet_dialog_subtitle);
mDivider = mDialogView.requireViewById(R.id.divider);
@@ -239,20 +266,20 @@
mMobileToggleDivider = mDialogView.requireViewById(R.id.mobile_toggle_divider);
mMobileDataToggle = mDialogView.requireViewById(R.id.mobile_toggle);
mWiFiToggle = mDialogView.requireViewById(R.id.wifi_toggle);
- mBackgroundOn = mContext.getDrawable(R.drawable.settingslib_switch_bar_bg_on);
+ mBackgroundOn = context.getDrawable(R.drawable.settingslib_switch_bar_bg_on);
mInternetDialogTitle.setText(getDialogTitleText());
mInternetDialogTitle.setGravity(Gravity.START | Gravity.CENTER_VERTICAL);
- mBackgroundOff = mContext.getDrawable(R.drawable.internet_dialog_selected_effect);
- setOnClickListener();
+ mBackgroundOff = context.getDrawable(R.drawable.internet_dialog_selected_effect);
+ setOnClickListener(dialog);
mTurnWifiOnLayout.setBackground(null);
mAirplaneModeButton.setVisibility(
mInternetDialogController.isAirplaneModeEnabled() ? View.VISIBLE : View.GONE);
- mWifiRecyclerView.setLayoutManager(new LinearLayoutManager(mContext));
+ mWifiRecyclerView.setLayoutManager(new LinearLayoutManager(context));
mWifiRecyclerView.setAdapter(mAdapter);
}
@Override
- public void start() {
+ public void onStart(SystemUIDialog dialog) {
if (DEBUG) {
Log.d(TAG, "onStart");
}
@@ -273,7 +300,7 @@
}
@Override
- public void stop() {
+ public void onStop(SystemUIDialog dialog) {
if (DEBUG) {
Log.d(TAG, "onStop");
}
@@ -288,7 +315,7 @@
mShareWifiButton.setOnClickListener(null);
mAirplaneModeButton.setOnClickListener(null);
mInternetDialogController.onStop();
- mInternetDialogFactory.destroyDialog();
+ mInternetDialogManager.destroyDialog();
}
@Override
@@ -296,8 +323,11 @@
if (DEBUG) {
Log.d(TAG, "dismissDialog");
}
- mInternetDialogFactory.destroyDialog();
- dismiss();
+ mInternetDialogManager.destroyDialog();
+ if (mDialog != null) {
+ mDialog.dismiss();
+ mDialog = null;
+ }
}
/**
@@ -334,11 +364,11 @@
updateWifiScanNotify(isWifiEnabled, isWifiScanEnabled, isDeviceLocked);
}
- private void setOnClickListener() {
+ private void setOnClickListener(SystemUIDialog dialog) {
mMobileNetworkLayout.setOnClickListener(v -> {
int autoSwitchNonDdsSubId = mInternetDialogController.getActiveAutoSwitchNonDdsSubId();
if (autoSwitchNonDdsSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
- showTurnOffAutoDataSwitchDialog(autoSwitchNonDdsSubId);
+ showTurnOffAutoDataSwitchDialog(dialog, autoSwitchNonDdsSubId);
}
mInternetDialogController.connectCarrierNetwork();
});
@@ -346,10 +376,10 @@
boolean isChecked = mMobileDataToggle.isChecked();
if (!isChecked && shouldShowMobileDialog()) {
mMobileDataToggle.setChecked(true);
- showTurnOffMobileDialog();
+ showTurnOffMobileDialog(dialog);
} else if (mInternetDialogController.isMobileDataEnabled() != isChecked) {
- mInternetDialogController.setMobileDataEnabled(mContext, mDefaultDataSubId,
- isChecked, false);
+ mInternetDialogController.setMobileDataEnabled(
+ dialog.getContext(), mDefaultDataSubId, isChecked, false);
}
});
mConnectedWifListLayout.setOnClickListener(this::onClickConnectedWifi);
@@ -359,7 +389,7 @@
if (mInternetDialogController.isWifiEnabled() == isChecked) return;
mInternetDialogController.setWifiEnabled(isChecked);
});
- mDoneButton.setOnClickListener(v -> dismiss());
+ mDoneButton.setOnClickListener(v -> dialog.dismiss());
mShareWifiButton.setOnClickListener(v -> {
if (mInternetDialogController.mayLaunchShareWifiSettings(mConnectedWifiEntry)) {
mUiEventLogger.log(InternetDialogEvent.SHARE_WIFI_QS_BUTTON_CLICKED);
@@ -378,6 +408,14 @@
private void setMobileDataLayout(boolean activeNetworkIsCellular,
boolean isCarrierNetworkActive) {
+
+ if (mDialog != null) {
+ setMobileDataLayout(mDialog, activeNetworkIsCellular, isCarrierNetworkActive);
+ }
+ }
+
+ private void setMobileDataLayout(SystemUIDialog dialog, boolean activeNetworkIsCellular,
+ boolean isCarrierNetworkActive) {
boolean isNetworkConnected = activeNetworkIsCellular || isCarrierNetworkActive;
// 1. Mobile network should be gone if airplane mode ON or the list of active
// subscriptionId is null.
@@ -431,19 +469,19 @@
if (stub != null) {
stub.inflate();
}
- mSecondaryMobileNetworkLayout = findViewById(R.id.secondary_mobile_network_layout);
+ mSecondaryMobileNetworkLayout = dialog.findViewById(
+ R.id.secondary_mobile_network_layout);
mSecondaryMobileNetworkLayout.setOnClickListener(
this::onClickConnectedSecondarySub);
mSecondaryMobileNetworkLayout.setBackground(mBackgroundOn);
- mSecondaryMobileTitleText = mDialogView.requireViewById(
+ TextView mSecondaryMobileTitleText = mDialogView.requireViewById(
R.id.secondary_mobile_title);
mSecondaryMobileTitleText.setText(getMobileNetworkTitle(autoSwitchNonDdsSubId));
mSecondaryMobileTitleText.setTextAppearance(
R.style.TextAppearance_InternetDialog_Active);
- mSecondaryMobileSummaryText =
- mDialogView.requireViewById(R.id.secondary_mobile_summary);
+ TextView mSecondaryMobileSummaryText = mDialogView.requireViewById(R.id.secondary_mobile_summary);
summary = getMobileNetworkSummary(autoSwitchNonDdsSubId);
if (!TextUtils.isEmpty(summary)) {
mSecondaryMobileSummaryText.setText(
@@ -465,7 +503,7 @@
ImageView mSecondaryMobileSettingsIcon =
mDialogView.requireViewById(R.id.secondary_settings_icon);
mSecondaryMobileSettingsIcon.setColorFilter(
- mContext.getColor(R.color.connected_network_primary_color));
+ dialog.getContext().getColor(R.color.connected_network_primary_color));
// set secondary visual for default data sub
mMobileNetworkLayout.setBackground(mBackgroundOff);
@@ -473,7 +511,7 @@
mMobileSummaryText.setTextAppearance(
R.style.TextAppearance_InternetDialog_Secondary);
mSignalIcon.setColorFilter(
- mContext.getColor(R.color.connected_network_secondary_color));
+ dialog.getContext().getColor(R.color.connected_network_secondary_color));
} else {
mMobileNetworkLayout.setBackground(
isNetworkConnected ? mBackgroundOn : mBackgroundOff);
@@ -491,7 +529,8 @@
// Set airplane mode to the summary for carrier network
if (mInternetDialogController.isAirplaneModeEnabled()) {
mAirplaneModeSummaryText.setVisibility(View.VISIBLE);
- mAirplaneModeSummaryText.setText(mContext.getText(R.string.airplane_mode));
+ mAirplaneModeSummaryText.setText(
+ dialog.getContext().getText(R.string.airplane_mode));
mAirplaneModeSummaryText.setTextAppearance(secondaryRes);
} else {
mAirplaneModeSummaryText.setVisibility(View.GONE);
@@ -523,7 +562,7 @@
@MainThread
private void updateConnectedWifi(boolean isWifiEnabled, boolean isDeviceLocked) {
- if (!isWifiEnabled || mConnectedWifiEntry == null || isDeviceLocked) {
+ if (mDialog == null || !isWifiEnabled || mConnectedWifiEntry == null || isDeviceLocked) {
mConnectedWifListLayout.setVisibility(View.GONE);
mShareWifiButton.setVisibility(View.GONE);
return;
@@ -534,7 +573,7 @@
mConnectedWifiIcon.setImageDrawable(
mInternetDialogController.getInternetWifiDrawable(mConnectedWifiEntry));
mWifiSettingsIcon.setColorFilter(
- mContext.getColor(R.color.connected_network_primary_color));
+ mDialog.getContext().getColor(R.color.connected_network_primary_color));
if (mInternetDialogController.getConfiguratorQrCodeGeneratorIntentOrNull(
mConnectedWifiEntry) != null) {
mShareWifiButton.setVisibility(View.VISIBLE);
@@ -593,7 +632,7 @@
@MainThread
private void updateWifiScanNotify(boolean isWifiEnabled, boolean isWifiScanEnabled,
boolean isDeviceLocked) {
- if (isWifiEnabled || !isWifiScanEnabled || isDeviceLocked) {
+ if (mDialog == null || isWifiEnabled || !isWifiScanEnabled || isDeviceLocked) {
mWifiScanNotifyLayout.setVisibility(View.GONE);
return;
}
@@ -602,7 +641,7 @@
AnnotationLinkSpan.LinkInfo.DEFAULT_ANNOTATION,
mInternetDialogController::launchWifiScanningSetting);
mWifiScanNotifyText.setText(AnnotationLinkSpan.linkify(
- getContext().getText(R.string.wifi_scan_notify_message), linkInfo));
+ mDialog.getContext().getText(R.string.wifi_scan_notify_message), linkInfo));
mWifiScanNotifyText.setMovementMethod(LinkMovementMethod.getInstance());
}
mWifiScanNotifyLayout.setVisibility(View.VISIBLE);
@@ -657,7 +696,10 @@
}
private boolean shouldShowMobileDialog() {
- boolean flag = Prefs.getBoolean(mContext, QS_HAS_TURNED_OFF_MOBILE_DATA,
+ if (mDialog == null) {
+ return false;
+ }
+ boolean flag = Prefs.getBoolean(mDialog.getContext(), QS_HAS_TURNED_OFF_MOBILE_DATA,
false);
if (mInternetDialogController.isMobileDataEnabled() && !flag) {
return true;
@@ -665,40 +707,42 @@
return false;
}
- private void showTurnOffMobileDialog() {
+ private void showTurnOffMobileDialog(SystemUIDialog dialog) {
+ Context context = dialog.getContext();
CharSequence carrierName = getMobileNetworkTitle(mDefaultDataSubId);
boolean isInService = mInternetDialogController.isVoiceStateInService(mDefaultDataSubId);
if (TextUtils.isEmpty(carrierName) || !isInService) {
- carrierName = mContext.getString(R.string.mobile_data_disable_message_default_carrier);
+ carrierName = context.getString(R.string.mobile_data_disable_message_default_carrier);
}
- mAlertDialog = new Builder(mContext)
+ mAlertDialog = new AlertDialog.Builder(context)
.setTitle(R.string.mobile_data_disable_title)
- .setMessage(mContext.getString(R.string.mobile_data_disable_message, carrierName))
+ .setMessage(context.getString(R.string.mobile_data_disable_message, carrierName))
.setNegativeButton(android.R.string.cancel, (d, w) -> {
})
.setPositiveButton(
com.android.internal.R.string.alert_windows_notification_turn_off_action,
(d, w) -> {
- mInternetDialogController.setMobileDataEnabled(mContext,
+ mInternetDialogController.setMobileDataEnabled(context,
mDefaultDataSubId, false, false);
mMobileDataToggle.setChecked(false);
- Prefs.putBoolean(mContext, QS_HAS_TURNED_OFF_MOBILE_DATA, true);
+ Prefs.putBoolean(context, QS_HAS_TURNED_OFF_MOBILE_DATA, true);
})
.create();
mAlertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
SystemUIDialog.setShowForAllUsers(mAlertDialog, true);
SystemUIDialog.registerDismissListener(mAlertDialog);
SystemUIDialog.setWindowOnTop(mAlertDialog, mKeyguard.isShowing());
- mDialogTransitionAnimator.showFromDialog(mAlertDialog, this, null, false);
+ mDialogTransitionAnimator.showFromDialog(mAlertDialog, dialog, null, false);
}
- private void showTurnOffAutoDataSwitchDialog(int subId) {
+ private void showTurnOffAutoDataSwitchDialog(SystemUIDialog dialog, int subId) {
+ Context context = dialog.getContext();
CharSequence carrierName = getMobileNetworkTitle(mDefaultDataSubId);
if (TextUtils.isEmpty(carrierName)) {
- carrierName = mContext.getString(R.string.mobile_data_disable_message_default_carrier);
+ carrierName = context.getString(R.string.mobile_data_disable_message_default_carrier);
}
- mAlertDialog = new Builder(mContext)
- .setTitle(mContext.getString(R.string.auto_data_switch_disable_title, carrierName))
+ mAlertDialog = new AlertDialog.Builder(context)
+ .setTitle(context.getString(R.string.auto_data_switch_disable_title, carrierName))
.setMessage(R.string.auto_data_switch_disable_message)
.setNegativeButton(R.string.auto_data_switch_dialog_negative_button,
(d, w) -> {
@@ -716,7 +760,7 @@
SystemUIDialog.setShowForAllUsers(mAlertDialog, true);
SystemUIDialog.registerDismissListener(mAlertDialog);
SystemUIDialog.setWindowOnTop(mAlertDialog, mKeyguard.isShowing());
- mDialogTransitionAnimator.showFromDialog(mAlertDialog, this, null, false);
+ mDialogTransitionAnimator.showFromDialog(mAlertDialog, dialog, null, false);
}
@Override
@@ -802,11 +846,10 @@
}
@Override
- public void onWindowFocusChanged(boolean hasFocus) {
- super.onWindowFocusChanged(hasFocus);
+ public void onWindowFocusChanged(SystemUIDialog dialog, boolean hasFocus) {
if (mAlertDialog != null && !mAlertDialog.isShowing()) {
- if (!hasFocus && isShowing()) {
- dismiss();
+ if (!hasFocus && dialog.isShowing()) {
+ dialog.dismiss();
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogManager.kt
similarity index 60%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogFactory.kt
rename to packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogManager.kt
index c5f8983..2a177c7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogManager.kt
@@ -15,64 +15,49 @@
*/
package com.android.systemui.qs.tiles.dialog
-import android.content.Context
-import android.os.Handler
import android.util.Log
import android.view.View
import com.android.internal.jank.InteractionJankMonitor
-import com.android.internal.logging.UiEventLogger
import com.android.systemui.animation.DialogCuj
import com.android.systemui.animation.DialogTransitionAnimator
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.statusbar.policy.KeyguardStateController
-import java.util.concurrent.Executor
+import com.android.systemui.statusbar.phone.SystemUIDialog
import javax.inject.Inject
private const val TAG = "InternetDialogFactory"
private val DEBUG = Log.isLoggable(TAG, Log.DEBUG)
/**
- * Factory to create [InternetDialog] objects.
+ * Factory to create [InternetDialogDelegate] objects.
*/
@SysUISingleton
-class InternetDialogFactory @Inject constructor(
- @Main private val handler: Handler,
- @Background private val executor: Executor,
- private val internetDialogController: InternetDialogController,
- private val context: Context,
- private val uiEventLogger: UiEventLogger,
+class InternetDialogManager @Inject constructor(
private val dialogTransitionAnimator: DialogTransitionAnimator,
- private val keyguardStateController: KeyguardStateController
+ private val dialogFactory: InternetDialogDelegate.Factory
) {
companion object {
private const val INTERACTION_JANK_TAG = "internet"
- var internetDialog: InternetDialog? = null
+ var dialog: SystemUIDialog? = null
}
- /** Creates a [InternetDialog]. The dialog will be animated from [view] if it is not null. */
+ /** Creates a [InternetDialogDelegate]. The dialog will be animated from [view] if it is not null. */
fun create(
aboveStatusBar: Boolean,
canConfigMobileData: Boolean,
canConfigWifi: Boolean,
view: View?
) {
- if (internetDialog != null) {
+ if (dialog != null) {
if (DEBUG) {
Log.d(TAG, "InternetDialog is showing, do not create it twice.")
}
return
} else {
- internetDialog = InternetDialog(
- context, this, internetDialogController,
- canConfigMobileData, canConfigWifi, aboveStatusBar, uiEventLogger,
- dialogTransitionAnimator, handler,
- executor, keyguardStateController
- )
+ dialog = dialogFactory.create(
+ aboveStatusBar, canConfigMobileData, canConfigWifi).createDialog()
if (view != null) {
dialogTransitionAnimator.showFromView(
- internetDialog!!, view,
+ dialog!!, view,
animateBackgroundBoundsChange = true,
cuj = DialogCuj(
InteractionJankMonitor.CUJ_SHADE_DIALOG_OPEN,
@@ -80,7 +65,7 @@
)
)
} else {
- internetDialog?.show()
+ dialog!!.show()
}
}
}
@@ -89,6 +74,6 @@
if (DEBUG) {
Log.d(TAG, "destroyDialog")
}
- internetDialog = null
+ dialog = null
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 40f0132..95d9bc4 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -122,6 +122,7 @@
import com.android.systemui.dagger.qualifiers.DisplayId;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor;
+import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor;
import com.android.systemui.doze.DozeLog;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.dump.DumpsysTableLogger;
@@ -1825,7 +1826,14 @@
/** Returns space between top of lock icon and bottom of NotificationStackScrollLayout. */
private float getLockIconPadding() {
float lockIconPadding = 0f;
- if (mLockIconViewController.getTop() != 0f) {
+ if (DeviceEntryUdfpsRefactor.isEnabled()) {
+ View deviceEntryIconView = mKeyguardViewConfigurator.getKeyguardRootView()
+ .findViewById(R.id.device_entry_icon_view);
+ if (deviceEntryIconView != null) {
+ lockIconPadding = mNotificationStackScrollLayoutController.getBottom()
+ - deviceEntryIconView.getTop();
+ }
+ } else if (mLockIconViewController.getTop() != 0f) {
lockIconPadding = mNotificationStackScrollLayoutController.getBottom()
- mLockIconViewController.getTop();
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
index 457b3d7..29de688 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt
@@ -27,7 +27,7 @@
import androidx.constraintlayout.widget.ConstraintSet.START
import androidx.constraintlayout.widget.ConstraintSet.TOP
import androidx.lifecycle.lifecycleScope
-import com.android.systemui.Flags.centralizedStatusBarDimensRefactor
+import com.android.systemui.Flags.centralizedStatusBarHeightFix
import com.android.systemui.Flags.migrateClocksToBlueprint
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
@@ -183,7 +183,7 @@
}
private fun calculateLargeShadeHeaderHeight(): Int {
- return if (centralizedStatusBarDimensRefactor()) {
+ return if (centralizedStatusBarHeightFix()) {
largeScreenHeaderHelperLazy.get().getLargeScreenHeaderHeight()
} else {
resources.getDimensionPixelSize(R.dimen.large_screen_shade_header_height)
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
index f86c71b..f7b9e4e 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
@@ -20,7 +20,7 @@
import static android.view.WindowInsets.Type.ime;
import static com.android.internal.jank.InteractionJankMonitor.CUJ_NOTIFICATION_SHADE_QS_EXPAND_COLLAPSE;
-import static com.android.systemui.Flags.centralizedStatusBarDimensRefactor;
+import static com.android.systemui.Flags.centralizedStatusBarHeightFix;
import static com.android.systemui.Flags.migrateClocksToBlueprint;
import static com.android.systemui.classifier.Classifier.QS_COLLAPSE;
import static com.android.systemui.shade.NotificationPanelViewController.COUNTER_PANEL_OPEN_QS;
@@ -453,7 +453,7 @@
mUseLargeScreenShadeHeader =
LargeScreenUtils.shouldUseLargeScreenShadeHeader(mPanelView.getResources());
mLargeScreenShadeHeaderHeight =
- centralizedStatusBarDimensRefactor()
+ centralizedStatusBarHeightFix()
? mLargeScreenHeaderHelperLazy.get().getLargeScreenHeaderHeight()
: mResources.getDimensionPixelSize(
R.dimen.large_screen_shade_header_height);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
index a66bacd..df9c57c 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
@@ -38,7 +38,7 @@
import com.android.app.animation.Interpolators
import com.android.settingslib.Utils
import com.android.systemui.Dumpable
-import com.android.systemui.Flags.centralizedStatusBarDimensRefactor
+import com.android.systemui.Flags.centralizedStatusBarHeightFix
import com.android.systemui.animation.ShadeInterpolation
import com.android.systemui.battery.BatteryMeterView
import com.android.systemui.battery.BatteryMeterViewController
@@ -433,7 +433,7 @@
changes += combinedShadeHeadersConstraintManager.emptyCutoutConstraints()
}
- if (centralizedStatusBarDimensRefactor()) {
+ if (centralizedStatusBarHeightFix()) {
view.setPadding(view.paddingLeft, sbInsets.top, view.paddingRight, view.paddingBottom)
}
view.updateAllConstraints(changes)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index ffb11dd..ca19f71 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -177,6 +177,7 @@
private static final int MSG_CONFIRM_IMMERSIVE_PROMPT = 77 << MSG_SHIFT;
private static final int MSG_IMMERSIVE_CHANGED = 78 << MSG_SHIFT;
private static final int MSG_SET_QS_TILES = 79 << MSG_SHIFT;
+ private static final int MSG_ENTER_DESKTOP = 80 << MSG_SHIFT;
public static final int FLAG_EXCLUDE_NONE = 0;
public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
public static final int FLAG_EXCLUDE_RECENTS_PANEL = 1 << 1;
@@ -520,6 +521,11 @@
* @see IStatusBar#immersiveModeChanged
*/
default void immersiveModeChanged(int rootDisplayAreaId, boolean isImmersiveMode) {}
+
+ /**
+ * @see IStatusBar#enterDesktop(int)
+ */
+ default void enterDesktop(int displayId) {}
}
@VisibleForTesting
@@ -1420,6 +1426,13 @@
mHandler.obtainMessage(MSG_GO_TO_FULLSCREEN_FROM_SPLIT).sendToTarget();
}
+ @Override
+ public void enterDesktop(int displayId) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = displayId;
+ mHandler.obtainMessage(MSG_ENTER_DESKTOP, args).sendToTarget();
+ }
+
private final class H extends Handler {
private H(Looper l) {
super(l);
@@ -1914,6 +1927,13 @@
mCallbacks.get(i).immersiveModeChanged(rootDisplayAreaId, isImmersiveMode);
}
break;
+ case MSG_ENTER_DESKTOP:
+ args = (SomeArgs) msg.obj;
+ int displayId = args.argi1;
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ mCallbacks.get(i).enterDesktop(displayId);
+ }
+ break;
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index 9916ef6..1a06eec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -156,14 +156,12 @@
final String action = intent.getAction();
if (ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED.equals(action)) {
- if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
- mKeyguardAllowingNotifications =
- intent.getBooleanExtra(EXTRA_KM_PRIVATE_NOTIFS_ALLOWED, false);
- if (mCurrentUserId == getSendingUserId()) {
- boolean changed = updateLockscreenNotificationSetting();
- if (changed) {
- notifyNotificationStateChanged();
- }
+ mKeyguardAllowingNotifications =
+ intent.getBooleanExtra(EXTRA_KM_PRIVATE_NOTIFS_ALLOWED, false);
+ if (mCurrentUserId == getSendingUserId()) {
+ boolean changed = updateLockscreenNotificationSetting();
+ if (changed) {
+ notifyNotificationStateChanged();
}
}
}
@@ -176,36 +174,26 @@
final String action = intent.getAction();
if (ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals(action)) {
- if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
- boolean changed = false;
- int sendingUserId = getSendingUserId();
- if (sendingUserId == USER_ALL) {
- // When a Device Owner triggers changes it's sent as USER_ALL. Normalize
- // the user before calling into DPM
- sendingUserId = mCurrentUserId;
- @SuppressLint("MissingPermission")
- List<UserInfo> users = mUserManager.getUsers();
- for (int i = users.size() - 1; i >= 0; i--) {
- changed |= updateDpcSettings(users.get(i).id);
- }
- } else {
- changed |= updateDpcSettings(sendingUserId);
- }
-
- if (mCurrentUserId == sendingUserId) {
- changed |= updateLockscreenNotificationSetting();
- }
- if (changed) {
- notifyNotificationStateChanged();
+ boolean changed = false;
+ int sendingUserId = getSendingUserId();
+ if (sendingUserId == USER_ALL) {
+ // When a Device Owner triggers changes it's sent as USER_ALL. Normalize
+ // the user before calling into DPM
+ sendingUserId = mCurrentUserId;
+ @SuppressLint("MissingPermission")
+ List<UserInfo> users = mUserManager.getUsers();
+ for (int i = users.size() - 1; i >= 0; i--) {
+ changed |= updateDpcSettings(users.get(i).id);
}
} else {
- if (isCurrentProfile(getSendingUserId())) {
- mUsersAllowingPrivateNotifications.clear();
- updateLockscreenNotificationSetting();
- // TODO(b/231976036): Consolidate pipeline invalidations related to this
- // event
- // notifyNotificationStateChanged();
- }
+ changed |= updateDpcSettings(sendingUserId);
+ }
+
+ if (mCurrentUserId == sendingUserId) {
+ changed |= updateLockscreenNotificationSetting();
+ }
+ if (changed) {
+ notifyNotificationStateChanged();
}
}
}
@@ -225,12 +213,10 @@
updateCurrentProfilesCache();
} else if (Objects.equals(action, Intent.ACTION_USER_ADDED)){
updateCurrentProfilesCache();
- if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
- final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
- mBackgroundExecutor.execute(() -> {
- initValuesForUser(userId);
- });
- }
+ final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, USER_NULL);
+ mBackgroundExecutor.execute(() -> {
+ initValuesForUser(userId);
+ });
} else if (profileAvailabilityActions(action)) {
updateCurrentProfilesCache();
} else if (Objects.equals(action, Intent.ACTION_USER_UNLOCKED)) {
@@ -360,28 +346,16 @@
}
private void init() {
- mLockscreenSettingsObserver = new ExecutorContentObserver(
- mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)
- ? mBackgroundExecutor
- : mMainExecutor) {
+ mLockscreenSettingsObserver = new ExecutorContentObserver(mBackgroundExecutor) {
@Override
public void onChange(boolean selfChange, Collection<Uri> uris, int flags) {
- if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
- @SuppressLint("MissingPermission")
- List<UserInfo> users = mUserManager.getUsers();
- for (int i = users.size() - 1; i >= 0; i--) {
- onChange(selfChange, uris, flags,users.get(i).getUserHandle());
- }
- } else {
- // We don't know which user changed LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS or
- // LOCK_SCREEN_SHOW_NOTIFICATIONS, so we just dump our cache ...
- mUsersAllowingPrivateNotifications.clear();
- mUsersAllowingNotifications.clear();
- // ... and refresh all the notifications
- updateLockscreenNotificationSetting();
- notifyNotificationStateChanged();
+ @SuppressLint("MissingPermission")
+ List<UserInfo> users = mUserManager.getUsers();
+ for (int i = users.size() - 1; i >= 0; i--) {
+ onChange(selfChange, uris, flags,users.get(i).getUserHandle());
}
+
}
// Note: even though this is an override, this method is not called by the OS
@@ -390,22 +364,20 @@
@Override
public void onChange(boolean selfChange, Collection<Uri> uris,
int flags, UserHandle user) {
- if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
- boolean changed = false;
- for (Uri uri: uris) {
- if (SHOW_LOCKSCREEN.equals(uri)) {
- changed |= updateUserShowSettings(user.getIdentifier());
- } else if (SHOW_PRIVATE_LOCKSCREEN.equals(uri)) {
- changed |= updateUserShowPrivateSettings(user.getIdentifier());
- }
+ boolean changed = false;
+ for (Uri uri: uris) {
+ if (SHOW_LOCKSCREEN.equals(uri)) {
+ changed |= updateUserShowSettings(user.getIdentifier());
+ } else if (SHOW_PRIVATE_LOCKSCREEN.equals(uri)) {
+ changed |= updateUserShowPrivateSettings(user.getIdentifier());
}
+ }
- if (mCurrentUserId == user.getIdentifier()) {
- changed |= updateLockscreenNotificationSetting();
- }
- if (changed) {
- notifyNotificationStateChanged();
- }
+ if (mCurrentUserId == user.getIdentifier()) {
+ changed |= updateLockscreenNotificationSetting();
+ }
+ if (changed) {
+ notifyNotificationStateChanged();
}
}
};
@@ -432,16 +404,10 @@
mLockscreenSettingsObserver,
USER_ALL);
- if (!mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
- mContext.getContentResolver().registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.ZEN_MODE), false,
- mSettingsObserver);
- }
mBroadcastDispatcher.registerReceiver(mAllUsersReceiver,
new IntentFilter(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
- mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)
- ? mBackgroundExecutor : null, UserHandle.ALL);
+ mBackgroundExecutor, UserHandle.ALL);
if (keyguardPrivateNotifications()) {
mBroadcastDispatcher.registerReceiver(mKeyguardReceiver,
new IntentFilter(ACTION_KEYGUARD_PRIVATE_NOTIFICATIONS_CHANGED),
@@ -471,17 +437,13 @@
mCurrentUserId = mUserTracker.getUserId(); // in case we reg'd receiver too late
updateCurrentProfilesCache();
- if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
- // Set up
- mBackgroundExecutor.execute(() -> {
- @SuppressLint("MissingPermission") List<UserInfo> users = mUserManager.getUsers();
- for (int i = users.size() - 1; i >= 0; i--) {
- initValuesForUser(users.get(i).id);
- }
- });
- } else {
- mSettingsObserver.onChange(false); // set up
- }
+ // Set up
+ mBackgroundExecutor.execute(() -> {
+ @SuppressLint("MissingPermission") List<UserInfo> users = mUserManager.getUsers();
+ for (int i = users.size() - 1; i >= 0; i--) {
+ initValuesForUser(users.get(i).id);
+ }
+ });
}
private void initValuesForUser(@UserIdInt int userId) {
@@ -519,26 +481,15 @@
boolean show;
boolean allowedByDpm;
- if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
- if (keyguardPrivateNotifications()) {
- show = mUsersUsersAllowingNotifications.get(mCurrentUserId);
- } else {
- show = mUsersUsersAllowingNotifications.get(mCurrentUserId)
- && mKeyguardAllowingNotifications;
- }
- // If DPC never notified us about a user, that means they have no policy for the user,
- // and they allow the behavior
- allowedByDpm = mUsersDpcAllowingNotifications.get(mCurrentUserId, true);
+ if (keyguardPrivateNotifications()) {
+ show = mUsersUsersAllowingNotifications.get(mCurrentUserId);
} else {
- show = mSecureSettings.getIntForUser(
- LOCK_SCREEN_SHOW_NOTIFICATIONS,
- 1,
- mCurrentUserId) != 0;
- final int dpmFlags = mDevicePolicyManager.getKeyguardDisabledFeatures(
- null /* admin */, mCurrentUserId);
- allowedByDpm = (dpmFlags
- & KEYGUARD_DISABLE_SECURE_NOTIFICATIONS) == 0;
+ show = mUsersUsersAllowingNotifications.get(mCurrentUserId)
+ && mKeyguardAllowingNotifications;
}
+ // If DPC never notified us about a user, that means they have no policy for the user,
+ // and they allow the behavior
+ allowedByDpm = mUsersDpcAllowingNotifications.get(mCurrentUserId, true);
final boolean oldValue = mShowLockscreenNotifications;
setShowLockscreenNotifications(show && allowedByDpm);
@@ -600,42 +551,24 @@
* when the lockscreen is in "public" (secure & locked) mode?
*/
public boolean userAllowsPrivateNotificationsInPublic(int userHandle) {
- if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
- if (userHandle == USER_ALL) {
- userHandle = mCurrentUserId;
- }
- if (mUsersUsersAllowingPrivateNotifications.indexOfKey(userHandle) < 0) {
- Log.i(TAG, "Asking for redact notifs setting too early", new Throwable());
- return false;
- }
- if (mUsersDpcAllowingPrivateNotifications.indexOfKey(userHandle) < 0) {
- Log.i(TAG, "Asking for redact notifs dpm override too early", new Throwable());
- return false;
- }
- if (keyguardPrivateNotifications()) {
- return mUsersUsersAllowingPrivateNotifications.get(userHandle)
- && mUsersDpcAllowingPrivateNotifications.get(userHandle)
- && mKeyguardAllowingNotifications;
- } else {
- return mUsersUsersAllowingPrivateNotifications.get(userHandle)
- && mUsersDpcAllowingPrivateNotifications.get(userHandle);
- }
+ if (userHandle == USER_ALL) {
+ userHandle = mCurrentUserId;
+ }
+ if (mUsersUsersAllowingPrivateNotifications.indexOfKey(userHandle) < 0) {
+ Log.i(TAG, "Asking for redact notifs setting too early", new Throwable());
+ return false;
+ }
+ if (mUsersDpcAllowingPrivateNotifications.indexOfKey(userHandle) < 0) {
+ Log.i(TAG, "Asking for redact notifs dpm override too early", new Throwable());
+ return false;
+ }
+ if (keyguardPrivateNotifications()) {
+ return mUsersUsersAllowingPrivateNotifications.get(userHandle)
+ && mUsersDpcAllowingPrivateNotifications.get(userHandle)
+ && mKeyguardAllowingNotifications;
} else {
- if (userHandle == USER_ALL) {
- return true;
- }
-
- if (mUsersAllowingPrivateNotifications.indexOfKey(userHandle) < 0) {
- final boolean allowedByUser = 0 != mSecureSettings.getIntForUser(
- LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, userHandle);
- final boolean allowedByDpm = adminAllowsKeyguardFeature(userHandle,
- KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS);
- final boolean allowed = allowedByUser && allowedByDpm;
- mUsersAllowingPrivateNotifications.append(userHandle, allowed);
- return allowed;
- }
-
- return mUsersAllowingPrivateNotifications.get(userHandle);
+ return mUsersUsersAllowingPrivateNotifications.get(userHandle)
+ && mUsersDpcAllowingPrivateNotifications.get(userHandle);
}
}
@@ -688,48 +621,30 @@
* "public" (secure & locked) mode?
*/
public boolean userAllowsNotificationsInPublic(int userHandle) {
- if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
- // Unlike 'show private', settings does not show a copy of this setting for each
- // profile, so it inherits from the parent user.
- if (userHandle == USER_ALL || mCurrentManagedProfiles.contains(userHandle)) {
- userHandle = mCurrentUserId;
- }
- if (mUsersUsersAllowingNotifications.indexOfKey(userHandle) < 0) {
- // TODO(b/301955929): STOP_SHIP (stop flag flip): remove this read and use a safe
- // default value before moving to 'released'
- Log.wtf(TAG, "Asking for show notifs setting too early", new Throwable());
- updateUserShowSettings(userHandle);
- }
- if (mUsersDpcAllowingNotifications.indexOfKey(userHandle) < 0) {
- // TODO(b/301955929): STOP_SHIP (stop flag flip): remove this read and use a safe
- // default value before moving to 'released'
- Log.wtf(TAG, "Asking for show notifs dpm override too early", new Throwable());
- updateDpcSettings(userHandle);
- }
- if (keyguardPrivateNotifications()) {
- return mUsersUsersAllowingNotifications.get(userHandle)
- && mUsersDpcAllowingNotifications.get(userHandle);
- } else {
- return mUsersUsersAllowingNotifications.get(userHandle)
- && mUsersDpcAllowingNotifications.get(userHandle)
- && mKeyguardAllowingNotifications;
- }
+ // Unlike 'show private', settings does not show a copy of this setting for each
+ // profile, so it inherits from the parent user.
+ if (userHandle == USER_ALL || mCurrentManagedProfiles.contains(userHandle)) {
+ userHandle = mCurrentUserId;
+ }
+ if (mUsersUsersAllowingNotifications.indexOfKey(userHandle) < 0) {
+ // TODO(b/301955929): STOP_SHIP (stop flag flip): remove this read and use a safe
+ // default value before moving to 'released'
+ Log.wtf(TAG, "Asking for show notifs setting too early", new Throwable());
+ updateUserShowSettings(userHandle);
+ }
+ if (mUsersDpcAllowingNotifications.indexOfKey(userHandle) < 0) {
+ // TODO(b/301955929): STOP_SHIP (stop flag flip): remove this read and use a safe
+ // default value before moving to 'released'
+ Log.wtf(TAG, "Asking for show notifs dpm override too early", new Throwable());
+ updateDpcSettings(userHandle);
+ }
+ if (keyguardPrivateNotifications()) {
+ return mUsersUsersAllowingNotifications.get(userHandle)
+ && mUsersDpcAllowingNotifications.get(userHandle);
} else {
- if (isCurrentProfile(userHandle) && userHandle != mCurrentUserId) {
- return true;
- }
-
- if (mUsersAllowingNotifications.indexOfKey(userHandle) < 0) {
- final boolean allowedByUser = 0 != mSecureSettings.getIntForUser(
- LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, userHandle);
- final boolean allowedByDpm = adminAllowsKeyguardFeature(userHandle,
- KEYGUARD_DISABLE_SECURE_NOTIFICATIONS);
- final boolean allowedBySystem = mKeyguardManager.getPrivateNotificationsAllowed();
- final boolean allowed = allowedByUser && allowedByDpm && allowedBySystem;
- mUsersAllowingNotifications.append(userHandle, allowed);
- return allowed;
- }
- return mUsersAllowingNotifications.get(userHandle);
+ return mUsersUsersAllowingNotifications.get(userHandle)
+ && mUsersDpcAllowingNotifications.get(userHandle)
+ && mKeyguardAllowingNotifications;
}
}
@@ -766,13 +681,7 @@
return true;
}
NotificationEntry entry = mCommonNotifCollectionLazy.get().getEntry(key);
- if (mFeatureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
- return entry != null && entry.isChannelVisibilityPrivate();
- } else {
- return entry != null
- && entry.getRanking().getLockscreenVisibilityOverride()
- == Notification.VISIBILITY_PRIVATE;
- }
+ return entry != null && entry.isChannelVisibilityPrivate();
}
@SuppressLint("MissingPermission")
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 0e0f152..6155348 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar;
import static com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerProgress;
+import static com.android.systemui.util.ColorUtilKt.hexColorString;
import android.content.Context;
import android.content.res.Configuration;
@@ -42,6 +43,7 @@
import com.android.systemui.flags.RefactorFlag;
import com.android.systemui.res.R;
import com.android.systemui.shade.transition.LargeScreenShadeInterpolator;
+import com.android.systemui.statusbar.notification.ColorUpdateLogger;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.SourceType;
import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
@@ -187,8 +189,8 @@
@Override
public String toString() {
- return "NotificationShelf"
- + "(hideBackground=" + mHideBackground
+ return super.toString()
+ + " (hideBackground=" + mHideBackground
+ " notGoneIndex=" + mNotGoneIndex
+ " hasItemsInStableShelf=" + mHasItemsInStableShelf
+ " interactive=" + mInteractive
@@ -368,6 +370,17 @@
&& isYInView(localY, slop, top, bottom);
}
+ @Override
+ public void updateBackgroundColors() {
+ super.updateBackgroundColors();
+ ColorUpdateLogger colorUpdateLogger = ColorUpdateLogger.getInstance();
+ if (colorUpdateLogger != null) {
+ colorUpdateLogger.logEvent("Shelf.updateBackgroundColors()",
+ "normalBgColor=" + hexColorString(getNormalBgColor())
+ + " background=" + mBackgroundNormal.toDumpString());
+ }
+ }
+
/**
* Update the shelf appearance based on the other notifications around it. This transforms
* the icons from the notification area into the shelf.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
index fc84973..724b19c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
@@ -61,7 +61,6 @@
import com.android.settingslib.mobile.TelephonyIcons;
import com.android.settingslib.net.DataUsageController;
import com.android.systemui.Dumpable;
-import com.android.systemui.res.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
@@ -72,7 +71,8 @@
import com.android.systemui.log.LogBuffer;
import com.android.systemui.log.core.LogLevel;
import com.android.systemui.log.dagger.StatusBarNetworkControllerLog;
-import com.android.systemui.qs.tiles.dialog.InternetDialogFactory;
+import com.android.systemui.qs.tiles.dialog.InternetDialogManager;
+import com.android.systemui.res.R;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -83,8 +83,6 @@
import com.android.systemui.telephony.TelephonyListenerManager;
import com.android.systemui.util.CarrierConfigTracker;
-import dalvik.annotation.optimization.NeverCompile;
-
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
@@ -99,6 +97,7 @@
import javax.inject.Inject;
+import dalvik.annotation.optimization.NeverCompile;
import kotlin.Unit;
/** Platform implementation of the network controller. **/
@@ -201,7 +200,7 @@
private boolean mUserSetup;
private boolean mSimDetected;
private boolean mForceCellularValidated;
- private InternetDialogFactory mInternetDialogFactory;
+ private InternetDialogManager mInternetDialogManager;
private Handler mMainHandler;
private ConfigurationController.ConfigurationListener mConfigurationListener =
@@ -245,7 +244,7 @@
WifiStatusTrackerFactory trackerFactory,
MobileSignalControllerFactory mobileFactory,
@Main Handler handler,
- InternetDialogFactory internetDialogFactory,
+ InternetDialogManager internetDialogManager,
DumpManager dumpManager,
@StatusBarNetworkControllerLog LogBuffer logBuffer) {
this(context, connectivityManager,
@@ -272,7 +271,7 @@
dumpManager,
logBuffer);
mReceiverHandler.post(mRegisterListeners);
- mInternetDialogFactory = internetDialogFactory;
+ mInternetDialogManager = internetDialogManager;
}
@VisibleForTesting
@@ -829,7 +828,7 @@
mReceiverHandler.post(this::handleConfigurationChanged);
break;
case Settings.Panel.ACTION_INTERNET_CONNECTIVITY:
- mMainHandler.post(() -> mInternetDialogFactory.create(true,
+ mMainHandler.post(() -> mInternetDialogManager.create(true,
mAccessPoints.canConfigMobileData(), mAccessPoints.canConfigWifi(),
null /* view */));
break;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt
index 6429815..11636bd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/StatusBarModePerDisplayRepository.kt
@@ -17,6 +17,8 @@
package com.android.systemui.statusbar.data.repository
import android.graphics.Rect
+import android.view.InsetsFlags
+import android.view.ViewDebug
import android.view.WindowInsets
import android.view.WindowInsetsController
import android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS
@@ -305,8 +307,8 @@
letterboxDetails.isNotEmpty()
override fun dump(pw: PrintWriter, args: Array<out String>) {
- pw.println("originalStatusBarAttributes: ${_originalStatusBarAttributes.value}")
- pw.println("modifiedStatusBarAttributes: ${modifiedStatusBarAttributes.value}")
+ pw.println("${_originalStatusBarAttributes.value}")
+ pw.println("${modifiedStatusBarAttributes.value}")
pw.println("statusBarMode: ${statusBarMode.value}")
}
@@ -320,7 +322,20 @@
val navbarColorManagedByIme: Boolean,
@WindowInsets.Type.InsetsType val requestedVisibleTypes: Int,
val letterboxDetails: List<LetterboxDetails>,
- )
+ ) {
+ override fun toString(): String {
+ return """
+ StatusBarAttributes(
+ appearance=${appearance.toAppearanceString()},
+ appearanceRegions=$appearanceRegions,
+ navbarColorManagedByIme=$navbarColorManagedByIme,
+ requestedVisibleTypes=${requestedVisibleTypes.toWindowInsetsString()},
+ letterboxDetails=$letterboxDetails
+ )
+ """
+ .trimIndent()
+ }
+ }
/**
* Internal class keeping track of how [StatusBarAttributes] were transformed into new
@@ -331,9 +346,31 @@
val appearanceRegions: List<AppearanceRegion>,
val navbarColorManagedByIme: Boolean,
val statusBarBounds: BoundsPair,
- )
+ ) {
+ override fun toString(): String {
+ return """
+ ModifiedStatusBarAttributes(
+ appearance=${appearance.toAppearanceString()},
+ appearanceRegions=$appearanceRegions,
+ navbarColorManagedByIme=$navbarColorManagedByIme,
+ statusBarBounds=$statusBarBounds
+ )
+ """
+ .trimIndent()
+ }
+ }
}
+private fun @receiver:WindowInsets.Type.InsetsType Int.toWindowInsetsString() =
+ "[${WindowInsets.Type.toString(this).replace(" ", ", ")}]"
+
+private fun @receiver:Appearance Int.toAppearanceString() =
+ if (this == 0) {
+ "NONE"
+ } else {
+ ViewDebug.flagsToString(InsetsFlags::class.java, "appearance", this)
+ }
+
@AssistedFactory
interface StatusBarModePerDisplayRepositoryFactory {
fun create(@Assisted("displayId") displayId: Int): StatusBarModePerDisplayRepositoryImpl
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ColorUpdateLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ColorUpdateLogger.kt
new file mode 100644
index 0000000..c416d43
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ColorUpdateLogger.kt
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.notification
+
+import android.icu.text.SimpleDateFormat
+import android.util.IndentingPrintWriter
+import com.android.systemui.Dumpable
+import com.android.systemui.Flags.notificationColorUpdateLogger
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.flags.FeatureFlagsClassic
+import com.android.systemui.util.Compile
+import com.android.systemui.util.asIndenting
+import com.android.systemui.util.printCollection
+import com.android.systemui.util.withIncreasedIndent
+import com.google.errorprone.annotations.CompileTimeConstant
+import java.io.PrintWriter
+import java.util.Locale
+import java.util.SortedSet
+import java.util.TreeSet
+import javax.inject.Inject
+
+@SysUISingleton
+class ColorUpdateLogger
+@Inject
+constructor(
+ val featureFlags: FeatureFlagsClassic,
+ dumpManager: DumpManager,
+) : Dumpable {
+
+ inline val isEnabled
+ get() = Compile.IS_DEBUG && notificationColorUpdateLogger()
+ private val frames: MutableList<Frame> = mutableListOf()
+
+ init {
+ dumpManager.registerDumpable(this)
+ if (isEnabled) {
+ instance = this
+ }
+ }
+
+ @JvmOverloads
+ fun logTriggerEvent(@CompileTimeConstant type: String, extra: String? = null) {
+ if (!isEnabled) return
+ val event = Event(type = type, extraValue = extra)
+ val didAppend = frames.lastOrNull()?.tryAddTrigger(event) == true
+ if (!didAppend) {
+ frames.add(Frame(event))
+ if (frames.size > maxFrames) frames.removeAt(0)
+ }
+ }
+
+ @JvmOverloads
+ fun logEvent(@CompileTimeConstant type: String, extra: String? = null) {
+ if (!isEnabled) return
+ val frame = frames.lastOrNull() ?: return
+ frame.events.add(Event(type = type, extraValue = extra))
+ frame.trim()
+ }
+
+ @JvmOverloads
+ fun logNotificationEvent(
+ @CompileTimeConstant type: String,
+ key: String,
+ extra: String? = null
+ ) {
+ if (!isEnabled) return
+ val frame = frames.lastOrNull() ?: return
+ frame.events.add(Event(type = type, extraValue = extra, notificationKey = key))
+ frame.trim()
+ }
+
+ override fun dump(pwOrig: PrintWriter, args: Array<out String>) {
+ val pw = pwOrig.asIndenting()
+ pw.println("enabled: $isEnabled")
+ pw.printCollection("frames", frames) { it.dump(pw) }
+ }
+
+ private class Frame(event: Event) {
+ val startTime: Long = event.time
+ val events: MutableList<Event> = mutableListOf(event)
+ val triggers: SortedSet<String> = TreeSet<String>().apply { add(event.type) }
+ var trimmedEvents: Int = 0
+
+ fun tryAddTrigger(newEvent: Event): Boolean {
+ if (newEvent.time < startTime) return false
+ if (newEvent.time - startTime > triggerStartsNewFrameAge) return false
+ if (newEvent.type in triggers) return false
+ triggers.add(newEvent.type)
+ events.add(newEvent)
+ trim()
+ return true
+ }
+
+ fun trim() {
+ if (events.size > maxEventsPerFrame) {
+ events.removeFirst()
+ trimmedEvents++
+ }
+ }
+
+ fun dump(pw: IndentingPrintWriter) {
+ pw.println("Frame")
+ pw.withIncreasedIndent {
+ pw.println("startTime: ${timeString(startTime)}")
+ pw.printCollection("triggers", triggers)
+ pw.println("trimmedEvents: $trimmedEvents")
+ pw.printCollection("events", events) { it.dump(pw) }
+ }
+ }
+ }
+
+ private class Event(
+ @CompileTimeConstant val type: String,
+ val extraValue: String? = null,
+ val notificationKey: String? = null,
+ ) {
+ val time: Long = System.currentTimeMillis()
+
+ fun dump(pw: IndentingPrintWriter) {
+ pw.append(timeString(time)).append(": ").append(type)
+ extraValue?.let { pw.append(" ").append(it) }
+ notificationKey?.let { pw.append(" ---- ").append(logKey(it)) }
+ pw.println()
+ }
+ }
+
+ private companion object {
+ @JvmStatic
+ var instance: ColorUpdateLogger? = null
+ private set
+ private const val maxFrames = 5
+ private const val maxEventsPerFrame = 250
+ private const val triggerStartsNewFrameAge = 5000
+
+ private val dateFormat = SimpleDateFormat("MM-dd HH:mm:ss.SSS", Locale.US)
+ private fun timeString(time: Long): String = dateFormat.format(time)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationContentDescription.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationContentDescription.kt
index 17fc5c6..bdd9fd0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationContentDescription.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationContentDescription.kt
@@ -26,11 +26,11 @@
/** Returns accessibility content description for a given notification. */
@MainThread
-fun contentDescForNotification(c: Context, n: Notification?): CharSequence {
- val appName = n?.loadHeaderAppName(c) ?: ""
- val title = n?.extras?.getCharSequence(Notification.EXTRA_TITLE)
- val text = n?.extras?.getCharSequence(Notification.EXTRA_TEXT)
- val ticker = n?.tickerText
+fun contentDescForNotification(c: Context, n: Notification): CharSequence {
+ val appName = n.loadHeaderAppName(c) ?: ""
+ val title = n.extras?.getCharSequence(Notification.EXTRA_TITLE)
+ val text = n.extras?.getCharSequence(Notification.EXTRA_TEXT)
+ val ticker = n.tickerText
// Some apps just put the app name into the title
val titleOrText = if (TextUtils.equals(title, appName)) text else title
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinator.java
index 0be4bde..16af9d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinator.java
@@ -22,7 +22,10 @@
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
+import androidx.annotation.NonNull;
+
import com.android.internal.statusbar.IStatusBarService;
+import com.android.systemui.Flags;
import com.android.systemui.media.controls.util.MediaFeatureFlag;
import com.android.systemui.statusbar.notification.InflationException;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
@@ -53,32 +56,13 @@
private final NotifFilter mMediaFilter = new NotifFilter(TAG) {
@Override
- public boolean shouldFilterOut(NotificationEntry entry, long now) {
+ public boolean shouldFilterOut(@NonNull NotificationEntry entry, long now) {
if (!mIsMediaFeatureEnabled || !isMediaNotification(entry.getSbn())) {
return false;
}
- switch (mIconsState.getOrDefault(entry, STATE_ICONS_UNINFLATED)) {
- case STATE_ICONS_UNINFLATED:
- try {
- mIconManager.createIcons(entry);
- mIconsState.put(entry, STATE_ICONS_INFLATED);
- } catch (InflationException e) {
- reportInflationError(entry, e);
- mIconsState.put(entry, STATE_ICONS_ERROR);
- }
- break;
- case STATE_ICONS_INFLATED:
- try {
- mIconManager.updateIcons(entry);
- } catch (InflationException e) {
- reportInflationError(entry, e);
- mIconsState.put(entry, STATE_ICONS_ERROR);
- }
- break;
- case STATE_ICONS_ERROR:
- // do nothing
- break;
+ if (!Flags.notificationsBackgroundMediaIcons()) {
+ inflateOrUpdateIcons(entry);
}
return true;
@@ -87,24 +71,67 @@
private final NotifCollectionListener mCollectionListener = new NotifCollectionListener() {
@Override
- public void onEntryInit(NotificationEntry entry) {
- mIconsState.put(entry, STATE_ICONS_UNINFLATED);
- }
-
- @Override
- public void onEntryUpdated(NotificationEntry entry) {
- if (mIconsState.getOrDefault(entry, STATE_ICONS_UNINFLATED) == STATE_ICONS_ERROR) {
- // The update may have fixed the inflation error, so give it another chance.
+ public void onEntryInit(@NonNull NotificationEntry entry) {
+ // We default to STATE_ICONS_UNINFLATED anyway, so there's no need to initialize it.
+ if (!Flags.notificationsBackgroundMediaIcons()) {
mIconsState.put(entry, STATE_ICONS_UNINFLATED);
}
}
@Override
- public void onEntryCleanUp(NotificationEntry entry) {
+ public void onEntryAdded(@NonNull NotificationEntry entry) {
+ if (Flags.notificationsBackgroundMediaIcons()) {
+ if (isMediaNotification(entry.getSbn())) {
+ inflateOrUpdateIcons(entry);
+ }
+ }
+ }
+
+ @Override
+ public void onEntryUpdated(@NonNull NotificationEntry entry) {
+ if (mIconsState.getOrDefault(entry, STATE_ICONS_UNINFLATED) == STATE_ICONS_ERROR) {
+ // The update may have fixed the inflation error, so give it another chance.
+ mIconsState.put(entry, STATE_ICONS_UNINFLATED);
+ }
+
+ if (Flags.notificationsBackgroundMediaIcons()) {
+ if (isMediaNotification(entry.getSbn())) {
+ inflateOrUpdateIcons(entry);
+ }
+ }
+ }
+
+ @Override
+ public void onEntryCleanUp(@NonNull NotificationEntry entry) {
mIconsState.remove(entry);
}
};
+ private void inflateOrUpdateIcons(NotificationEntry entry) {
+ switch (mIconsState.getOrDefault(entry, STATE_ICONS_UNINFLATED)) {
+ case STATE_ICONS_UNINFLATED:
+ try {
+ mIconManager.createIcons(entry);
+ mIconsState.put(entry, STATE_ICONS_INFLATED);
+ } catch (InflationException e) {
+ reportInflationError(entry, e);
+ mIconsState.put(entry, STATE_ICONS_ERROR);
+ }
+ break;
+ case STATE_ICONS_INFLATED:
+ try {
+ mIconManager.updateIcons(entry);
+ } catch (InflationException e) {
+ reportInflationError(entry, e);
+ mIconsState.put(entry, STATE_ICONS_ERROR);
+ }
+ break;
+ case STATE_ICONS_ERROR:
+ // do nothing
+ break;
+ }
+ }
+
private void reportInflationError(NotificationEntry entry, Exception e) {
// This is the same logic as in PreparationCoordinator; it doesn't handle media
// notifications when the media feature is enabled since they aren't displayed in the shade,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
index 3809ea0..b8a9594 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinator.kt
@@ -23,6 +23,7 @@
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener
+import com.android.systemui.statusbar.notification.ColorUpdateLogger
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
import com.android.systemui.statusbar.notification.row.NotificationGutsManager
@@ -41,7 +42,8 @@
private val mConfigurationController: ConfigurationController,
private val mLockscreenUserManager: NotificationLockscreenUserManager,
private val mGutsManager: NotificationGutsManager,
- private val mKeyguardUpdateMonitor: KeyguardUpdateMonitor
+ private val mKeyguardUpdateMonitor: KeyguardUpdateMonitor,
+ private val colorUpdateLogger: ColorUpdateLogger,
) : Coordinator, ConfigurationController.ConfigurationListener {
private var mIsSwitchingUser = false
@@ -51,11 +53,13 @@
private val mKeyguardUpdateCallback = object : KeyguardUpdateMonitorCallback() {
override fun onUserSwitching(userId: Int) {
+ colorUpdateLogger.logTriggerEvent("VCC.mKeyguardUpdateCallback.onUserSwitching()")
log { "ViewConfigCoordinator.onUserSwitching(userId=$userId)" }
mIsSwitchingUser = true
}
override fun onUserSwitchComplete(userId: Int) {
+ colorUpdateLogger.logTriggerEvent("VCC.mKeyguardUpdateCallback.onUserSwitchComplete()")
log { "ViewConfigCoordinator.onUserSwitchComplete(userId=$userId)" }
mIsSwitchingUser = false
applyChangesOnUserSwitched()
@@ -64,6 +68,7 @@
private val mUserChangedListener = object : UserChangedListener {
override fun onUserChanged(userId: Int) {
+ colorUpdateLogger.logTriggerEvent("VCC.mUserChangedListener.onUserChanged()")
log { "ViewConfigCoordinator.onUserChanged(userId=$userId)" }
applyChangesOnUserSwitched()
}
@@ -77,6 +82,7 @@
}
override fun onDensityOrFontScaleChanged() {
+ colorUpdateLogger.logTriggerEvent("VCC.onDensityOrFontScaleChanged()")
log {
val keyguardIsSwitchingUser = mKeyguardUpdateMonitor.isSwitchingUser
"ViewConfigCoordinator.onDensityOrFontScaleChanged()" +
@@ -93,6 +99,7 @@
}
override fun onUiModeChanged() {
+ colorUpdateLogger.logTriggerEvent("VCC.onUiModeChanged()")
log {
val keyguardIsSwitchingUser = mKeyguardUpdateMonitor.isSwitchingUser
"ViewConfigCoordinator.onUiModeChanged()" +
@@ -107,10 +114,12 @@
}
override fun onThemeChanged() {
+ colorUpdateLogger.logTriggerEvent("VCC.onThemeChanged()")
onDensityOrFontScaleChanged()
}
private fun applyChangesOnUserSwitched() {
+ colorUpdateLogger.logEvent("VCC.applyChangesOnUserSwitched()")
if (mReinflateNotificationsOnUserSwitched) {
updateNotificationsOnDensityOrFontScaleChanged()
mReinflateNotificationsOnUserSwitched = false
@@ -122,6 +131,8 @@
}
private fun updateNotificationsOnUiModeChanged() {
+ colorUpdateLogger.logEvent("VCC.updateNotificationsOnUiModeChanged()",
+ "mode=" + mConfigurationController.nightModeName)
log { "ViewConfigCoordinator.updateNotificationsOnUiModeChanged()" }
traceSection("updateNotifOnUiModeChanged") {
mPipeline?.allNotifs?.forEach { entry ->
@@ -131,6 +142,7 @@
}
private fun updateNotificationsOnDensityOrFontScaleChanged() {
+ colorUpdateLogger.logEvent("VCC.updateNotificationsOnDensityOrFontScaleChanged()")
mPipeline?.allNotifs?.forEach { entry ->
entry.onDensityOrFontScaleChanged()
val exposedGuts = entry.areGutsExposed()
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
index 16f18a3..f792898 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/view/FooterView.java
@@ -19,6 +19,7 @@
import static android.graphics.PorterDuff.Mode.SRC_ATOP;
import static com.android.systemui.Flags.notificationBackgroundTintOptimization;
+import static com.android.systemui.util.ColorUtilKt.hexColorString;
import android.annotation.ColorInt;
import android.annotation.DrawableRes;
@@ -40,11 +41,13 @@
import com.android.settingslib.Utils;
import com.android.systemui.res.R;
+import com.android.systemui.statusbar.notification.ColorUpdateLogger;
import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor;
import com.android.systemui.statusbar.notification.row.FooterViewButton;
import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
import com.android.systemui.statusbar.notification.stack.ExpandableViewState;
import com.android.systemui.statusbar.notification.stack.ViewState;
+import com.android.systemui.util.DrawableDumpKt;
import com.android.systemui.util.DumpUtilsKt;
import java.io.PrintWriter;
@@ -239,6 +242,10 @@
@Override
protected void onFinishInflate() {
+ ColorUpdateLogger colorUpdateLogger = ColorUpdateLogger.getInstance();
+ if (colorUpdateLogger != null) {
+ colorUpdateLogger.logTriggerEvent("Footer.onFinishInflate()");
+ }
super.onFinishInflate();
mClearAllButton = (FooterViewButton) findSecondaryView();
mManageOrHistoryButton = findViewById(R.id.manage_text);
@@ -348,6 +355,10 @@
@Override
protected void onConfigurationChanged(Configuration newConfig) {
+ ColorUpdateLogger colorUpdateLogger = ColorUpdateLogger.getInstance();
+ if (colorUpdateLogger != null) {
+ colorUpdateLogger.logTriggerEvent("Footer.onConfigurationChanged()");
+ }
super.onConfigurationChanged(newConfig);
updateColors();
if (!FooterViewRefactor.isEnabled()) {
@@ -365,14 +376,17 @@
com.android.internal.R.attr.materialColorOnSurface);
final Drawable clearAllBg = theme.getDrawable(R.drawable.notif_footer_btn_background);
final Drawable manageBg = theme.getDrawable(R.drawable.notif_footer_btn_background);
+ final @ColorInt int scHigh;
if (!notificationBackgroundTintOptimization()) {
- final @ColorInt int scHigh = Utils.getColorAttrDefaultColor(mContext,
+ scHigh = Utils.getColorAttrDefaultColor(mContext,
com.android.internal.R.attr.materialColorSurfaceContainerHigh);
if (scHigh != 0) {
final ColorFilter bgColorFilter = new PorterDuffColorFilter(scHigh, SRC_ATOP);
clearAllBg.setColorFilter(bgColorFilter);
manageBg.setColorFilter(bgColorFilter);
}
+ } else {
+ scHigh = 0;
}
mClearAllButton.setBackground(clearAllBg);
mClearAllButton.setTextColor(onSurface);
@@ -380,6 +394,13 @@
mManageOrHistoryButton.setTextColor(onSurface);
mSeenNotifsFooterTextView.setTextColor(onSurface);
mSeenNotifsFooterTextView.setCompoundDrawableTintList(ColorStateList.valueOf(onSurface));
+ ColorUpdateLogger colorUpdateLogger = ColorUpdateLogger.getInstance();
+ if (colorUpdateLogger != null) {
+ colorUpdateLogger.logEvent("Footer.updateColors()",
+ "textColor(onSurface)=" + hexColorString(onSurface)
+ + " backgroundTint(surfaceContainerHigh)=" + hexColorString(scHigh)
+ + " background=" + DrawableDumpKt.dumpToString(manageBg));
+ }
}
private void updateResources() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt
index 9fb453a..65ab4fd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt
@@ -136,7 +136,7 @@
}
launch {
- viewModel.clearAllButton.accessibilityDescriptionId.collect { textId ->
+ viewModel.manageOrHistoryButton.accessibilityDescriptionId.collect { textId ->
footer.setManageOrHistoryButtonDescription(textId)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt
index d7fe36f..332ece4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProvider.kt
@@ -219,15 +219,11 @@
}
private fun isRankingVisibilitySecret(entry: NotificationEntry): Boolean {
- return if (featureFlags.isEnabled(Flags.NOTIF_LS_BACKGROUND_THREAD)) {
- // ranking.lockscreenVisibilityOverride contains possibly out of date DPC and Setting
- // info, and NotificationLockscreenUserManagerImpl is already listening for updates
- // to those
- entry.ranking.channel != null && entry.ranking.channel.lockscreenVisibility ==
+ // ranking.lockscreenVisibilityOverride contains possibly out of date DPC and Setting
+ // info, and NotificationLockscreenUserManagerImpl is already listening for updates
+ // to those
+ return entry.ranking.channel != null && entry.ranking.channel.lockscreenVisibility ==
VISIBILITY_SECRET
- } else {
- entry.ranking.lockscreenVisibilityOverride == VISIBILITY_SECRET
- }
}
override fun dump(pw: PrintWriter, args: Array<out String>) = pw.asIndenting().run {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index fca527f..7358034 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -88,7 +88,7 @@
private boolean mActivated;
private Interpolator mCurrentAppearInterpolator;
- NotificationBackgroundView mBackgroundNormal;
+ protected NotificationBackgroundView mBackgroundNormal;
private float mAnimationTranslationY;
private boolean mDrawingAppearAnimation;
private ValueAnimator mAppearAnimator;
@@ -142,6 +142,10 @@
updateBackgroundTint();
}
+ protected int getNormalBgColor() {
+ return mNormalColor;
+ }
+
/**
* @param width The actual width to apply to the background view.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index cc91ed3..d828ad7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -21,6 +21,7 @@
import static com.android.systemui.statusbar.notification.collection.NotificationEntry.DismissState.PARENT_DISMISSED;
import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP;
+import static com.android.systemui.util.ColorUtilKt.hexColorString;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -85,6 +86,7 @@
import com.android.systemui.statusbar.SmartReplyController;
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
+import com.android.systemui.statusbar.notification.ColorUpdateLogger;
import com.android.systemui.statusbar.notification.FeedbackIcon;
import com.android.systemui.statusbar.notification.LaunchAnimationParameters;
import com.android.systemui.statusbar.notification.NotificationFadeAware;
@@ -172,6 +174,7 @@
private Optional<BubblesManager> mBubblesManagerOptional;
private MetricsLogger mMetricsLogger;
private NotificationChildrenContainerLogger mChildrenContainerLogger;
+ private ColorUpdateLogger mColorUpdateLogger;
private NotificationDismissibilityProvider mDismissibilityProvider;
private FeatureFlags mFeatureFlags;
private int mIconTransformContentShift;
@@ -445,6 +448,7 @@
/**
* Sets animations running in the layouts of this row, including public, private, and children.
+ *
* @param running whether the animations should be started running or stopped.
*/
public void setAnimationRunning(boolean running) {
@@ -611,6 +615,12 @@
private void updateBackgroundColorsOfSelf() {
super.updateBackgroundColors();
+ if (mColorUpdateLogger.isEnabled()) {
+ mColorUpdateLogger.logNotificationEvent("ENR.updateBackgroundColorsOfSelf()",
+ mLoggingKey,
+ "normalBgColor=" + hexColorString(getNormalBgColor())
+ + " background=" + mBackgroundNormal.toDumpString());
+ }
}
@Override
@@ -1389,7 +1399,7 @@
}
public void setContentBackground(int customBackgroundColor, boolean animate,
- NotificationContentView notificationContentView) {
+ NotificationContentView notificationContentView) {
if (getShowingLayout() == notificationContentView) {
setTintColor(customBackgroundColor, animate);
}
@@ -1458,7 +1468,7 @@
}
/**
- * @return if this entry should be kept in its parent during removal.
+ * @return if this entry should be kept in its parent during removal.
*/
public boolean keepInParentForDismissAnimation() {
return mKeepInParentForDismissAnimation;
@@ -1769,6 +1779,7 @@
NotificationDismissibilityProvider dismissibilityProvider,
MetricsLogger metricsLogger,
NotificationChildrenContainerLogger childrenContainerLogger,
+ ColorUpdateLogger colorUpdateLogger,
SmartReplyConstants smartReplyConstants,
SmartReplyController smartReplyController,
FeatureFlags featureFlags,
@@ -1807,6 +1818,7 @@
mNotificationGutsManager = gutsManager;
mMetricsLogger = metricsLogger;
mChildrenContainerLogger = childrenContainerLogger;
+ mColorUpdateLogger = colorUpdateLogger;
mDismissibilityProvider = dismissibilityProvider;
mFeatureFlags = featureFlags;
}
@@ -2265,7 +2277,7 @@
}
public Animator getTranslateViewAnimator(final float leftTarget,
- AnimatorUpdateListener listener) {
+ AnimatorUpdateListener listener) {
if (mTranslateAnim != null) {
mTranslateAnim.cancel();
}
@@ -2664,6 +2676,7 @@
return getCollapsedHeight();
}
}
+
/**
* @return {@code true} if the notification can show it's heads up layout. This is mostly true
* except for legacy use cases.
@@ -2833,7 +2846,7 @@
@Override
public void setHideSensitive(boolean hideSensitive, boolean animated, long delay,
- long duration) {
+ long duration) {
if (getVisibility() == GONE) {
// If we are GONE, the hideSensitive parameter will not be calculated and always be
// false, which is incorrect, let's wait until a real call comes in later.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
index 5614f3a..e59829b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
@@ -41,6 +41,7 @@
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.SmartReplyController;
+import com.android.systemui.statusbar.notification.ColorUpdateLogger;
import com.android.systemui.statusbar.notification.FeedbackIcon;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.provider.NotificationDismissibilityProvider;
@@ -86,6 +87,7 @@
private final SystemClock mClock;
private final String mAppName;
private final String mNotificationKey;
+ private final ColorUpdateLogger mColorUpdateLogger;
private final KeyguardBypassController mKeyguardBypassController;
private final GroupMembershipManager mGroupMembershipManager;
private final GroupExpansionManager mGroupExpansionManager;
@@ -200,6 +202,7 @@
ActivatableNotificationViewController activatableNotificationViewController,
RemoteInputViewSubcomponent.Factory rivSubcomponentFactory,
MetricsLogger metricsLogger,
+ ColorUpdateLogger colorUpdateLogger,
NotificationRowLogger logBufferLogger,
NotificationChildrenContainerLogger childrenContainerLogger,
NotificationListContainer listContainer,
@@ -256,6 +259,7 @@
mDragController = dragController;
mMetricsLogger = metricsLogger;
mChildrenContainerLogger = childrenContainerLogger;
+ mColorUpdateLogger = colorUpdateLogger;
mLogBufferLogger = logBufferLogger;
mSmartReplyConstants = smartReplyConstants;
mSmartReplyController = smartReplyController;
@@ -290,6 +294,7 @@
mDismissibilityProvider,
mMetricsLogger,
mChildrenContainerLogger,
+ mColorUpdateLogger,
mSmartReplyConstants,
mSmartReplyController,
mFeatureFlags,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
index ec8e5d7..ea9df9a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.notification.row;
+import static com.android.systemui.Flags.notificationColorUpdateLogger;
+
import android.animation.AnimatorListenerAdapter;
import android.content.Context;
import android.content.res.Configuration;
@@ -54,8 +56,8 @@
public abstract class ExpandableView extends FrameLayout implements Dumpable, Roundable {
private static final String TAG = "ExpandableView";
/** whether the dump() for this class should include verbose details */
- protected static final boolean DUMP_VERBOSE =
- Compile.IS_DEBUG && Log.isLoggable(TAG, Log.VERBOSE);
+ protected static final boolean DUMP_VERBOSE = Compile.IS_DEBUG
+ && (Log.isLoggable(TAG, Log.VERBOSE) || notificationColorUpdateLogger());
private RoundableState mRoundableState = null;
protected OnHeightChangedListener mOnHeightChangedListener;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
index 7ea9b14..ed3a38d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationBackgroundView.java
@@ -36,6 +36,7 @@
import com.android.settingslib.Utils;
import com.android.systemui.Dumpable;
import com.android.systemui.res.R;
+import com.android.systemui.util.DrawableDumpKt;
import java.io.PrintWriter;
import java.util.Arrays;
@@ -333,6 +334,16 @@
pw.println("mActualHeight: " + mActualHeight);
pw.println("mTintColor: " + hexColorString(mTintColor));
pw.println("mRippleColor: " + hexColorString(mRippleColor));
- pw.println("mBackground: " + mBackground);
+ pw.println("mBackground: " + DrawableDumpKt.dumpToString(mBackground));
+ }
+
+ /** create a concise dump of this view's colors */
+ public String toDumpString() {
+ return "<NotificationBackgroundView"
+ + " tintColor=" + hexColorString(mTintColor)
+ + " rippleColor=" + hexColorString(mRippleColor)
+ + " bgColor=" + DrawableDumpKt.getSolidColor(mBackground)
+ + ">";
+
}
}
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 933a780..7925a1c 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
@@ -99,6 +99,7 @@
import com.android.systemui.statusbar.EmptyShadeView;
import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.StatusBarState;
+import com.android.systemui.statusbar.notification.ColorUpdateLogger;
import com.android.systemui.statusbar.notification.FakeShadowView;
import com.android.systemui.statusbar.notification.LaunchAnimationParameters;
import com.android.systemui.statusbar.notification.NotificationTransitionAnimatorController;
@@ -122,6 +123,7 @@
import com.android.systemui.statusbar.policy.ScrollAdapter;
import com.android.systemui.statusbar.policy.SplitShadeStateController;
import com.android.systemui.util.Assert;
+import com.android.systemui.util.ColorUtilKt;
import com.android.systemui.util.DumpUtilsKt;
import com.google.errorprone.annotations.CompileTimeConstant;
@@ -805,8 +807,8 @@
updateBackgroundDimming();
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
- if (child instanceof ActivatableNotificationView) {
- ((ActivatableNotificationView) child).updateBackgroundColors();
+ if (child instanceof ActivatableNotificationView activatableView) {
+ activatableView.updateBackgroundColors();
}
}
}
@@ -4595,6 +4597,13 @@
final @ColorInt int onSurfaceVariant = Utils.getColorAttrDefaultColor(
mContext, com.android.internal.R.attr.materialColorOnSurfaceVariant);
+ ColorUpdateLogger colorUpdateLogger = ColorUpdateLogger.getInstance();
+ if (colorUpdateLogger != null) {
+ colorUpdateLogger.logEvent("NSSL.updateDecorViews()",
+ "onSurface=" + ColorUtilKt.hexColorString(onSurface)
+ + " onSurfaceVariant=" + ColorUtilKt.hexColorString(onSurfaceVariant));
+ }
+
mSectionsManager.setHeaderForegroundColors(onSurface, onSurfaceVariant);
if (mFooterView != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 78e6a79..d2ff266 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -96,6 +96,7 @@
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.notification.ColorUpdateLogger;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.LaunchAnimationParameters;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
@@ -177,6 +178,7 @@
private final ConfigurationController mConfigurationController;
private final ZenModeController mZenModeController;
private final MetricsLogger mMetricsLogger;
+ private final ColorUpdateLogger mColorUpdateLogger;
private final DumpManager mDumpManager;
private final FalsingCollector mFalsingCollector;
@@ -239,6 +241,7 @@
new View.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
+ mColorUpdateLogger.logTriggerEvent("NSSLC.onViewAttachedToWindow()");
mConfigurationController.addCallback(mConfigurationListener);
if (!FooterViewRefactor.isEnabled()) {
mZenModeController.addCallback(mZenModeControllerCallback);
@@ -254,6 +257,7 @@
@Override
public void onViewDetachedFromWindow(View v) {
+ mColorUpdateLogger.logTriggerEvent("NSSLC.onViewDetachedFromWindow()");
mConfigurationController.removeCallback(mConfigurationListener);
if (!FooterViewRefactor.isEnabled()) {
mZenModeController.removeCallback(mZenModeControllerCallback);
@@ -332,12 +336,16 @@
@Override
public void onUiModeChanged() {
+ mColorUpdateLogger.logTriggerEvent("NSSLC.onUiModeChanged()",
+ "mode=" + mConfigurationController.getNightModeName());
mView.updateBgColor();
mView.updateDecorViews();
}
@Override
public void onThemeChanged() {
+ mColorUpdateLogger.logTriggerEvent("NSSLC.onThemeChanged()",
+ "mode=" + mConfigurationController.getNightModeName());
mView.updateCornerRadius();
mView.updateBgColor();
mView.updateDecorViews();
@@ -719,6 +727,7 @@
ZenModeController zenModeController,
NotificationLockscreenUserManager lockscreenUserManager,
MetricsLogger metricsLogger,
+ ColorUpdateLogger colorUpdateLogger,
DumpManager dumpManager,
FalsingCollector falsingCollector,
FalsingManager falsingManager,
@@ -773,6 +782,7 @@
mZenModeController = zenModeController;
mLockscreenUserManager = lockscreenUserManager;
mMetricsLogger = metricsLogger;
+ mColorUpdateLogger = colorUpdateLogger;
mDumpManager = dumpManager;
mLockscreenShadeTransitionController = lockscreenShadeTransitionController;
mFalsingCollector = falsingCollector;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt
index 4b8fb1e..20e8cac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/domain/interactor/SharedNotificationContainerInteractor.kt
@@ -18,7 +18,7 @@
package com.android.systemui.statusbar.notification.stack.domain.interactor
import android.content.Context
-import com.android.systemui.Flags.centralizedStatusBarDimensRefactor
+import com.android.systemui.Flags.centralizedStatusBarHeightFix
import com.android.systemui.common.ui.data.repository.ConfigurationRepository
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
@@ -76,7 +76,7 @@
getDimensionPixelSize(R.dimen.notification_panel_margin_bottom),
marginTop = getDimensionPixelSize(R.dimen.notification_panel_margin_top),
marginTopLargeScreen =
- if (centralizedStatusBarDimensRefactor()) {
+ if (centralizedStatusBarHeightFix()) {
largeScreenHeaderHelperLazy.get().getLargeScreenHeaderHeight()
} else {
getDimensionPixelSize(R.dimen.large_screen_shade_header_height)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt
index 6321820..7b50256 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt
@@ -122,32 +122,31 @@
qsFullScreen,
isRemoteInputActive,
isShadeClosed ->
- // A pair of (visible, canAnimate)
when {
- !hasNotifications -> Pair(false, true)
+ !hasNotifications -> VisibilityChange.HIDE_WITH_ANIMATION
// Hide the footer until the user setup is complete, to prevent access
// to settings (b/193149550).
- !isUserSetUp -> Pair(false, true)
+ !isUserSetUp -> VisibilityChange.HIDE_WITH_ANIMATION
// Do not show the footer if the lockscreen is visible (incl. AOD),
// except if the shade is opened on top. See also b/219680200.
// Do not animate, as that makes the footer appear briefly when
// transitioning between the shade and keyguard.
- isShowingOnLockscreen -> Pair(false, false)
+ isShowingOnLockscreen -> VisibilityChange.HIDE_WITHOUT_ANIMATION
// Do not show the footer if quick settings are fully expanded (except
// for the foldable split shade view). See b/201427195 && b/222699879.
- qsExpansion == 1f && qsFullScreen -> Pair(false, true)
+ qsExpansion == 1f && qsFullScreen -> VisibilityChange.HIDE_WITH_ANIMATION
// Hide the footer if remote input is active (i.e. user is replying to a
// notification). See b/75984847.
- isRemoteInputActive -> Pair(false, true)
+ isRemoteInputActive -> VisibilityChange.HIDE_WITH_ANIMATION
// Never show the footer if the shade is collapsed (e.g. when HUNing).
- isShadeClosed -> Pair(false, false)
- else -> Pair(true, true)
+ isShadeClosed -> VisibilityChange.HIDE_WITHOUT_ANIMATION
+ else -> VisibilityChange.SHOW_WITH_ANIMATION
}
}
.distinctUntilChanged(
// Equivalent unless visibility changes
- areEquivalent = { a: Pair<Boolean, Boolean>, b: Pair<Boolean, Boolean> ->
- a.first == b.first
+ areEquivalent = { a: VisibilityChange, b: VisibilityChange ->
+ a.visible == b.visible
}
)
// Should we animate the visibility change?
@@ -160,17 +159,24 @@
::Pair
)
.onStart { emit(Pair(false, false)) }
- ) { (visible, canAnimate), (isShadeFullyExpanded, animationsEnabled) ->
+ ) { visibilityChange, (isShadeFullyExpanded, animationsEnabled) ->
// Animate if the shade is interactive, but NOT on the lockscreen. Having
// animations enabled while on the lockscreen makes the footer appear briefly
// when transitioning between the shade and keyguard.
- val shouldAnimate = isShadeFullyExpanded && animationsEnabled && canAnimate
- AnimatableEvent(visible, shouldAnimate)
+ val shouldAnimate =
+ isShadeFullyExpanded && animationsEnabled && visibilityChange.canAnimate
+ AnimatableEvent(visibilityChange.visible, shouldAnimate)
}
.toAnimatedValueFlow()
}
}
+ enum class VisibilityChange(val visible: Boolean, val canAnimate: Boolean) {
+ HIDE_WITHOUT_ANIMATION(visible = false, canAnimate = false),
+ HIDE_WITH_ANIMATION(visible = false, canAnimate = true),
+ SHOW_WITH_ANIMATION(visible = true, canAnimate = true)
+ }
+
private val isShowingOnLockscreen: Flow<Boolean> by lazy {
if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) {
flowOf(false)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
index 476b054..f325157 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt
@@ -45,6 +45,7 @@
import com.android.systemui.keyguard.ui.viewmodel.DozingToLockscreenTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.GlanceableHubToLockscreenTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.GoneToAodTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.GoneToDozingTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel
@@ -98,6 +99,7 @@
private val dreamingToLockscreenTransitionViewModel: DreamingToLockscreenTransitionViewModel,
private val glanceableHubToLockscreenTransitionViewModel:
GlanceableHubToLockscreenTransitionViewModel,
+ private val goneToAodTransitionViewModel: GoneToAodTransitionViewModel,
private val goneToDozingTransitionViewModel: GoneToDozingTransitionViewModel,
private val goneToDreamingTransitionViewModel: GoneToDreamingTransitionViewModel,
private val lockscreenToDreamingTransitionViewModel: LockscreenToDreamingTransitionViewModel,
@@ -304,9 +306,10 @@
val alphaTransitions =
merge(
alternateBouncerToGoneTransitionViewModel.lockscreenAlpha,
- aodToLockscreenTransitionViewModel.lockscreenAlpha(viewState),
+ aodToLockscreenTransitionViewModel.notificationAlpha,
dozingToLockscreenTransitionViewModel.lockscreenAlpha,
dreamingToLockscreenTransitionViewModel.lockscreenAlpha,
+ goneToAodTransitionViewModel.notificationAlpha,
goneToDreamingTransitionViewModel.lockscreenAlpha,
goneToDozingTransitionViewModel.lockscreenAlpha,
lockscreenToDreamingTransitionViewModel.lockscreenAlpha,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
index 6e8ad2e..dea9416 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ConfigurationControllerImpl.kt
@@ -159,6 +159,15 @@
override fun isLayoutRtl(): Boolean {
return layoutDirection == LAYOUT_DIRECTION_RTL
}
+
+ override fun getNightModeName(): String {
+ return when (uiMode and Configuration.UI_MODE_NIGHT_MASK) {
+ Configuration.UI_MODE_NIGHT_YES -> "night"
+ Configuration.UI_MODE_NIGHT_NO -> "day"
+ Configuration.UI_MODE_NIGHT_UNDEFINED -> "undefined"
+ else -> "err"
+ }
+ }
}
// This could be done with a Collection.filter and Collection.forEach, but Collection.filter
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index db55da7..0adc1b0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar.phone;
-import static com.android.systemui.Flags.centralizedStatusBarDimensRefactor;
+import static com.android.systemui.Flags.centralizedStatusBarHeightFix;
import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInScale;
import static com.android.systemui.statusbar.notification.NotificationUtils.interpolate;
@@ -169,7 +169,7 @@
mStatusViewBottomMargin =
res.getDimensionPixelSize(R.dimen.keyguard_status_view_bottom_margin);
mSplitShadeTopNotificationsMargin =
- centralizedStatusBarDimensRefactor()
+ centralizedStatusBarHeightFix()
? LargeScreenHeaderHelper.getLargeScreenHeaderHeight(context)
: res.getDimensionPixelSize(R.dimen.large_screen_shade_header_height);
mSplitShadeTargetTopMargin =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index 7691459..302bdcc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -16,7 +16,7 @@
package com.android.systemui.statusbar.phone;
-import static com.android.systemui.Flags.centralizedStatusBarDimensRefactor;
+import static com.android.systemui.Flags.centralizedStatusBarHeightFix;
import static com.android.systemui.ScreenDecorations.DisplayCutoutView.boundsFromDirection;
import static com.android.systemui.util.Utils.getStatusBarHeaderHeightKeyguard;
@@ -131,7 +131,7 @@
mUserSwitcherContainer = findViewById(R.id.user_switcher_container);
mIsPrivacyDotEnabled = mContext.getResources().getBoolean(R.bool.config_enablePrivacyDot);
loadDimens();
- if (!centralizedStatusBarDimensRefactor()) {
+ if (!centralizedStatusBarHeightFix()) {
setGravity(Gravity.CENTER_VERTICAL);
}
}
@@ -311,7 +311,7 @@
final int minRight = (!isLayoutRtl() && mIsPrivacyDotEnabled)
? Math.max(mMinDotWidth, mPadding.right) : mPadding.right;
- int top = centralizedStatusBarDimensRefactor() ? waterfallTop + mPadding.top : waterfallTop;
+ int top = centralizedStatusBarHeightFix() ? waterfallTop + mPadding.top : waterfallTop;
setPadding(minLeft, top, minRight, 0);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 6f78604..d2e36b8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -733,7 +733,8 @@
if (mPanelExpansionFraction != panelExpansionFraction) {
if (panelExpansionFraction != 0f
- && mKeyguardUnlockAnimationController.isPlayingCannedUnlockAnimation()) {
+ && mKeyguardUnlockAnimationController.isPlayingCannedUnlockAnimation()
+ && mState != ScrimState.UNLOCKED) {
mAnimatingPanelExpansionOnUnlock = true;
} else if (panelExpansionFraction == 0f) {
mAnimatingPanelExpansionOnUnlock = false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
index ac203db..323ab80 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
@@ -27,11 +27,17 @@
import android.view.DisplayCutout
import android.view.Surface
import androidx.annotation.VisibleForTesting
+import com.android.app.tracing.traceSection
import com.android.internal.policy.SystemBarUtils
+import com.android.systemui.BottomMarginCommand
import com.android.systemui.Dumpable
+import com.android.systemui.StatusBarInsetsCommand
+import com.android.systemui.SysUICutoutInformation
+import com.android.systemui.SysUICutoutProvider
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dump.DumpManager
import com.android.systemui.res.R
+import com.android.systemui.statusbar.commandline.CommandRegistry
import com.android.systemui.statusbar.policy.CallbackController
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE
@@ -41,12 +47,6 @@
import com.android.systemui.util.leak.RotationUtils.Rotation
import com.android.systemui.util.leak.RotationUtils.getExactRotation
import com.android.systemui.util.leak.RotationUtils.getResourcesForRotation
-import com.android.app.tracing.traceSection
-import com.android.systemui.BottomMarginCommand
-import com.android.systemui.StatusBarInsetsCommand
-import com.android.systemui.SysUICutoutInformation
-import com.android.systemui.SysUICutoutProvider
-import com.android.systemui.statusbar.commandline.CommandRegistry
import java.io.PrintWriter
import java.lang.Math.max
import javax.inject.Inject
@@ -54,45 +54,53 @@
/**
* Encapsulates logic that can solve for the left/right insets required for the status bar contents.
* Takes into account:
- * 1. rounded_corner_content_padding
- * 2. status_bar_padding_start, status_bar_padding_end
- * 2. display cutout insets from left or right
- * 3. waterfall insets
+ * 1. rounded_corner_content_padding
+ * 2. status_bar_padding_start, status_bar_padding_end
+ * 2. display cutout insets from left or right
+ * 3. waterfall insets
*
+ * Importantly, these functions can determine status bar content left/right insets for any rotation
+ * before having done a layout pass in that rotation.
*
- * Importantly, these functions can determine status bar content left/right insets for any rotation
- * before having done a layout pass in that rotation.
- *
- * NOTE: This class is not threadsafe
+ * NOTE: This class is not threadsafe
*/
@SysUISingleton
-class StatusBarContentInsetsProvider @Inject constructor(
+class StatusBarContentInsetsProvider
+@Inject
+constructor(
val context: Context,
val configurationController: ConfigurationController,
val dumpManager: DumpManager,
val commandRegistry: CommandRegistry,
val sysUICutoutProvider: SysUICutoutProvider,
-) : CallbackController<StatusBarContentInsetsChangedListener>,
- ConfigurationController.ConfigurationListener,
- Dumpable {
+) :
+ CallbackController<StatusBarContentInsetsChangedListener>,
+ ConfigurationController.ConfigurationListener,
+ Dumpable {
// Limit cache size as potentially we may connect large number of displays
// (e.g. network displays)
private val insetsCache = LruCache<CacheKey, Rect>(MAX_CACHE_SIZE)
private val listeners = mutableSetOf<StatusBarContentInsetsChangedListener>()
- private val isPrivacyDotEnabled: Boolean by lazy(LazyThreadSafetyMode.PUBLICATION) {
- context.resources.getBoolean(R.bool.config_enablePrivacyDot)
- }
+ private val isPrivacyDotEnabled: Boolean by
+ lazy(LazyThreadSafetyMode.PUBLICATION) {
+ context.resources.getBoolean(R.bool.config_enablePrivacyDot)
+ }
init {
configurationController.addCallback(this)
dumpManager.registerDumpable(TAG, this)
commandRegistry.registerCommand(StatusBarInsetsCommand.NAME) {
- StatusBarInsetsCommand(object : StatusBarInsetsCommand.Callback {
- override fun onExecute(command: StatusBarInsetsCommand, printWriter: PrintWriter) {
- executeCommand(command, printWriter)
+ StatusBarInsetsCommand(
+ object : StatusBarInsetsCommand.Callback {
+ override fun onExecute(
+ command: StatusBarInsetsCommand,
+ printWriter: PrintWriter
+ ) {
+ executeCommand(command, printWriter)
+ }
}
- })
+ )
}
}
@@ -122,15 +130,13 @@
}
private fun notifyInsetsChanged() {
- listeners.forEach {
- it.onStatusBarContentInsetsChanged()
- }
+ listeners.forEach { it.onStatusBarContentInsetsChanged() }
}
/**
- * Some views may need to care about whether or not the current top display cutout is located
- * in the corner rather than somewhere in the center. In the case of a corner cutout, the
- * status bar area is contiguous.
+ * Some views may need to care about whether or not the current top display cutout is located in
+ * the corner rather than somewhere in the center. In the case of a corner cutout, the status
+ * bar area is contiguous.
*/
fun currentRotationHasCornerCutout(): Boolean {
val cutout = checkNotNull(context.display).cutout ?: return false
@@ -147,8 +153,8 @@
* dot in the coordinates relative to the given rotation.
*
* @param rotation the rotation for which the bounds are required. This is an absolute value
- * (i.e., ROTATION_NONE will always return the same bounds regardless of the context
- * from which this method is called)
+ * (i.e., ROTATION_NONE will always return the same bounds regardless of the context from
+ * which this method is called)
*/
fun getBoundingRectForPrivacyChipForRotation(
@Rotation rotation: Int,
@@ -163,8 +169,8 @@
val rotatedResources = getResourcesForRotation(rotation, context)
val dotWidth = rotatedResources.getDimensionPixelSize(R.dimen.ongoing_appops_dot_diameter)
- val chipWidth = rotatedResources.getDimensionPixelSize(
- R.dimen.ongoing_appops_chip_max_width)
+ val chipWidth =
+ rotatedResources.getDimensionPixelSize(R.dimen.ongoing_appops_chip_max_width)
val isRtl = configurationController.isLayoutRtl
return getPrivacyChipBoundingRectForInsets(insets, dotWidth, chipWidth, isRtl)
@@ -190,14 +196,21 @@
point.orientToRotZero(getExactRotation(context))
val width = point.logicalWidth(rotation)
- val area = insetsCache[key] ?: getAndSetCalculatedAreaForRotation(
- rotation, sysUICutout, getResourcesForRotation(rotation, context), key)
+ val area =
+ insetsCache[key]
+ ?: getAndSetCalculatedAreaForRotation(
+ rotation,
+ sysUICutout,
+ getResourcesForRotation(rotation, context),
+ key
+ )
Insets.of(area.left, area.top, /* right= */ width - area.right, /* bottom= */ 0)
}
/**
* Calculate the insets for the status bar content in the device's current rotation
+ *
* @see getStatusBarContentAreaForRotation
*/
fun getStatusBarContentInsetsForCurrentRotation(): Insets {
@@ -205,27 +218,28 @@
}
/**
- * Calculates the area of the status bar contents invariant of the current device rotation,
- * in the target rotation's coordinates
+ * Calculates the area of the status bar contents invariant of the current device rotation, in
+ * the target rotation's coordinates
*
* @param rotation the rotation for which the bounds are required. This is an absolute value
- * (i.e., ROTATION_NONE will always return the same bounds regardless of the context
- * from which this method is called)
+ * (i.e., ROTATION_NONE will always return the same bounds regardless of the context from
+ * which this method is called)
*/
@JvmOverloads
- fun getStatusBarContentAreaForRotation(
- @Rotation rotation: Int
- ): Rect {
+ fun getStatusBarContentAreaForRotation(@Rotation rotation: Int): Rect {
val sysUICutout = sysUICutoutProvider.cutoutInfoForCurrentDisplay()
val displayCutout = sysUICutout?.cutout
val key = getCacheKey(rotation, displayCutout)
- return insetsCache[key] ?: getAndSetCalculatedAreaForRotation(
- rotation, sysUICutout, getResourcesForRotation(rotation, context), key)
+ return insetsCache[key]
+ ?: getAndSetCalculatedAreaForRotation(
+ rotation,
+ sysUICutout,
+ getResourcesForRotation(rotation, context),
+ key
+ )
}
- /**
- * Get the status bar content area for the given rotation, in absolute bounds
- */
+ /** Get the status bar content area for the given rotation, in absolute bounds */
fun getStatusBarContentAreaForCurrentRotation(): Rect {
val rotation = getExactRotation(context)
return getStatusBarContentAreaForRotation(rotation)
@@ -237,10 +251,9 @@
rotatedResources: Resources,
key: CacheKey
): Rect {
- return getCalculatedAreaForRotation(sysUICutout, targetRotation, rotatedResources)
- .also {
- insetsCache.put(key, it)
- }
+ return getCalculatedAreaForRotation(sysUICutout, targetRotation, rotatedResources).also {
+ insetsCache.put(key, it)
+ }
}
private fun getCalculatedAreaForRotation(
@@ -250,12 +263,14 @@
): Rect {
val currentRotation = getExactRotation(context)
- val roundedCornerPadding = rotatedResources
- .getDimensionPixelSize(R.dimen.rounded_corner_content_padding)
- val minDotPadding = if (isPrivacyDotEnabled)
+ val roundedCornerPadding =
+ rotatedResources.getDimensionPixelSize(R.dimen.rounded_corner_content_padding)
+ val minDotPadding =
+ if (isPrivacyDotEnabled)
rotatedResources.getDimensionPixelSize(R.dimen.ongoing_appops_dot_min_padding)
else 0
- val dotWidth = if (isPrivacyDotEnabled)
+ val dotWidth =
+ if (isPrivacyDotEnabled)
rotatedResources.getDimensionPixelSize(R.dimen.ongoing_appops_dot_diameter)
else 0
@@ -271,20 +286,21 @@
val bottomAlignedMargin = getBottomAlignedMargin(targetRotation, rotatedResources)
val statusBarContentHeight =
- rotatedResources.getDimensionPixelSize(R.dimen.status_bar_icon_size_sp)
+ rotatedResources.getDimensionPixelSize(R.dimen.status_bar_icon_size_sp)
return calculateInsetsForRotationWithRotatedResources(
- currentRotation,
- targetRotation,
- sysUICutout,
- context.resources.configuration.windowConfiguration.maxBounds,
- SystemBarUtils.getStatusBarHeightForRotation(context, targetRotation),
- minLeft,
- minRight,
- configurationController.isLayoutRtl,
- dotWidth,
- bottomAlignedMargin,
- statusBarContentHeight)
+ currentRotation,
+ targetRotation,
+ sysUICutout,
+ context.resources.configuration.windowConfiguration.maxBounds,
+ SystemBarUtils.getStatusBarHeightForRotation(context, targetRotation),
+ minLeft,
+ minRight,
+ configurationController.isLayoutRtl,
+ dotWidth,
+ bottomAlignedMargin,
+ statusBarContentHeight
+ )
}
private fun executeCommand(command: StatusBarInsetsCommand, printWriter: PrintWriter) {
@@ -295,7 +311,7 @@
val rotation = command.rotationValue
if (rotation == null) {
printWriter.println(
- "Rotation should be one of ${BottomMarginCommand.ROTATION_DEGREES_OPTIONS}"
+ "Rotation should be one of ${BottomMarginCommand.ROTATION_DEGREES_OPTIONS}"
)
return
}
@@ -323,13 +339,13 @@
return override
}
val dimenRes =
- when (targetRotation) {
- Surface.ROTATION_0 -> R.dimen.status_bar_bottom_aligned_margin_rotation_0
- Surface.ROTATION_90 -> R.dimen.status_bar_bottom_aligned_margin_rotation_90
- Surface.ROTATION_180 -> R.dimen.status_bar_bottom_aligned_margin_rotation_180
- Surface.ROTATION_270 -> R.dimen.status_bar_bottom_aligned_margin_rotation_270
- else -> throw IllegalStateException("Unknown rotation: $targetRotation")
- }
+ when (targetRotation) {
+ Surface.ROTATION_0 -> R.dimen.status_bar_bottom_aligned_margin_rotation_0
+ Surface.ROTATION_90 -> R.dimen.status_bar_bottom_aligned_margin_rotation_90
+ Surface.ROTATION_180 -> R.dimen.status_bar_bottom_aligned_margin_rotation_180
+ Surface.ROTATION_270 -> R.dimen.status_bar_bottom_aligned_margin_rotation_270
+ else -> throw IllegalStateException("Unknown rotation: $targetRotation")
+ }
return resources.getDimensionPixelSize(dimenRes)
}
@@ -339,17 +355,12 @@
}
override fun dump(pw: PrintWriter, args: Array<out String>) {
- insetsCache.snapshot().forEach { (key, rect) ->
- pw.println("$key -> $rect")
- }
+ insetsCache.snapshot().forEach { (key, rect) -> pw.println("$key -> $rect") }
pw.println(insetsCache)
pw.println("Bottom margin overrides: $marginBottomOverrides")
}
- private fun getCacheKey(
- @Rotation rotation: Int,
- displayCutout: DisplayCutout?
- ): CacheKey =
+ private fun getCacheKey(@Rotation rotation: Int, displayCutout: DisplayCutout?): CacheKey =
CacheKey(
rotation = rotation,
displaySize = Rect(context.resources.configuration.windowConfiguration.maxBounds),
@@ -387,15 +398,19 @@
isRtl: Boolean
): Rect {
return if (isRtl) {
- Rect(contentRect.left - dotWidth,
- contentRect.top,
- contentRect.left + chipWidth,
- contentRect.bottom)
+ Rect(
+ contentRect.left - dotWidth,
+ contentRect.top,
+ contentRect.left + chipWidth,
+ contentRect.bottom
+ )
} else {
- Rect(contentRect.right - chipWidth,
- contentRect.top,
- contentRect.right + dotWidth,
- contentRect.bottom)
+ Rect(
+ contentRect.right - chipWidth,
+ contentRect.top,
+ contentRect.right + dotWidth,
+ contentRect.bottom
+ )
}
}
@@ -439,20 +454,21 @@
val rotZeroBounds = getRotationZeroDisplayBounds(maxBounds, currentRotation)
return getStatusBarContentBounds(
- sysUICutout,
- statusBarHeight,
- rotZeroBounds.right,
- rotZeroBounds.bottom,
- maxBounds.width(),
- maxBounds.height(),
- minLeft,
- minRight,
- isRtl,
- dotWidth,
- targetRotation,
- currentRotation,
- bottomAlignedMargin,
- statusBarContentHeight)
+ sysUICutout,
+ statusBarHeight,
+ rotZeroBounds.right,
+ rotZeroBounds.bottom,
+ maxBounds.width(),
+ maxBounds.height(),
+ minLeft,
+ minRight,
+ isRtl,
+ dotWidth,
+ targetRotation,
+ currentRotation,
+ bottomAlignedMargin,
+ statusBarContentHeight
+ )
}
/**
@@ -470,31 +486,30 @@
* @param dotWidth privacy dot image width (0 if privacy dot is disabled)
* @param targetRotation the rotation for which to calculate margins
* @param currentRotation the rotation from which the display cutout was generated
- *
* @return a Rect which exactly calculates the Status Bar's content rect relative to the target
- * rotation
+ * rotation
*/
private fun getStatusBarContentBounds(
- sysUICutout: SysUICutoutInformation?,
- sbHeight: Int,
- width: Int,
- height: Int,
- cWidth: Int,
- cHeight: Int,
- minLeft: Int,
- minRight: Int,
- isRtl: Boolean,
- dotWidth: Int,
- @Rotation targetRotation: Int,
- @Rotation currentRotation: Int,
- bottomAlignedMargin: Int,
- statusBarContentHeight: Int
+ sysUICutout: SysUICutoutInformation?,
+ sbHeight: Int,
+ width: Int,
+ height: Int,
+ cWidth: Int,
+ cHeight: Int,
+ minLeft: Int,
+ minRight: Int,
+ isRtl: Boolean,
+ dotWidth: Int,
+ @Rotation targetRotation: Int,
+ @Rotation currentRotation: Int,
+ bottomAlignedMargin: Int,
+ statusBarContentHeight: Int
): Rect {
val insetTop = getInsetTop(bottomAlignedMargin, statusBarContentHeight, sbHeight)
val logicalDisplayWidth = if (targetRotation.isHorizontal()) height else width
- // Exclude the bottom rect, as it doesn't intersect with the status bar.
+ // Exclude the bottom rect, as it doesn't intersect with the status bar.
val cutoutRects = sysUICutout?.cutout?.boundingRectsLeftRightTop
if (cutoutRects.isNullOrEmpty()) {
return Rect(minLeft, insetTop, logicalDisplayWidth - minRight, sbHeight)
@@ -580,9 +595,9 @@
*/
@Px
private fun getInsetTop(
- bottomAlignedMargin: Int,
- statusBarContentHeight: Int,
- statusBarHeight: Int
+ bottomAlignedMargin: Int,
+ statusBarContentHeight: Int,
+ statusBarHeight: Int
): Int {
val bottomAlignmentEnabled = bottomAlignedMargin >= 0
if (!bottomAlignmentEnabled) {
@@ -672,7 +687,8 @@
private fun Rect.logicalWidth(@Rotation rot: Int): Int {
return when (rot) {
- ROTATION_NONE, ROTATION_UPSIDE_DOWN -> width()
+ ROTATION_NONE,
+ ROTATION_UPSIDE_DOWN -> width()
else /* LANDSCAPE, SEASCAPE */ -> height()
}
}
@@ -683,7 +699,8 @@
private fun Point.orientToRotZero(@Rotation rot: Int) {
when (rot) {
- ROTATION_NONE, ROTATION_UPSIDE_DOWN -> return
+ ROTATION_NONE,
+ ROTATION_UPSIDE_DOWN -> return
else -> {
// swap width and height to zero-orient bounds
val yTmp = y
@@ -695,7 +712,8 @@
private fun Point.logicalWidth(@Rotation rot: Int): Int {
return when (rot) {
- ROTATION_NONE, ROTATION_UPSIDE_DOWN -> x
+ ROTATION_NONE,
+ ROTATION_UPSIDE_DOWN -> x
else -> y
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
index 1414150..2c1780d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseHeadsUpManager.java
@@ -158,7 +158,15 @@
@Override
public void showNotification(@NonNull NotificationEntry entry) {
mLogger.logShowNotification(entry);
- addEntry(entry);
+
+ // Add new entry and begin managing it
+ HeadsUpEntry headsUpEntry = createHeadsUpEntry();
+ headsUpEntry.setEntry(entry);
+ mHeadsUpEntryMap.put(entry.getKey(), headsUpEntry);
+ onEntryAdded(headsUpEntry);
+ entry.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+ entry.setIsHeadsUpEntry(true);
+
updateNotification(entry.getKey(), true /* shouldHeadsUpAgain */);
entry.setInterruption();
}
@@ -319,19 +327,6 @@
}
/**
- * Add a new entry and begin managing it.
- * @param entry the entry to add
- */
- protected final void addEntry(@NonNull NotificationEntry entry) {
- HeadsUpEntry headsUpEntry = createHeadsUpEntry();
- headsUpEntry.setEntry(entry);
- mHeadsUpEntryMap.put(entry.getKey(), headsUpEntry);
- onEntryAdded(headsUpEntry);
- entry.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
- entry.setIsHeadsUpEntry(true);
- }
-
- /**
* Manager-specific logic that should occur when an entry is added.
* @param headsUpEntry entry added
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java
index b2ef818..cec77c1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ConfigurationController.java
@@ -33,6 +33,9 @@
/** Query the current configuration's layout direction */
boolean isLayoutRtl();
+ /** Logging only; Query the current configuration's night mode name */
+ String getNightModeName();
+
interface ConfigurationListener {
default void onConfigChanged(Configuration newConfig) {}
default void onDensityOrFontScaleChanged() {}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
index df210b0..600005b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.policy;
+import static com.android.systemui.Flags.registerZenModeContentObserverBackground;
+
import android.app.AlarmManager;
import android.app.Flags;
import android.app.NotificationManager;
@@ -45,6 +47,7 @@
import com.android.systemui.Dumpable;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.settings.UserTracker;
@@ -104,6 +107,7 @@
public ZenModeControllerImpl(
Context context,
@Main Handler handler,
+ @Background Handler bgHandler,
BroadcastDispatcher broadcastDispatcher,
DumpManager dumpManager,
GlobalSettings globalSettings,
@@ -134,9 +138,18 @@
}
};
mNoMan = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
- globalSettings.registerContentObserver(Global.ZEN_MODE, modeContentObserver);
+ if (registerZenModeContentObserverBackground()) {
+ bgHandler.post(() -> {
+ globalSettings.registerContentObserver(Global.ZEN_MODE, modeContentObserver);
+ globalSettings.registerContentObserver(Global.ZEN_MODE_CONFIG_ETAG,
+ configContentObserver);
+ });
+ } else {
+ globalSettings.registerContentObserver(Global.ZEN_MODE, modeContentObserver);
+ globalSettings.registerContentObserver(Global.ZEN_MODE_CONFIG_ETAG,
+ configContentObserver);
+ }
updateZenMode(getModeSettingValueFromProvider());
- globalSettings.registerContentObserver(Global.ZEN_MODE_CONFIG_ETAG, configContentObserver);
updateZenModeConfig();
updateConsolidatedNotificationPolicy();
mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
index 585ab72..44c684c 100644
--- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java
@@ -815,7 +815,7 @@
? () -> {}
: () -> {
Log.d(TAG, "ThemeHomeDelay: ThemeOverlayController ready");
- mActivityManager.setThemeOverlayReady(true);
+ mActivityManager.setThemeOverlayReady(currentUser);
};
if (colorSchemeIsApplied(managedProfiles)) {
diff --git a/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt b/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt
index 4dfd5a1..b3e60e3 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt
@@ -29,25 +29,31 @@
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.power.shared.model.ScreenPowerState
import com.android.systemui.power.shared.model.WakeSleepReason
+import com.android.systemui.power.shared.model.WakefulnessState
import com.android.systemui.shared.system.SysUiStatsLog
+import com.android.systemui.unfold.DisplaySwitchLatencyTracker.DisplaySwitchLatencyEvent
import com.android.systemui.unfold.dagger.UnfoldSingleThreadBg
import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractor
import com.android.systemui.util.Compile
import com.android.systemui.util.Utils.isDeviceFoldable
import com.android.systemui.util.animation.data.repository.AnimationStatusRepository
import com.android.systemui.util.kotlin.pairwise
+import com.android.systemui.util.kotlin.race
import com.android.systemui.util.time.SystemClock
import com.android.systemui.util.time.measureTimeMillis
+import java.time.Duration
import java.util.concurrent.Executor
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.TimeoutCancellationException
import kotlinx.coroutines.asCoroutineDispatcher
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.launch
+import kotlinx.coroutines.withTimeout
/**
* [DisplaySwitchLatencyTracker] tracks latency and related fields for display switch of a foldable
@@ -70,6 +76,8 @@
) : CoreStartable {
private val backgroundDispatcher = singleThreadBgExecutor.asCoroutineDispatcher()
+ private val isAodEnabled: Boolean
+ get() = keyguardInteractor.isAodAvailable.value
@OptIn(ExperimentalCoroutinesApi::class)
override fun start() {
@@ -98,8 +106,14 @@
val displaySwitchTimeMs =
measureTimeMillis(systemClock) {
- traceAsync(TAG, "displaySwitch") {
- waitForDisplaySwitch(toFoldableDeviceState)
+ try {
+ withTimeout(SCREEN_EVENT_TIMEOUT) {
+ traceAsync(TAG, "displaySwitch") {
+ waitForDisplaySwitch(toFoldableDeviceState)
+ }
+ }
+ } catch (e: TimeoutCancellationException) {
+ Log.e(TAG, "Wait for display switch timed out")
}
}
@@ -129,19 +143,19 @@
val isTransitionEnabled =
unfoldTransitionInteractor.isAvailable &&
animationStatusRepository.areAnimationsEnabled().first()
- if (shouldWaitForScreenOn(toFoldableDeviceState, isTransitionEnabled)) {
- waitForScreenTurnedOn()
- } else {
+ if (shouldWaitForTransitionStart(toFoldableDeviceState, isTransitionEnabled)) {
traceAsync(TAG, "waitForTransitionStart()") {
unfoldTransitionInteractor.waitForTransitionStart()
}
+ } else {
+ race({ waitForScreenTurnedOn() }, { waitForGoToSleepWithScreenOff() })
}
}
- private fun shouldWaitForScreenOn(
+ private fun shouldWaitForTransitionStart(
toFoldableDeviceState: Int,
isTransitionEnabled: Boolean
- ): Boolean = (toFoldableDeviceState == FOLDABLE_DEVICE_STATE_CLOSED || !isTransitionEnabled)
+ ): Boolean = (toFoldableDeviceState != FOLDABLE_DEVICE_STATE_CLOSED && isTransitionEnabled)
private suspend fun waitForScreenTurnedOn() {
traceAsync(TAG, "waitForScreenTurnedOn()") {
@@ -149,19 +163,30 @@
}
}
+ private suspend fun waitForGoToSleepWithScreenOff() {
+ traceAsync(TAG, "waitForGoToSleepWithScreenOff()") {
+ powerInteractor.detailedWakefulness
+ .filter { it.internalWakefulnessState == WakefulnessState.ASLEEP && !isAodEnabled }
+ .first()
+ }
+ }
+
private fun getCurrentState(): Int =
when {
isStateAod() -> SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__TO_STATE__AOD
+ isStateScreenOff() -> SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__TO_STATE__SCREEN_OFF
else -> SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__TO_STATE__UNKNOWN
}
- private fun isStateAod(): Boolean {
+ private fun isStateAod(): Boolean = (isAsleepDueToFold() && isAodEnabled)
+
+ private fun isStateScreenOff(): Boolean = (isAsleepDueToFold() && !isAodEnabled)
+
+ private fun isAsleepDueToFold(): Boolean {
val lastWakefulnessEvent = powerInteractor.detailedWakefulness.value
- val isAodEnabled = keyguardInteractor.isAodAvailable.value
return (lastWakefulnessEvent.isAsleep() &&
- (lastWakefulnessEvent.lastSleepReason == WakeSleepReason.FOLD) &&
- isAodEnabled)
+ (lastWakefulnessEvent.lastSleepReason == WakeSleepReason.FOLD))
}
private inline fun log(msg: () -> String) {
@@ -232,6 +257,7 @@
private const val VALUE_UNKNOWN = -1
private const val TAG = "DisplaySwitchLatency"
private val DEBUG = Compile.IS_DEBUG && Log.isLoggable(TAG, Log.VERBOSE)
+ private val SCREEN_EVENT_TIMEOUT = Duration.ofMillis(15000).toMillis()
private const val FOLDABLE_DEVICE_STATE_UNKNOWN =
SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__FROM_FOLDABLE_DEVICE_STATE__STATE_UNKNOWN
diff --git a/packages/SystemUI/src/com/android/systemui/util/DrawableDump.kt b/packages/SystemUI/src/com/android/systemui/util/DrawableDump.kt
new file mode 100644
index 0000000..0c079a3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/DrawableDump.kt
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.util
+
+import android.content.res.ColorStateList
+import android.graphics.BlendMode
+import android.graphics.BlendModeColorFilter
+import android.graphics.ColorFilter
+import android.graphics.LightingColorFilter
+import android.graphics.PorterDuffColorFilter
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.DrawableWrapper
+import android.graphics.drawable.GradientDrawable
+import android.graphics.drawable.LayerDrawable
+import android.graphics.drawable.RippleDrawable
+import android.util.Log
+
+fun dumpToString(drawable: Drawable?): String =
+ if (Compile.IS_DEBUG) StringBuilder().appendDrawable(drawable).toString()
+ else drawable.toString()
+
+fun getSolidColor(drawable: Drawable?): String =
+ if (Compile.IS_DEBUG) hexColorString(getSolidColors(drawable)?.defaultColor)
+ else if (drawable == null) "null" else "?"
+
+private fun getSolidColors(drawable: Drawable?): ColorStateList? {
+ return when (drawable) {
+ is GradientDrawable -> {
+ return drawable.getStateField<ColorStateList>("mSolidColors")
+ }
+ is LayerDrawable -> {
+ for (iLayer in 0 until drawable.numberOfLayers) {
+ getSolidColors(drawable.getDrawable(iLayer))?.let {
+ return it
+ }
+ }
+ null
+ }
+ is DrawableWrapper -> {
+ return getSolidColors(drawable.drawable)
+ }
+ else -> null
+ }
+}
+
+private fun StringBuilder.appendDrawable(drawable: Drawable?): StringBuilder {
+ if (drawable == null) {
+ append("null")
+ return this
+ }
+ append("<")
+ append(drawable.javaClass.simpleName)
+
+ drawable.getStateField<ColorStateList>("mTint", fieldRequired = false)?.let {
+ append(" tint=")
+ appendColors(it)
+ append(" blendMode=")
+ append(drawable.getStateField<BlendMode>("mBlendMode"))
+ }
+ drawable.colorFilter
+ ?.takeUnless { drawable is DrawableWrapper }
+ ?.let {
+ append(" colorFilter=")
+ appendColorFilter(it)
+ }
+ when (drawable) {
+ is DrawableWrapper -> {
+ append(" wrapped=")
+ appendDrawable(drawable.drawable)
+ }
+ is LayerDrawable -> {
+ if (drawable is RippleDrawable) {
+ drawable.getStateField<ColorStateList>("mColor")?.let {
+ append(" color=")
+ appendColors(it)
+ }
+ drawable.effectColor?.let {
+ append(" effectColor=")
+ appendColors(it)
+ }
+ }
+ append(" layers=[")
+ for (iLayer in 0 until drawable.numberOfLayers) {
+ if (iLayer != 0) append(", ")
+ appendDrawable(drawable.getDrawable(iLayer))
+ }
+ append("]")
+ }
+ is GradientDrawable -> {
+ drawable
+ .getStateField<Int>("mShape")
+ ?.takeIf { it != 0 }
+ ?.let {
+ append(" shape=")
+ append(it)
+ }
+ drawable.getStateField<ColorStateList>("mSolidColors")?.let {
+ append(" solidColors=")
+ appendColors(it)
+ }
+ drawable.getStateField<ColorStateList>("mStrokeColors")?.let {
+ append(" strokeColors=")
+ appendColors(it)
+ }
+ drawable.colors?.let {
+ append(" gradientColors=[")
+ it.forEachIndexed { iColor, color ->
+ if (iColor != 0) append(", ")
+ append(hexColorString(color))
+ }
+ append("]")
+ }
+ }
+ }
+ append(">")
+ return this
+}
+
+private inline fun <reified T> Drawable.getStateField(
+ name: String,
+ fieldRequired: Boolean = true
+): T? {
+ val state = this.constantState ?: return null
+ val clazz = state.javaClass
+ return try {
+ val field = clazz.getDeclaredField(name)
+ field.isAccessible = true
+ field.get(state) as T?
+ } catch (ex: Exception) {
+ if (fieldRequired) {
+ Log.w(TAG, "Missing ${clazz.simpleName}.$name: ${T::class.simpleName}", ex)
+ } else if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "Missing ${clazz.simpleName}.$name: ${T::class.simpleName} ($ex)")
+ }
+ null
+ }
+}
+
+private fun Appendable.appendColors(colorStateList: ColorStateList?) {
+ if (colorStateList == null) {
+ append("null")
+ return
+ }
+ val colors = colorStateList.colors
+ if (colors.size == 1) {
+ append(hexColorString(colors[0]))
+ return
+ }
+ append("<ColorStateList size=")
+ append(colors.size.toString())
+ append(" default=")
+ append(hexColorString(colorStateList.defaultColor))
+ append(">")
+}
+
+private fun Appendable.appendColorFilter(colorFilter: ColorFilter?) {
+ if (colorFilter == null) {
+ append("null")
+ return
+ }
+ append("<")
+ append(colorFilter.javaClass.simpleName)
+ when (colorFilter) {
+ is PorterDuffColorFilter -> {
+ append(" color=")
+ append(hexColorString(colorFilter.color))
+ append(" mode=")
+ append(colorFilter.mode.toString())
+ }
+ is BlendModeColorFilter -> {
+ append(" color=")
+ append(hexColorString(colorFilter.color))
+ append(" mode=")
+ append(colorFilter.mode.toString())
+ }
+ is LightingColorFilter -> {
+ append(" multiply=")
+ append(hexColorString(colorFilter.colorMultiply))
+ append(" add=")
+ append(hexColorString(colorFilter.colorAdd))
+ }
+ else -> append(" unhandled")
+ }
+ append(">")
+}
+
+private const val TAG = "DrawableDump"
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/FlowDumper.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/FlowDumper.kt
new file mode 100644
index 0000000..e17274c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/FlowDumper.kt
@@ -0,0 +1,142 @@
+/*
+ * 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.util.kotlin
+
+import android.util.IndentingPrintWriter
+import com.android.systemui.Dumpable
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.util.asIndenting
+import com.android.systemui.util.printCollection
+import java.io.PrintWriter
+import java.util.concurrent.ConcurrentHashMap
+import java.util.concurrent.atomic.AtomicBoolean
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharedFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.flow
+
+/**
+ * An interface which gives the implementing type flow extension functions which will register a
+ * given flow as a field in the Dumpable.
+ */
+interface FlowDumper : Dumpable {
+ /**
+ * Include the last emitted value of this Flow whenever it is being collected. Remove its value
+ * when collection ends.
+ *
+ * @param dumpName the name to use for this field in the dump output
+ */
+ fun <T> Flow<T>.dumpWhileCollecting(dumpName: String): Flow<T>
+
+ /**
+ * Include the [SharedFlow.replayCache] for this Flow in the dump.
+ *
+ * @param dumpName the name to use for this field in the dump output
+ */
+ fun <T, F : SharedFlow<T>> F.dumpReplayCache(dumpName: String): F
+
+ /**
+ * Include the [StateFlow.value] for this Flow in the dump.
+ *
+ * @param dumpName the name to use for this field in the dump output
+ */
+ fun <T, F : StateFlow<T>> F.dumpValue(dumpName: String): F
+
+ /** The default [Dumpable.dump] implementation which just calls [dumpFlows] */
+ override fun dump(pw: PrintWriter, args: Array<out String>) = dumpFlows(pw.asIndenting())
+
+ /** Dump all the values from any registered / active Flows. */
+ fun dumpFlows(pw: IndentingPrintWriter)
+}
+
+/**
+ * An implementation of [FlowDumper]. This be extended directly, or can be used to implement
+ * [FlowDumper] by delegation.
+ *
+ * @param dumpManager if provided, this will be used by the [FlowDumperImpl] to register and
+ * unregister itself when there is something to dump.
+ * @param tag a static name by which this [FlowDumperImpl] is registered. If not provided, this
+ * class's name will be used. If you're implementing by delegation, you probably want to provide
+ * this tag to get a meaningful dumpable name.
+ */
+open class FlowDumperImpl(private val dumpManager: DumpManager?, tag: String? = null) : FlowDumper {
+ private val stateFlowMap = ConcurrentHashMap<String, StateFlow<*>>()
+ private val sharedFlowMap = ConcurrentHashMap<String, SharedFlow<*>>()
+ private val flowCollectionMap = ConcurrentHashMap<Pair<String, String>, Any>()
+ override fun dumpFlows(pw: IndentingPrintWriter) {
+ pw.printCollection("StateFlow (value)", stateFlowMap.toSortedMap().entries) { (key, flow) ->
+ append(key).append('=').println(flow.value)
+ }
+ pw.printCollection("SharedFlow (replayCache)", sharedFlowMap.toSortedMap().entries) {
+ (key, flow) ->
+ append(key).append('=').println(flow.replayCache)
+ }
+ val comparator = compareBy<Pair<String, String>> { it.first }.thenBy { it.second }
+ pw.printCollection("Flow (latest)", flowCollectionMap.toSortedMap(comparator).entries) {
+ (pair, value) ->
+ append(pair.first).append('=').println(value)
+ }
+ }
+
+ private val Any.idString: String
+ get() = Integer.toHexString(System.identityHashCode(this))
+
+ override fun <T> Flow<T>.dumpWhileCollecting(dumpName: String): Flow<T> = flow {
+ val mapKey = dumpName to idString
+ try {
+ collect {
+ flowCollectionMap[mapKey] = it ?: "null"
+ updateRegistration(required = true)
+ emit(it)
+ }
+ } finally {
+ flowCollectionMap.remove(mapKey)
+ updateRegistration(required = false)
+ }
+ }
+
+ override fun <T, F : StateFlow<T>> F.dumpValue(dumpName: String): F {
+ stateFlowMap[dumpName] = this
+ return this
+ }
+
+ override fun <T, F : SharedFlow<T>> F.dumpReplayCache(dumpName: String): F {
+ sharedFlowMap[dumpName] = this
+ return this
+ }
+
+ private val dumpManagerName = tag ?: "[$idString] ${javaClass.simpleName}"
+ private var registered = AtomicBoolean(false)
+ private fun updateRegistration(required: Boolean) {
+ if (dumpManager == null) return
+ if (required && registered.get()) return
+ synchronized(registered) {
+ val shouldRegister =
+ stateFlowMap.isNotEmpty() ||
+ sharedFlowMap.isNotEmpty() ||
+ flowCollectionMap.isNotEmpty()
+ val wasRegistered = registered.getAndSet(shouldRegister)
+ if (wasRegistered != shouldRegister) {
+ if (shouldRegister) {
+ dumpManager.registerCriticalDumpable(dumpManagerName, this)
+ } else {
+ dumpManager.unregisterDumpable(dumpManagerName)
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index e832506..15e0965 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -356,6 +356,12 @@
// TODO(b/278084491): update sysui state for changes on other displays
}
}, mSysUiMainExecutor);
+ mCommandQueue.addCallback(new CommandQueue.Callbacks() {
+ @Override
+ public void enterDesktop(int displayId) {
+ desktopMode.enterDesktop(displayId);
+ }
+ });
}
@VisibleForTesting
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
index d048cbe..36d4d12 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
@@ -18,6 +18,7 @@
import android.testing.TestableLooper
import android.view.View
+import android.view.ViewGroup
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -59,7 +60,9 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
-@TestableLooper.RunWithLooper
+// collectFlow in KeyguardPinBasedInputViewController.onViewAttached calls JavaAdapter.CollectFlow,
+// which calls View.onRepeatWhenAttached, which requires being run on main thread.
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
class KeyguardPinViewControllerTest : SysuiTestCase() {
private lateinit var objectKeyguardPINView: KeyguardPINView
@@ -90,6 +93,8 @@
@Mock private val mEmergencyButtonController: EmergencyButtonController? = null
private val falsingCollector: FalsingCollector = FalsingCollectorFake()
private val keyguardKeyboardInteractor = KeyguardKeyboardInteractor(FakeKeyboardRepository())
+ private val passwordTextViewLayoutParams =
+ ViewGroup.LayoutParams(/* width= */ 0, /* height= */ 0)
@Mock lateinit var postureController: DevicePostureController
@Mock lateinit var mSelectedUserInteractor: SelectedUserInteractor
@@ -104,11 +109,9 @@
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
- Mockito.`when`(mockKeyguardPinView.requireViewById<View>(R.id.bouncer_message_area))
+ `when`(mockKeyguardPinView.requireViewById<View>(R.id.bouncer_message_area))
.thenReturn(keyguardMessageArea)
- Mockito.`when`(
- keyguardMessageAreaControllerFactory.create(any(KeyguardMessageArea::class.java))
- )
+ `when`(keyguardMessageAreaControllerFactory.create(any(KeyguardMessageArea::class.java)))
.thenReturn(keyguardMessageAreaController)
`when`(mockKeyguardPinView.passwordTextViewId).thenReturn(R.id.pinEntry)
`when`(mockKeyguardPinView.findViewById<PasswordTextView>(R.id.pinEntry))
@@ -121,6 +124,7 @@
`when`(mockKeyguardPinView.buttons).thenReturn(arrayOf())
`when`(lockPatternUtils.getPinLength(anyInt())).thenReturn(6)
`when`(featureFlags.isEnabled(Flags.LOCKSCREEN_ENABLE_LANDSCAPE)).thenReturn(false)
+ `when`(passwordTextView.layoutParams).thenReturn(passwordTextViewLayoutParams)
objectKeyguardPINView =
View.inflate(mContext, R.layout.keyguard_pin_view, null)
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
index 4a2554e..9b5364e 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt
@@ -49,7 +49,9 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
-@TestableLooper.RunWithLooper
+// collectFlow in KeyguardPinBasedInputViewController.onViewAttached calls JavaAdapter.CollectFlow,
+// which calls View.onRepeatWhenAttached, which requires being run on main thread.
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
class KeyguardSimPinViewControllerTest : SysuiTestCase() {
private lateinit var simPinView: KeyguardSimPinView
private lateinit var underTest: KeyguardSimPinViewController
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
index 4f46184..e71490c 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt
@@ -43,7 +43,9 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
-@TestableLooper.RunWithLooper
+// collectFlow in KeyguardPinBasedInputViewController.onViewAttached calls JavaAdapter.CollectFlow,
+// which calls View.onRepeatWhenAttached, which requires being run on main thread.
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
class KeyguardSimPukViewControllerTest : SysuiTestCase() {
private lateinit var simPukView: KeyguardSimPukView
private lateinit var underTest: KeyguardSimPukViewController
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java
index 4d3243a..edb910a 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSliceViewControllerTest.java
@@ -22,8 +22,10 @@
import static org.mockito.Mockito.when;
import android.content.pm.PackageManager;
+import android.os.Handler;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import android.view.View;
@@ -57,17 +59,22 @@
private ActivityStarter mActivityStarter;
private FakeDisplayTracker mDisplayTracker = new FakeDisplayTracker(mContext);
private DumpManager mDumpManager = new DumpManager();
-
+ private Handler mHandler;
+ private Handler mBgHandler;
private KeyguardSliceViewController mController;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
+ TestableLooper testableLooper = TestableLooper.get(this);
+ assert testableLooper != null;
+ mHandler = new Handler(testableLooper.getLooper());
+ mBgHandler = new Handler(testableLooper.getLooper());
when(mView.isAttachedToWindow()).thenReturn(true);
when(mView.getContext()).thenReturn(mContext);
- mController = new KeyguardSliceViewController(
- mView, mActivityStarter, mConfigurationController,
- mTunerService, mDumpManager, mDisplayTracker);
+ mController = new KeyguardSliceViewController(mHandler, mBgHandler, mView,
+ mActivityStarter, mConfigurationController, mTunerService, mDumpManager,
+ mDisplayTracker);
mController.setupUri(KeyguardSliceProvider.KEYGUARD_SLICE_URI);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractorImplTest.kt
index 5eda2b2..569e064 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/CredentialInteractorImplTest.kt
@@ -89,6 +89,16 @@
}
}
+ @Test
+ fun testParentProfile() {
+ for (value in listOf(12, 8, 4)) {
+ whenever(userManager.getProfileParent(eq(USER_ID)))
+ .thenReturn(UserInfo(value, "test", 0))
+
+ assertThat(interactor.getParentProfileIdOrSelfId(USER_ID)).isEqualTo(value)
+ }
+ }
+
@Test fun pinCredentialWhenGood() = pinCredential(goodCredential())
@Test fun pinCredentialWhenBad() = pinCredential(badCredential())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
index 1a6da76..915522d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/CustomizationProviderTest.kt
@@ -53,11 +53,9 @@
import com.android.systemui.res.R
import com.android.systemui.settings.UserFileManager
import com.android.systemui.settings.UserTracker
-import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.shared.customization.data.content.CustomizationProviderContract as Contract
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
import com.android.systemui.statusbar.policy.KeyguardStateController
-import com.android.systemui.testKosmos
import com.android.systemui.util.FakeSharedPreferences
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
@@ -103,8 +101,6 @@
private lateinit var underTest: CustomizationProvider
private lateinit var testScope: TestScope
- private val kosmos = testKosmos()
-
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
@@ -189,7 +185,6 @@
},
)
.keyguardInteractor,
- shadeInteractor = kosmos.shadeInteractor,
lockPatternUtils = lockPatternUtils,
keyguardStateController = keyguardStateController,
userTracker = userTracker,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
index 45b2a42..d2a8444 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardQuickAffordanceInteractorParameterizedTest.kt
@@ -46,9 +46,7 @@
import com.android.systemui.settings.FakeUserTracker
import com.android.systemui.settings.UserFileManager
import com.android.systemui.settings.UserTracker
-import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.statusbar.policy.KeyguardStateController
-import com.android.systemui.testKosmos
import com.android.systemui.util.FakeSharedPreferences
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
@@ -244,8 +242,6 @@
private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository
private lateinit var userTracker: UserTracker
- private val kosmos = testKosmos()
-
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
@@ -315,7 +311,6 @@
featureFlags = featureFlags,
)
.keyguardInteractor,
- shadeInteractor = kosmos.shadeInteractor,
lockPatternUtils = lockPatternUtils,
keyguardStateController = keyguardStateController,
userTracker = userTracker,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt
index 08d44c1..aa7f9a9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/ClockSectionTest.kt
@@ -67,12 +67,15 @@
context.resources.getDimensionPixelSize(R.dimen.keyguard_clock_top_margin) +
Utils.getStatusBarHeaderHeightKeyguard(context)
- private val LARGE_CLOCK_TOP =
+ private val LARGE_CLOCK_TOP_WITHOUT_SMARTSPACE =
context.resources.getDimensionPixelSize(R.dimen.status_bar_height) +
context.resources.getDimensionPixelSize(
com.android.systemui.customization.R.dimen.small_clock_padding_top
) +
- context.resources.getDimensionPixelSize(R.dimen.keyguard_smartspace_top_offset) +
+ context.resources.getDimensionPixelSize(R.dimen.keyguard_smartspace_top_offset)
+
+ private val LARGE_CLOCK_TOP =
+ LARGE_CLOCK_TOP_WITHOUT_SMARTSPACE +
SMART_SPACE_DATE_WEATHER_HEIGHT +
ENHANCED_SMART_SPACE_HEIGHT
@@ -81,36 +84,30 @@
com.android.systemui.customization.R.dimen.small_clock_height
)
+ private var DIMENSION_BY_IDENTIFIER_NAME: List<Pair<String, Int>> = listOf()
+
@Before
fun setup() {
+ DIMENSION_BY_IDENTIFIER_NAME =
+ listOf(
+ "date_weather_view_height" to SMART_SPACE_DATE_WEATHER_HEIGHT,
+ "enhanced_smartspace_height" to ENHANCED_SMART_SPACE_HEIGHT,
+ )
+
MockitoAnnotations.initMocks(this)
val remoteResources = mock<Resources>()
- whenever(
- remoteResources.getIdentifier(
- anyString(),
- eq("dimen"),
- anyString(),
- )
- )
- .then { invocation ->
- val name = invocation.arguments[0] as String
- val index = DIMENSION_BY_IDENTIFIER_NAME.indexOfFirst { (key, _) -> key == name }
- if (index == -1) {
- error(
- "No entry for a dimension named \"$name\", please add it to the list above."
- )
- }
- index
- }
- whenever(
- remoteResources.getDimensionPixelSize(
- anyInt(),
- )
- )
- .then { invocation ->
- val id = invocation.arguments[0] as Int
- DIMENSION_BY_IDENTIFIER_NAME[id].second
- }
+ whenever(remoteResources.getIdentifier(anyString(), eq("dimen"), anyString())).then {
+ invocation ->
+ val name = invocation.arguments[0] as String
+ val index = DIMENSION_BY_IDENTIFIER_NAME.indexOfFirst { (key, _) -> key == name }
+ // increment index so that the not-found sentinel value lines up w/ what is
+ // returned by getIdentifier when a resource is not found
+ index + 1
+ }
+ whenever(remoteResources.getDimensionPixelSize(anyInt())).then { invocation ->
+ val id = invocation.arguments[0] as Int
+ DIMENSION_BY_IDENTIFIER_NAME[id - 1].second
+ }
val packageManager = mock<PackageManager>()
whenever(packageManager.getResourcesForApplication(anyString())).thenReturn(remoteResources)
mContext.setMockPackageManager(packageManager)
@@ -159,6 +156,36 @@
}
@Test
+ fun testApplyDefaultConstraints_LargeClock_MissingSmartspace_SplitShade() {
+ DIMENSION_BY_IDENTIFIER_NAME = listOf() // Remove Smartspace from mock
+ setLargeClock(true)
+ setSplitShade(true)
+ val cs = ConstraintSet()
+ underTest.applyDefaultConstraints(cs)
+
+ val expectedLargeClockTopMargin = LARGE_CLOCK_TOP_WITHOUT_SMARTSPACE
+ assertLargeClockTop(cs, expectedLargeClockTopMargin)
+
+ val expectedSmallClockTopMargin = SMALL_CLOCK_TOP_SPLIT_SHADE
+ assertSmallClockTop(cs, expectedSmallClockTopMargin)
+ }
+
+ @Test
+ fun testApplyDefaultConstraints_LargeClock_MissingSmartspace_NonSplitShade() {
+ DIMENSION_BY_IDENTIFIER_NAME = listOf() // Remove Smartspace from mock
+ setLargeClock(true)
+ setSplitShade(false)
+ val cs = ConstraintSet()
+ underTest.applyDefaultConstraints(cs)
+
+ val expectedLargeClockTopMargin = LARGE_CLOCK_TOP_WITHOUT_SMARTSPACE
+ assertLargeClockTop(cs, expectedLargeClockTopMargin)
+
+ val expectedSmallClockTopMargin = SMALL_CLOCK_TOP_NON_SPLIT_SHADE
+ assertSmallClockTop(cs, expectedSmallClockTopMargin)
+ }
+
+ @Test
fun testApplyDefaultConstraints_SmallClock_SplitShade() {
setLargeClock(false)
setSplitShade(true)
@@ -249,10 +276,5 @@
companion object {
private val SMART_SPACE_DATE_WEATHER_HEIGHT = 10
private val ENHANCED_SMART_SPACE_HEIGHT = 11
- private val DIMENSION_BY_IDENTIFIER_NAME =
- listOf(
- "date_weather_view_height" to SMART_SPACE_DATE_WEATHER_HEIGHT,
- "enhanced_smartspace_height" to ENHANCED_SMART_SPACE_HEIGHT,
- )
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
index 7290863..2ec2fe3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
@@ -54,7 +54,6 @@
import com.android.systemui.res.R
import com.android.systemui.settings.UserFileManager
import com.android.systemui.settings.UserTracker
-import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.shared.keyguard.shared.model.KeyguardQuickAffordanceSlots
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper
import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -219,7 +218,6 @@
quickAffordanceInteractor =
KeyguardQuickAffordanceInteractor(
keyguardInteractor = keyguardInteractor,
- shadeInteractor = kosmos.shadeInteractor,
lockPatternUtils = lockPatternUtils,
keyguardStateController = keyguardStateController,
userTracker = userTracker,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
index eded4dcb..18a34ba 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardQuickAffordancesCombinedViewModelTest.kt
@@ -264,14 +264,12 @@
whenever(lockscreenToPrimaryBouncerTransitionViewModel.shortcutsAlpha)
.thenReturn(emptyFlow())
whenever(shadeInteractor.qsExpansion).thenReturn(intendedShadeAlphaMutableStateFlow)
- whenever(shadeInteractor.anyExpansion).thenReturn(MutableStateFlow(0f))
underTest =
KeyguardQuickAffordancesCombinedViewModel(
quickAffordanceInteractor =
KeyguardQuickAffordanceInteractor(
keyguardInteractor = keyguardInteractor,
- shadeInteractor = shadeInteractor,
lockPatternUtils = lockPatternUtils,
keyguardStateController = keyguardStateController,
userTracker = userTracker,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt
index 83f8f18..0683321 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileNewImplTest.kt
@@ -30,7 +30,7 @@
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.QsEventLogger
import com.android.systemui.qs.logging.QSLogger
-import com.android.systemui.qs.tiles.dialog.InternetDialogFactory
+import com.android.systemui.qs.tiles.dialog.InternetDialogManager
import com.android.systemui.statusbar.connectivity.AccessPointController
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
import com.android.systemui.statusbar.pipeline.ethernet.domain.EthernetInteractor
@@ -83,7 +83,7 @@
@Mock private lateinit var sbStateController: StatusBarStateController
@Mock private lateinit var activityStarter: ActivityStarter
@Mock private lateinit var logger: QSLogger
- @Mock private lateinit var dialogFactory: InternetDialogFactory
+ @Mock private lateinit var dialogManager: InternetDialogManager
@Mock private lateinit var accessPointController: AccessPointController
@Before
@@ -118,7 +118,7 @@
activityStarter,
logger,
viewModel,
- dialogFactory,
+ dialogManager,
accessPointController
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileTest.java
index c1f1964..288facc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/InternetTileTest.java
@@ -17,7 +17,6 @@
package com.android.systemui.qs.tiles;
import static com.google.common.truth.Truth.assertThat;
-
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -38,7 +37,7 @@
import com.android.systemui.qs.QsEventLogger;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSTileImpl;
-import com.android.systemui.qs.tiles.dialog.InternetDialogFactory;
+import com.android.systemui.qs.tiles.dialog.InternetDialogManager;
import com.android.systemui.statusbar.connectivity.AccessPointController;
import com.android.systemui.statusbar.connectivity.IconState;
import com.android.systemui.statusbar.connectivity.NetworkController;
@@ -63,7 +62,7 @@
@Mock
private AccessPointController mAccessPointController;
@Mock
- private InternetDialogFactory mInternetDialogFactory;
+ private InternetDialogManager mInternetDialogManager;
@Mock
private QsEventLogger mUiEventLogger;
@@ -89,7 +88,7 @@
mock(QSLogger.class),
mNetworkController,
mAccessPointController,
- mInternetDialogFactory
+ mInternetDialogManager
);
mTile.initialize();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java
similarity index 99%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java
index 077ec4b..74f50df 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateControllerTest.java
@@ -5,7 +5,6 @@
import static android.telephony.SignalStrength.NUM_SIGNAL_STRENGTH_BINS;
import static android.telephony.SignalStrength.SIGNAL_STRENGTH_GREAT;
import static android.telephony.SignalStrength.SIGNAL_STRENGTH_POOR;
-
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.settingslib.wifi.WifiUtils.getHotspotIconResource;
import static com.android.systemui.qs.tiles.dialog.InternetDialogController.TOAST_PARAMS_HORIZONTAL_WEIGHT;
@@ -13,9 +12,7 @@
import static com.android.wifitrackerlib.WifiEntry.WIFI_LEVEL_MAX;
import static com.android.wifitrackerlib.WifiEntry.WIFI_LEVEL_MIN;
import static com.android.wifitrackerlib.WifiEntry.WIFI_LEVEL_UNREACHABLE;
-
import static com.google.common.truth.Truth.assertThat;
-
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
@@ -101,7 +98,7 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
-public class InternetDialogControllerTest extends SysuiTestCase {
+public class InternetDialogDelegateControllerTest extends SysuiTestCase {
private static final int SUB_ID = 1;
private static final int SUB_ID2 = 2;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateTest.java
similarity index 80%
rename from packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateTest.java
index c9e6274..db9f5cf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogDelegateTest.java
@@ -1,9 +1,7 @@
package com.android.systemui.qs.tiles.dialog;
import static com.android.systemui.qs.tiles.dialog.InternetDialogController.MAX_WIFI_ENTRY_COUNT;
-
import static com.google.common.truth.Truth.assertThat;
-
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
@@ -34,6 +32,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.DialogTransitionAnimator;
import com.android.systemui.res.R;
+import com.android.systemui.statusbar.phone.SystemUIDialog;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
@@ -55,7 +54,7 @@
@SmallTest
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
-public class InternetDialogTest extends SysuiTestCase {
+public class InternetDialogDelegateTest extends SysuiTestCase {
private static final String MOBILE_NETWORK_TITLE = "Mobile Title";
private static final String MOBILE_NETWORK_SUMMARY = "Mobile Summary";
@@ -78,9 +77,13 @@
private KeyguardStateController mKeyguard;
@Mock
private DialogTransitionAnimator mDialogTransitionAnimator;
+ @Mock
+ private SystemUIDialog.Factory mSystemUIDialogFactory;
+ @Mock
+ private SystemUIDialog mSystemUIDialog;
private FakeExecutor mBgExecutor = new FakeExecutor(new FakeSystemClock());
- private InternetDialog mInternetDialog;
+ private InternetDialogDelegate mInternetDialogDelegate;
private View mDialogView;
private View mSubTitle;
private LinearLayout mEthernet;
@@ -117,21 +120,30 @@
.spyStatic(WifiEnterpriseRestrictionUtils.class)
.startMocking();
when(WifiEnterpriseRestrictionUtils.isChangeWifiStateAllowed(mContext)).thenReturn(true);
-
+ when(mSystemUIDialogFactory.create(any(SystemUIDialog.Delegate.class)))
+ .thenReturn(mSystemUIDialog);
createInternetDialog();
}
private void createInternetDialog() {
- mInternetDialog = new InternetDialog(mContext, mock(InternetDialogFactory.class),
- mInternetDialogController, true, true, true, mock(UiEventLogger.class),
- mDialogTransitionAnimator, mHandler,
- mBgExecutor, mKeyguard);
- mInternetDialog.mAdapter = mInternetAdapter;
- mInternetDialog.mConnectedWifiEntry = mInternetWifiEntry;
- mInternetDialog.mWifiEntriesCount = mWifiEntries.size();
- mInternetDialog.show();
+ mInternetDialogDelegate = new InternetDialogDelegate(
+ mContext,
+ mock(InternetDialogManager.class),
+ mInternetDialogController,
+ true,
+ true,
+ true,
+ mock(UiEventLogger.class),
+ mDialogTransitionAnimator,
+ mHandler,
+ mBgExecutor,
+ mKeyguard,
+ mSystemUIDialogFactory);
+ mInternetDialogDelegate.mAdapter = mInternetAdapter;
+ mInternetDialogDelegate.mConnectedWifiEntry = mInternetWifiEntry;
+ mInternetDialogDelegate.mWifiEntriesCount = mWifiEntries.size();
- mDialogView = mInternetDialog.mDialogView;
+ mDialogView = mInternetDialogDelegate.mDialogView;
mSubTitle = mDialogView.requireViewById(R.id.internet_dialog_subtitle);
mEthernet = mDialogView.requireViewById(R.id.ethernet_layout);
mMobileDataLayout = mDialogView.requireViewById(R.id.mobile_network_layout);
@@ -148,7 +160,7 @@
@After
public void tearDown() {
- mInternetDialog.dismissDialog();
+ mInternetDialogDelegate.dismissDialog();
mMockitoSession.finishMocking();
}
@@ -160,9 +172,9 @@
@Test
public void hideWifiViews_WifiViewsGone() {
- mInternetDialog.hideWifiViews();
+ mInternetDialogDelegate.hideWifiViews();
- assertThat(mInternetDialog.mIsProgressBarVisible).isFalse();
+ assertThat(mInternetDialogDelegate.mIsProgressBarVisible).isFalse();
assertThat(mWifiToggle.getVisibility()).isEqualTo(View.GONE);
assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
assertThat(mWifiList.getVisibility()).isEqualTo(View.GONE);
@@ -173,7 +185,7 @@
public void updateDialog_withApmOn_internetDialogSubTitleGone() {
when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
- mInternetDialog.updateDialog(true);
+ mInternetDialogDelegate.updateDialog(true);
assertThat(mSubTitle.getVisibility()).isEqualTo(View.VISIBLE);
}
@@ -182,7 +194,7 @@
public void updateDialog_withApmOff_internetDialogSubTitleVisible() {
when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(false);
- mInternetDialog.updateDialog(true);
+ mInternetDialogDelegate.updateDialog(true);
assertThat(mSubTitle.getVisibility()).isEqualTo(View.VISIBLE);
}
@@ -192,7 +204,7 @@
when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(false);
when(mInternetDialogController.hasEthernet()).thenReturn(true);
- mInternetDialog.updateDialog(true);
+ mInternetDialogDelegate.updateDialog(true);
assertThat(mEthernet.getVisibility()).isEqualTo(View.VISIBLE);
}
@@ -202,7 +214,7 @@
when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(false);
when(mInternetDialogController.hasEthernet()).thenReturn(false);
- mInternetDialog.updateDialog(true);
+ mInternetDialogDelegate.updateDialog(true);
assertThat(mEthernet.getVisibility()).isEqualTo(View.GONE);
}
@@ -212,7 +224,7 @@
when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
when(mInternetDialogController.hasEthernet()).thenReturn(true);
- mInternetDialog.updateDialog(true);
+ mInternetDialogDelegate.updateDialog(true);
assertThat(mEthernet.getVisibility()).isEqualTo(View.VISIBLE);
}
@@ -222,7 +234,7 @@
when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
when(mInternetDialogController.hasEthernet()).thenReturn(false);
- mInternetDialog.updateDialog(true);
+ mInternetDialogDelegate.updateDialog(true);
assertThat(mEthernet.getVisibility()).isEqualTo(View.GONE);
}
@@ -234,7 +246,7 @@
when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(false);
when(mInternetDialogController.hasActiveSubId()).thenReturn(false);
- mInternetDialog.updateDialog(true);
+ mInternetDialogDelegate.updateDialog(true);
assertThat(mMobileDataLayout.getVisibility()).isEqualTo(View.GONE);
}
@@ -246,7 +258,7 @@
when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
when(mInternetDialogController.isWifiEnabled()).thenReturn(false);
- mInternetDialog.updateDialog(true);
+ mInternetDialogDelegate.updateDialog(true);
assertThat(mMobileDataLayout.getVisibility()).isEqualTo(View.GONE);
@@ -255,7 +267,7 @@
when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
when(mInternetDialogController.isWifiEnabled()).thenReturn(true);
- mInternetDialog.updateDialog(true);
+ mInternetDialogDelegate.updateDialog(true);
assertThat(mMobileDataLayout.getVisibility()).isEqualTo(View.VISIBLE);
}
@@ -265,7 +277,7 @@
when(mInternetDialogController.isCarrierNetworkActive()).thenReturn(false);
when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
- mInternetDialog.updateDialog(true);
+ mInternetDialogDelegate.updateDialog(true);
assertThat(mMobileDataLayout.getVisibility()).isEqualTo(View.GONE);
}
@@ -274,10 +286,10 @@
public void updateDialog_apmOnAndWifiOnHasCarrierNetwork_showAirplaneSummary() {
when(mInternetDialogController.isCarrierNetworkActive()).thenReturn(true);
when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
- mInternetDialog.mConnectedWifiEntry = null;
+ mInternetDialogDelegate.mConnectedWifiEntry = null;
doReturn(false).when(mInternetDialogController).activeNetworkIsCellular();
- mInternetDialog.updateDialog(true);
+ mInternetDialogDelegate.updateDialog(true);
assertThat(mMobileDataLayout.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mAirplaneModeSummaryText.getVisibility()).isEqualTo(View.VISIBLE);
@@ -287,10 +299,10 @@
public void updateDialog_apmOffAndWifiOnHasCarrierNetwork_notShowApmSummary() {
when(mInternetDialogController.isCarrierNetworkActive()).thenReturn(true);
when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(false);
- mInternetDialog.mConnectedWifiEntry = null;
+ mInternetDialogDelegate.mConnectedWifiEntry = null;
doReturn(false).when(mInternetDialogController).activeNetworkIsCellular();
- mInternetDialog.updateDialog(true);
+ mInternetDialogDelegate.updateDialog(true);
assertThat(mAirplaneModeSummaryText.getVisibility()).isEqualTo(View.GONE);
}
@@ -300,7 +312,7 @@
when(mInternetDialogController.isCarrierNetworkActive()).thenReturn(true);
when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(false);
- mInternetDialog.updateDialog(true);
+ mInternetDialogDelegate.updateDialog(true);
assertThat(mAirplaneModeSummaryText.getVisibility()).isEqualTo(View.GONE);
}
@@ -310,7 +322,7 @@
when(mInternetDialogController.isCarrierNetworkActive()).thenReturn(false);
when(mInternetDialogController.isAirplaneModeEnabled()).thenReturn(true);
- mInternetDialog.updateDialog(true);
+ mInternetDialogDelegate.updateDialog(true);
assertThat(mAirplaneModeSummaryText.getVisibility()).isEqualTo(View.GONE);
}
@@ -322,7 +334,7 @@
when(mInternetDialogController.isMobileDataEnabled()).thenReturn(true);
mMobileToggleSwitch.setChecked(false);
- mInternetDialog.updateDialog(true);
+ mInternetDialogDelegate.updateDialog(true);
assertThat(mMobileToggleSwitch.isChecked()).isTrue();
}
@@ -334,20 +346,20 @@
when(mInternetDialogController.isMobileDataEnabled()).thenReturn(false);
mMobileToggleSwitch.setChecked(false);
- mInternetDialog.updateDialog(true);
+ mInternetDialogDelegate.updateDialog(true);
assertThat(mMobileToggleSwitch.isChecked()).isFalse();
}
@Test
public void updateDialog_wifiOnAndHasInternetWifi_showConnectedWifi() {
- mInternetDialog.dismissDialog();
+ mInternetDialogDelegate.dismissDialog();
doReturn(true).when(mInternetDialogController).hasActiveSubId();
createInternetDialog();
// The preconditions WiFi ON and Internet WiFi are already in setUp()
doReturn(false).when(mInternetDialogController).activeNetworkIsCellular();
- mInternetDialog.updateDialog(true);
+ mInternetDialogDelegate.updateDialog(true);
assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.VISIBLE);
LinearLayout secondaryLayout = mDialogView.requireViewById(
@@ -358,10 +370,10 @@
@Test
public void updateDialog_wifiOnAndNoConnectedWifi_hideConnectedWifi() {
// The precondition WiFi ON is already in setUp()
- mInternetDialog.mConnectedWifiEntry = null;
+ mInternetDialogDelegate.mConnectedWifiEntry = null;
doReturn(false).when(mInternetDialogController).activeNetworkIsCellular();
- mInternetDialog.updateDialog(false);
+ mInternetDialogDelegate.updateDialog(false);
assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
}
@@ -369,10 +381,10 @@
@Test
public void updateDialog_wifiOnAndNoWifiEntry_showWifiListAndSeeAllArea() {
// The precondition WiFi ON is already in setUp()
- mInternetDialog.mConnectedWifiEntry = null;
- mInternetDialog.mWifiEntriesCount = 0;
+ mInternetDialogDelegate.mConnectedWifiEntry = null;
+ mInternetDialogDelegate.mWifiEntriesCount = 0;
- mInternetDialog.updateDialog(false);
+ mInternetDialogDelegate.updateDialog(false);
assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
// Show a blank block to fix the dialog height even if there is no WiFi list
@@ -384,10 +396,10 @@
@Test
public void updateDialog_wifiOnAndOneWifiEntry_showWifiListAndSeeAllArea() {
// The precondition WiFi ON is already in setUp()
- mInternetDialog.mConnectedWifiEntry = null;
- mInternetDialog.mWifiEntriesCount = 1;
+ mInternetDialogDelegate.mConnectedWifiEntry = null;
+ mInternetDialogDelegate.mWifiEntriesCount = 1;
- mInternetDialog.updateDialog(false);
+ mInternetDialogDelegate.updateDialog(false);
assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
// Show a blank block to fix the dialog height even if there is no WiFi list
@@ -399,9 +411,9 @@
@Test
public void updateDialog_wifiOnAndHasConnectedWifi_showAllWifiAndSeeAllArea() {
// The preconditions WiFi ON and WiFi entries are already in setUp()
- mInternetDialog.mWifiEntriesCount = 0;
+ mInternetDialogDelegate.mWifiEntriesCount = 0;
- mInternetDialog.updateDialog(false);
+ mInternetDialogDelegate.updateDialog(false);
assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.VISIBLE);
// Show a blank block to fix the dialog height even if there is no WiFi list
@@ -413,11 +425,11 @@
@Test
public void updateDialog_wifiOnAndHasMaxWifiList_showWifiListAndSeeAll() {
// The preconditions WiFi ON and WiFi entries are already in setUp()
- mInternetDialog.mConnectedWifiEntry = null;
- mInternetDialog.mWifiEntriesCount = MAX_WIFI_ENTRY_COUNT;
- mInternetDialog.mHasMoreWifiEntries = true;
+ mInternetDialogDelegate.mConnectedWifiEntry = null;
+ mInternetDialogDelegate.mWifiEntriesCount = MAX_WIFI_ENTRY_COUNT;
+ mInternetDialogDelegate.mHasMoreWifiEntries = true;
- mInternetDialog.updateDialog(false);
+ mInternetDialogDelegate.updateDialog(false);
assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.GONE);
assertThat(mWifiList.getVisibility()).isEqualTo(View.VISIBLE);
@@ -428,10 +440,10 @@
@Test
public void updateDialog_wifiOnAndHasBothWifiEntry_showBothWifiEntryAndSeeAll() {
// The preconditions WiFi ON and WiFi entries are already in setUp()
- mInternetDialog.mWifiEntriesCount = MAX_WIFI_ENTRY_COUNT - 1;
- mInternetDialog.mHasMoreWifiEntries = true;
+ mInternetDialogDelegate.mWifiEntriesCount = MAX_WIFI_ENTRY_COUNT - 1;
+ mInternetDialogDelegate.mHasMoreWifiEntries = true;
- mInternetDialog.updateDialog(false);
+ mInternetDialogDelegate.updateDialog(false);
assertThat(mConnectedWifi.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mWifiList.getVisibility()).isEqualTo(View.VISIBLE);
@@ -443,9 +455,9 @@
public void updateDialog_deviceLockedAndNoConnectedWifi_showWifiToggle() {
// The preconditions WiFi entries are already in setUp()
when(mInternetDialogController.isDeviceLocked()).thenReturn(true);
- mInternetDialog.mConnectedWifiEntry = null;
+ mInternetDialogDelegate.mConnectedWifiEntry = null;
- mInternetDialog.updateDialog(false);
+ mInternetDialogDelegate.updateDialog(false);
// Show WiFi Toggle without background
assertThat(mWifiToggle.getVisibility()).isEqualTo(View.VISIBLE);
@@ -461,7 +473,7 @@
// The preconditions WiFi ON and WiFi entries are already in setUp()
when(mInternetDialogController.isDeviceLocked()).thenReturn(true);
- mInternetDialog.updateDialog(false);
+ mInternetDialogDelegate.updateDialog(false);
// Show WiFi Toggle with highlight background
assertThat(mWifiToggle.getVisibility()).isEqualTo(View.VISIBLE);
@@ -474,11 +486,11 @@
@Test
public void updateDialog_disallowChangeWifiState_disableWifiSwitch() {
- mInternetDialog.dismissDialog();
+ mInternetDialogDelegate.dismissDialog();
when(WifiEnterpriseRestrictionUtils.isChangeWifiStateAllowed(mContext)).thenReturn(false);
createInternetDialog();
- mInternetDialog.updateDialog(false);
+ mInternetDialogDelegate.updateDialog(false);
// Disable Wi-Fi switch and show restriction message in summary.
assertThat(mWifiToggleSwitch.isEnabled()).isFalse();
@@ -488,11 +500,11 @@
@Test
public void updateDialog_allowChangeWifiState_enableWifiSwitch() {
- mInternetDialog.dismissDialog();
+ mInternetDialogDelegate.dismissDialog();
when(WifiEnterpriseRestrictionUtils.isChangeWifiStateAllowed(mContext)).thenReturn(true);
createInternetDialog();
- mInternetDialog.updateDialog(false);
+ mInternetDialogDelegate.updateDialog(false);
// Enable Wi-Fi switch and hide restriction message in summary.
assertThat(mWifiToggleSwitch.isEnabled()).isTrue();
@@ -501,14 +513,14 @@
@Test
public void updateDialog_showSecondaryDataSub() {
- mInternetDialog.dismissDialog();
+ mInternetDialogDelegate.dismissDialog();
doReturn(1).when(mInternetDialogController).getActiveAutoSwitchNonDdsSubId();
doReturn(true).when(mInternetDialogController).hasActiveSubId();
doReturn(false).when(mInternetDialogController).isAirplaneModeEnabled();
createInternetDialog();
clearInvocations(mInternetDialogController);
- mInternetDialog.updateDialog(true);
+ mInternetDialogDelegate.updateDialog(true);
LinearLayout primaryLayout = mDialogView.requireViewById(
R.id.mobile_network_layout);
@@ -523,7 +535,7 @@
ArgumentCaptor<AlertDialog> dialogArgumentCaptor =
ArgumentCaptor.forClass(AlertDialog.class);
verify(mDialogTransitionAnimator).showFromDialog(dialogArgumentCaptor.capture(),
- eq(mInternetDialog), eq(null), eq(false));
+ eq(mSystemUIDialog), eq(null), eq(false));
AlertDialog dialog = dialogArgumentCaptor.getValue();
dialog.show();
dialog.getButton(DialogInterface.BUTTON_POSITIVE).performClick();
@@ -541,7 +553,7 @@
public void updateDialog_wifiOn_hideWifiScanNotify() {
// The preconditions WiFi ON and WiFi entries are already in setUp()
- mInternetDialog.updateDialog(false);
+ mInternetDialogDelegate.updateDialog(false);
assertThat(mWifiScanNotify.getVisibility()).isEqualTo(View.GONE);
}
@@ -551,7 +563,7 @@
when(mInternetDialogController.isWifiEnabled()).thenReturn(false);
when(mInternetDialogController.isWifiScanEnabled()).thenReturn(false);
- mInternetDialog.updateDialog(false);
+ mInternetDialogDelegate.updateDialog(false);
assertThat(mWifiScanNotify.getVisibility()).isEqualTo(View.GONE);
}
@@ -562,7 +574,7 @@
when(mInternetDialogController.isWifiScanEnabled()).thenReturn(true);
when(mInternetDialogController.isDeviceLocked()).thenReturn(true);
- mInternetDialog.updateDialog(false);
+ mInternetDialogDelegate.updateDialog(false);
assertThat(mWifiScanNotify.getVisibility()).isEqualTo(View.GONE);
}
@@ -573,7 +585,7 @@
when(mInternetDialogController.isWifiScanEnabled()).thenReturn(true);
when(mInternetDialogController.isDeviceLocked()).thenReturn(false);
- mInternetDialog.updateDialog(false);
+ mInternetDialogDelegate.updateDialog(false);
assertThat(mWifiScanNotify.getVisibility()).isEqualTo(View.VISIBLE);
TextView wifiScanNotifyText = mDialogView.requireViewById(R.id.wifi_scan_notify_text);
@@ -586,7 +598,7 @@
when(mInternetDialogController.isWifiEnabled()).thenReturn(false);
mWifiToggleSwitch.setChecked(true);
- mInternetDialog.updateDialog(false);
+ mInternetDialogDelegate.updateDialog(false);
assertThat(mWifiToggleSwitch.isChecked()).isFalse();
}
@@ -596,7 +608,7 @@
when(mInternetDialogController.isWifiEnabled()).thenReturn(true);
mWifiToggleSwitch.setChecked(false);
- mInternetDialog.updateDialog(false);
+ mInternetDialogDelegate.updateDialog(false);
assertThat(mWifiToggleSwitch.isChecked()).isTrue();
}
@@ -611,20 +623,20 @@
@Test
public void onWifiScan_isScanTrue_setProgressBarVisibleTrue() {
- mInternetDialog.mIsProgressBarVisible = false;
+ mInternetDialogDelegate.mIsProgressBarVisible = false;
- mInternetDialog.onWifiScan(true);
+ mInternetDialogDelegate.onWifiScan(true);
- assertThat(mInternetDialog.mIsProgressBarVisible).isTrue();
+ assertThat(mInternetDialogDelegate.mIsProgressBarVisible).isTrue();
}
@Test
public void onWifiScan_isScanFalse_setProgressBarVisibleFalse() {
- mInternetDialog.mIsProgressBarVisible = true;
+ mInternetDialogDelegate.mIsProgressBarVisible = true;
- mInternetDialog.onWifiScan(false);
+ mInternetDialogDelegate.onWifiScan(false);
- assertThat(mInternetDialog.mIsProgressBarVisible).isFalse();
+ assertThat(mInternetDialogDelegate.mIsProgressBarVisible).isFalse();
}
@Test
@@ -633,42 +645,47 @@
// Then the maximum count is equal to MAX_WIFI_ENTRY_COUNT.
setNetworkVisible(false, false, false);
- assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT);
+ assertThat(mInternetDialogDelegate.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT);
// If the Connected Wi-Fi is displayed then reduce one of the Wi-Fi list max count.
setNetworkVisible(false, false, true);
- assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT - 1);
+ assertThat(mInternetDialogDelegate.getWifiListMaxCount())
+ .isEqualTo(MAX_WIFI_ENTRY_COUNT - 1);
// Only one of Ethernet, MobileData is displayed.
// Then the maximum count is equal to MAX_WIFI_ENTRY_COUNT.
setNetworkVisible(true, false, false);
- assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT);
+ assertThat(mInternetDialogDelegate.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT);
setNetworkVisible(false, true, false);
- assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT);
+ assertThat(mInternetDialogDelegate.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT);
// If the Connected Wi-Fi is displayed then reduce one of the Wi-Fi list max count.
setNetworkVisible(true, false, true);
- assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT - 1);
+ assertThat(mInternetDialogDelegate.getWifiListMaxCount())
+ .isEqualTo(MAX_WIFI_ENTRY_COUNT - 1);
setNetworkVisible(false, true, true);
- assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT - 1);
+ assertThat(mInternetDialogDelegate.getWifiListMaxCount())
+ .isEqualTo(MAX_WIFI_ENTRY_COUNT - 1);
// Both of Ethernet, MobileData, ConnectedWiFi is displayed.
// Then the maximum count is equal to MAX_WIFI_ENTRY_COUNT - 1.
setNetworkVisible(true, true, false);
- assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT - 1);
+ assertThat(mInternetDialogDelegate.getWifiListMaxCount())
+ .isEqualTo(MAX_WIFI_ENTRY_COUNT - 1);
// If the Connected Wi-Fi is displayed then reduce one of the Wi-Fi list max count.
setNetworkVisible(true, true, true);
- assertThat(mInternetDialog.getWifiListMaxCount()).isEqualTo(MAX_WIFI_ENTRY_COUNT - 2);
+ assertThat(mInternetDialogDelegate.getWifiListMaxCount())
+ .isEqualTo(MAX_WIFI_ENTRY_COUNT - 2);
}
@Test
@@ -676,9 +693,9 @@
when(mInternetDialogController.getConfiguratorQrCodeGeneratorIntentOrNull(any()))
.thenReturn(null);
- mInternetDialog.updateDialog(false);
+ mInternetDialogDelegate.updateDialog(false);
- assertThat(mInternetDialog.mShareWifiButton.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mInternetDialogDelegate.mShareWifiButton.getVisibility()).isEqualTo(View.GONE);
}
@Test
@@ -686,9 +703,10 @@
when(mInternetDialogController.getConfiguratorQrCodeGeneratorIntentOrNull(any()))
.thenReturn(new Intent());
- mInternetDialog.updateDialog(false);
+ mInternetDialogDelegate.updateDialog(false);
- assertThat(mInternetDialog.mShareWifiButton.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mInternetDialogDelegate.mShareWifiButton.getVisibility())
+ .isEqualTo(View.VISIBLE);
}
private void setNetworkVisible(boolean ethernetVisible, boolean mobileDataVisible,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index dddd424..db455cb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -96,6 +96,7 @@
import com.android.systemui.fragments.FragmentService;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.KeyguardViewConfigurator;
+import com.android.systemui.keyguard.data.repository.FakeKeyguardClockRepository;
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository;
import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor;
@@ -103,6 +104,7 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.keyguard.domain.interactor.NaturalScrollingSettingObserver;
+import com.android.systemui.keyguard.ui.view.KeyguardRootView;
import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingLockscreenHostedTransitionViewModel;
import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel;
@@ -280,6 +282,8 @@
@Mock protected UiEventLogger mUiEventLogger;
@Mock protected LockIconViewController mLockIconViewController;
@Mock protected KeyguardViewConfigurator mKeyguardViewConfigurator;
+ @Mock protected KeyguardRootView mKeyguardRootView;
+ @Mock protected View mKeyguardRootViewChild;
@Mock protected KeyguardMediaController mKeyguardMediaController;
@Mock protected NavigationModeController mNavigationModeController;
@Mock protected NavigationBarController mNavigationBarController;
@@ -351,6 +355,7 @@
protected KeyguardBottomAreaInteractor mKeyguardBottomAreaInteractor;
protected KeyguardClockInteractor mKeyguardClockInteractor;
protected FakeKeyguardRepository mFakeKeyguardRepository;
+ protected FakeKeyguardClockRepository mFakeKeyguardClockRepository;
protected KeyguardInteractor mKeyguardInteractor;
protected ShadeAnimationInteractor mShadeAnimationInteractor;
protected KosmosJavaAdapter mKosmos = new KosmosJavaAdapter(this);
@@ -389,12 +394,15 @@
mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_KEYGUARD_BOTTOM_AREA_REFACTOR);
mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT);
+ mSetFlagsRule.disableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
mMainDispatcher = getMainDispatcher();
KeyguardInteractorFactory.WithDependencies keyguardInteractorDeps =
KeyguardInteractorFactory.create();
mFakeKeyguardRepository = keyguardInteractorDeps.getRepository();
mKeyguardBottomAreaInteractor = new KeyguardBottomAreaInteractor(mFakeKeyguardRepository);
+ mFakeKeyguardClockRepository = new FakeKeyguardClockRepository();
+ mKeyguardClockInteractor = new KeyguardClockInteractor(mFakeKeyguardClockRepository);
mKeyguardInteractor = keyguardInteractorDeps.getKeyguardInteractor();
mShadeRepository = new FakeShadeRepository();
mShadeAnimationInteractor = new ShadeAnimationInteractorLegacyImpl(
@@ -835,6 +843,10 @@
when(mNotificationStackScrollLayoutController.getBottom()).thenReturn(stackBottom);
when(mLockIconViewController.getTop()).thenReturn((float) (stackBottom - lockIconPadding));
+ when(mKeyguardRootViewChild.getTop()).thenReturn((int) (stackBottom - lockIconPadding));
+ when(mKeyguardRootView.findViewById(anyInt())).thenReturn(mKeyguardRootViewChild);
+ when(mKeyguardViewConfigurator.getKeyguardRootView()).thenReturn(mKeyguardRootView);
+
when(mResources.getDimensionPixelSize(R.dimen.keyguard_indication_bottom_padding))
.thenReturn(indicationPadding);
mNotificationPanelViewController.loadDimens();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
index 059053c..d24fe1b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
@@ -203,6 +203,10 @@
when(mNotificationStackScrollLayoutController.getShelfHeight()).thenReturn(5);
assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
.isEqualTo(5);
+
+ mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
+ assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
+ .isEqualTo(5);
}
@Test
@@ -216,6 +220,10 @@
when(mNotificationStackScrollLayoutController.getShelfHeight()).thenReturn(5);
assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
.isEqualTo(0);
+
+ mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
+ assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
+ .isEqualTo(0);
}
@Test
@@ -229,6 +237,10 @@
when(mNotificationStackScrollLayoutController.getShelfHeight()).thenReturn(5);
assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
.isEqualTo(0);
+
+ mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
+ assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
+ .isEqualTo(0);
}
@Test
@@ -242,6 +254,10 @@
when(mNotificationStackScrollLayoutController.getShelfHeight()).thenReturn(5);
assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
.isEqualTo(2);
+
+ mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
+ assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
+ .isEqualTo(2);
}
@Test
@@ -255,6 +271,10 @@
when(mNotificationStackScrollLayoutController.getShelfHeight()).thenReturn(5);
assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
.isEqualTo(0);
+
+ mSetFlagsRule.enableFlags(com.android.systemui.Flags.FLAG_DEVICE_ENTRY_UDFPS_REFACTOR);
+ assertThat(mNotificationPanelViewController.getVerticalSpaceForLockscreenShelf())
+ .isEqualTo(0);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
index ab5e51c..59fe813 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
@@ -207,7 +207,7 @@
@Test
fun testDragDownHelperCalledWhenDraggingDown() =
testScope.runTest {
- mSetFlagsRule.disableFlags(AConfigFlags.FLAG_KEYGUARD_SHADE_MIGRATION_NSSL)
+ mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
whenever(dragDownHelper.isDraggingDown).thenReturn(true)
val now = SystemClock.elapsedRealtime()
val ev = MotionEvent.obtain(now, now, MotionEvent.ACTION_UP, 0f, 0f, 0 /* meta */)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
index 4cc1234..81d0e06 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerLegacyTest.kt
@@ -27,7 +27,7 @@
import androidx.constraintlayout.widget.ConstraintSet
import androidx.test.filters.SmallTest
import com.android.systemui.Flags as AConfigFlags
-import com.android.systemui.Flags.FLAG_CENTRALIZED_STATUS_BAR_DIMENS_REFACTOR
+import com.android.systemui.Flags.FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX
import com.android.systemui.SysuiTestCase
import com.android.systemui.fragments.FragmentHostManager
import com.android.systemui.fragments.FragmentService
@@ -167,7 +167,7 @@
fun testLargeScreen_updateResources_refactorFlagOff_splitShadeHeightIsSetBasedOnResource() {
val headerResourceHeight = 20
val headerHelperHeight = 30
- mSetFlagsRule.disableFlags(FLAG_CENTRALIZED_STATUS_BAR_DIMENS_REFACTOR)
+ mSetFlagsRule.disableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight())
.thenReturn(headerHelperHeight)
overrideResource(R.bool.config_use_large_screen_shade_header, true)
@@ -190,7 +190,7 @@
fun testLargeScreen_updateResources_refactorFlagOn_splitShadeHeightIsSetBasedOnHelper() {
val headerResourceHeight = 20
val headerHelperHeight = 30
- mSetFlagsRule.enableFlags(FLAG_CENTRALIZED_STATUS_BAR_DIMENS_REFACTOR)
+ mSetFlagsRule.enableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight())
.thenReturn(headerHelperHeight)
overrideResource(R.bool.config_use_large_screen_shade_header, true)
@@ -401,7 +401,7 @@
@Test
fun testSplitShadeLayout_isAlignedToGuideline() {
- mSetFlagsRule.disableFlags(AConfigFlags.FLAG_KEYGUARD_SHADE_MIGRATION_NSSL)
+ mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
enableSplitShade()
underTest.updateResources()
assertThat(getConstraintSetLayout(R.id.qs_frame).endToEnd).isEqualTo(R.id.qs_edge_guideline)
@@ -411,7 +411,7 @@
@Test
fun testSinglePaneLayout_childrenHaveEqualMargins() {
- mSetFlagsRule.disableFlags(AConfigFlags.FLAG_KEYGUARD_SHADE_MIGRATION_NSSL)
+ mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
disableSplitShade()
underTest.updateResources()
val qsStartMargin = getConstraintSetLayout(R.id.qs_frame).startMargin
@@ -428,7 +428,7 @@
@Test
fun testSplitShadeLayout_childrenHaveInsideMarginsOfZero() {
- mSetFlagsRule.disableFlags(AConfigFlags.FLAG_KEYGUARD_SHADE_MIGRATION_NSSL)
+ mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
enableSplitShade()
underTest.updateResources()
assertThat(getConstraintSetLayout(R.id.qs_frame).endMargin).isEqualTo(0)
@@ -446,8 +446,8 @@
@Test
fun testLargeScreenLayout_refactorFlagOff_qsAndNotifsTopMarginIsOfHeaderHeightResource() {
- mSetFlagsRule.disableFlags(FLAG_CENTRALIZED_STATUS_BAR_DIMENS_REFACTOR)
- mSetFlagsRule.disableFlags(AConfigFlags.FLAG_KEYGUARD_SHADE_MIGRATION_NSSL)
+ mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
+ mSetFlagsRule.disableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
setLargeScreen()
val largeScreenHeaderResourceHeight = 100
val largeScreenHeaderHelperHeight = 200
@@ -469,8 +469,8 @@
@Test
fun testLargeScreenLayout_refactorFlagOn_qsAndNotifsTopMarginIsOfHeaderHeightHelper() {
- mSetFlagsRule.enableFlags(FLAG_CENTRALIZED_STATUS_BAR_DIMENS_REFACTOR)
- mSetFlagsRule.disableFlags(AConfigFlags.FLAG_KEYGUARD_SHADE_MIGRATION_NSSL)
+ mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
+ mSetFlagsRule.enableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
setLargeScreen()
val largeScreenHeaderResourceHeight = 100
val largeScreenHeaderHelperHeight = 200
@@ -492,7 +492,7 @@
@Test
fun testSmallScreenLayout_qsAndNotifsTopMarginIsZero() {
- mSetFlagsRule.disableFlags(AConfigFlags.FLAG_KEYGUARD_SHADE_MIGRATION_NSSL)
+ mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
setSmallScreen()
underTest.updateResources()
assertThat(getConstraintSetLayout(R.id.qs_frame).topMargin).isEqualTo(0)
@@ -513,7 +513,7 @@
@Test
fun testSinglePaneShadeLayout_isAlignedToParent() {
- mSetFlagsRule.disableFlags(AConfigFlags.FLAG_KEYGUARD_SHADE_MIGRATION_NSSL)
+ mSetFlagsRule.disableFlags(AConfigFlags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT)
disableSplitShade()
underTest.updateResources()
assertThat(getConstraintSetLayout(R.id.qs_frame).endToEnd)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt
index d7eada8..4ae751b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationsQSContainerControllerTest.kt
@@ -26,7 +26,7 @@
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.test.filters.SmallTest
-import com.android.systemui.Flags.FLAG_CENTRALIZED_STATUS_BAR_DIMENS_REFACTOR
+import com.android.systemui.Flags.FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX
import com.android.systemui.SysuiTestCase
import com.android.systemui.fragments.FragmentHostManager
import com.android.systemui.fragments.FragmentService
@@ -162,7 +162,7 @@
@Test
fun testLargeScreen_updateResources_refactorFlagOff_splitShadeHeightIsSet_basedOnResource() {
- mSetFlagsRule.disableFlags(FLAG_CENTRALIZED_STATUS_BAR_DIMENS_REFACTOR)
+ mSetFlagsRule.disableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
val helperHeight = 30
val resourceHeight = 20
whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight()).thenReturn(helperHeight)
@@ -183,7 +183,7 @@
@Test
fun testLargeScreen_updateResources_refactorFlagOn_splitShadeHeightIsSet_basedOnHelper() {
- mSetFlagsRule.enableFlags(FLAG_CENTRALIZED_STATUS_BAR_DIMENS_REFACTOR)
+ mSetFlagsRule.enableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
val helperHeight = 30
val resourceHeight = 20
whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight()).thenReturn(helperHeight)
@@ -425,7 +425,7 @@
@Test
fun testLargeScreenLayout_refactorFlagOff_qsAndNotifsTopMarginIsOfHeaderResourceHeight() {
- mSetFlagsRule.disableFlags(FLAG_CENTRALIZED_STATUS_BAR_DIMENS_REFACTOR)
+ mSetFlagsRule.disableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
setLargeScreen()
val largeScreenHeaderHelperHeight = 200
val largeScreenHeaderResourceHeight = 100
@@ -445,7 +445,7 @@
@Test
fun testLargeScreenLayout_refactorFlagOn_qsAndNotifsTopMarginIsOfHeaderHelperHeight() {
- mSetFlagsRule.enableFlags(FLAG_CENTRALIZED_STATUS_BAR_DIMENS_REFACTOR)
+ mSetFlagsRule.enableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
setLargeScreen()
val largeScreenHeaderHelperHeight = 200
val largeScreenHeaderResourceHeight = 100
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
index f178046..b6ee46d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
@@ -52,8 +52,8 @@
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.util.ContrastColorUtil;
-import com.android.systemui.res.R;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.res.R;
import com.android.systemui.statusbar.notification.NotificationContentDescription;
import org.junit.Before;
@@ -153,7 +153,7 @@
Icon icon = Icon.createWithBitmap(bitmap);
StatusBarIcon largeIcon = new StatusBarIcon(UserHandle.ALL, "mockPackage",
icon, 0, 0, "");
- mIconView.setNotification(mock(StatusBarNotification.class));
+ mIconView.setNotification(getMockSbn());
mIconView.getIcon(largeIcon);
// no crash? good
@@ -196,7 +196,7 @@
// the icon view layout size would be 60x150
// (the height is always 150 due to TEST_STATUS_BAR_HEIGHT)
setUpIconView(dpIconSize, dpDrawingSize, dpIconSize);
- mIconView.setNotification(mock(StatusBarNotification.class));
+ mIconView.setNotification(getMockSbn());
// the raw drawable size is 50x50. When put the drawable into iconView whose
// layout size is 60x150, the drawable size would not be constrained and thus keep 50x50
setIconDrawableWithSize(/* width= */ 50, /* height= */ 50);
@@ -215,7 +215,7 @@
// the icon view layout size would be 60x150
// (the height is always 150 due to TEST_STATUS_BAR_HEIGHT)
setUpIconView(dpIconSize, dpDrawingSize, dpIconSize);
- mIconView.setNotification(mock(StatusBarNotification.class));
+ mIconView.setNotification(getMockSbn());
// the raw drawable size is 50x100. When put the drawable into iconView whose
// layout size is 60x150, the drawable size would not be constrained and thus keep 50x100
setIconDrawableWithSize(/* width= */ 50, /* height= */ 100);
@@ -235,7 +235,7 @@
// the icon view layout size would be 60x150
// (the height is always 150 due to TEST_STATUS_BAR_HEIGHT)
setUpIconView(dpIconSize, dpDrawingSize, dpIconSize);
- mIconView.setNotification(mock(StatusBarNotification.class));
+ mIconView.setNotification(getMockSbn());
// the raw drawable size is 100x50. When put the drawable into iconView whose
// layout size is 60x150, the drawable size would be constrained to 60x30
setIconDrawableWithSize(/* width= */ 100, /* height= */ 50);
@@ -257,7 +257,7 @@
// the icon view layout size would be 40x150
// (the height is always 150 due to TEST_STATUS_BAR_HEIGHT)
setUpIconView(dpIconSize, dpDrawingSize, spIconSize);
- mIconView.setNotification(mock(StatusBarNotification.class));
+ mIconView.setNotification(getMockSbn());
// the raw drawable size is 50x50. When put the drawable into iconView whose
// layout size is 40x150, the drawable size would be constrained to 40x40
setIconDrawableWithSize(/* width= */ 50, /* height= */ 50);
@@ -283,7 +283,7 @@
// the icon view layout size would be 40x150
// (the height is always 150 due to TEST_STATUS_BAR_HEIGHT)
setUpIconView(dpIconSize, dpDrawingSize, spIconSize);
- mIconView.setNotification(mock(StatusBarNotification.class));
+ mIconView.setNotification(getMockSbn());
// the raw drawable size is 70x70. When put the drawable into iconView whose
// layout size is 40x150, the drawable size would be constrained to 40x40
setIconDrawableWithSize(/* width= */ 70, /* height= */ 70);
@@ -310,7 +310,7 @@
// the icon view layout size would be 40x150
// (the height is always 150 due to TEST_STATUS_BAR_HEIGHT)
setUpIconView(dpIconSize, dpDrawingSize, spIconSize);
- mIconView.setNotification(mock(StatusBarNotification.class));
+ mIconView.setNotification(getMockSbn());
// the raw drawable size is 50x100. When put the drawable into iconView whose
// layout size is 40x150, the drawable size would be constrained to 40x80
setIconDrawableWithSize(/* width= */ 50, /* height= */ 100);
@@ -334,7 +334,7 @@
// the icon view layout size would be 80x150
// (the height is always 150 due to TEST_STATUS_BAR_HEIGHT)
setUpIconView(dpIconSize, dpDrawingSize, spIconSize);
- mIconView.setNotification(mock(StatusBarNotification.class));
+ mIconView.setNotification(getMockSbn());
// the raw drawable size is 50x50. When put the drawable into iconView whose
// layout size is 80x150, the drawable size would not be constrained and thus keep 50x50
setIconDrawableWithSize(/* width= */ 50, /* height= */ 50);
@@ -357,7 +357,7 @@
// the icon view layout size would be 80x150
// (the height is always 150 due to TEST_STATUS_BAR_HEIGHT)
setUpIconView(dpIconSize, dpDrawingSize, spIconSize);
- mIconView.setNotification(mock(StatusBarNotification.class));
+ mIconView.setNotification(getMockSbn());
// the raw drawable size is 50x100. When put the drawable into iconView whose
// layout size is 80x150, the drawable size would not be constrained and thus keep 50x100
setIconDrawableWithSize(/* width= */ 50, /* height= */ 100);
@@ -381,7 +381,7 @@
// the icon view layout size would be 80x150
// (the height is always 150 due to TEST_STATUS_BAR_HEIGHT)
setUpIconView(dpIconSize, dpDrawingSize, spIconSize);
- mIconView.setNotification(mock(StatusBarNotification.class));
+ mIconView.setNotification(getMockSbn());
// the raw drawable size is 100x50. When put the drawable into iconView whose
// layout size is 80x150, the drawable size would not be constrained and thus keep 80x40
setIconDrawableWithSize(/* width= */ 100, /* height= */ 50);
@@ -397,6 +397,12 @@
mIconView.getIconScale(), 0.01f);
}
+ private static StatusBarNotification getMockSbn() {
+ StatusBarNotification sbn = mock(StatusBarNotification.class);
+ when(sbn.getNotification()).thenReturn(mock(Notification.class));
+ return sbn;
+ }
+
/**
* Setup iconView dimens for testing. The result icon view layout width would
* be spIconSize and height would be 150.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinatorTest.java
index 590c902..b548117 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/MediaCoordinatorTest.java
@@ -19,6 +19,7 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.never;
@@ -28,12 +29,15 @@
import android.app.Notification.MediaStyle;
import android.media.session.MediaSession;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
import android.service.notification.NotificationListenerService;
import android.testing.AndroidTestingRunner;
import androidx.test.filters.SmallTest;
import com.android.internal.statusbar.IStatusBarService;
+import com.android.systemui.Flags;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.media.controls.util.MediaFeatureFlag;
import com.android.systemui.statusbar.notification.InflationException;
@@ -153,7 +157,8 @@
}
@Test
- public void inflateMediaNotificationIconsMediaEnabled() throws InflationException {
+ @DisableFlags(Flags.FLAG_NOTIFICATIONS_BACKGROUND_MEDIA_ICONS)
+ public void inflateMediaNotificationIconsMediaEnabled_old() throws InflationException {
finishSetupWithMediaFeatureFlagEnabled(true);
mListener.onEntryInit(mMediaEntry);
@@ -181,7 +186,37 @@
}
@Test
- public void inflationException() throws InflationException {
+ @EnableFlags(Flags.FLAG_NOTIFICATIONS_BACKGROUND_MEDIA_ICONS)
+ public void inflateMediaNotificationIconsMediaEnabled_new() throws InflationException {
+ finishSetupWithMediaFeatureFlagEnabled(true);
+
+ mListener.onEntryInit(mMediaEntry);
+ mListener.onEntryAdded(mMediaEntry);
+ verify(mIconManager).createIcons(eq(mMediaEntry));
+ verify(mIconManager, never()).updateIcons(eq(mMediaEntry));
+ clearInvocations(mIconManager);
+
+ mFilter.shouldFilterOut(mMediaEntry, 0);
+ verify(mIconManager, never()).createIcons(eq(mMediaEntry));
+ verify(mIconManager, never()).updateIcons(eq(mMediaEntry));
+
+ mListener.onEntryUpdated(mMediaEntry);
+ verify(mIconManager, never()).createIcons(eq(mMediaEntry));
+ verify(mIconManager).updateIcons(eq(mMediaEntry));
+
+ mListener.onEntryRemoved(mMediaEntry, NotificationListenerService.REASON_CANCEL);
+ mListener.onEntryCleanUp(mMediaEntry);
+ clearInvocations(mIconManager);
+
+ mListener.onEntryInit(mMediaEntry);
+ mListener.onEntryAdded(mMediaEntry);
+ verify(mIconManager).createIcons(eq(mMediaEntry));
+ verify(mIconManager, never()).updateIcons(eq(mMediaEntry));
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_NOTIFICATIONS_BACKGROUND_MEDIA_ICONS)
+ public void inflationException_old() throws InflationException {
finishSetupWithMediaFeatureFlagEnabled(true);
mListener.onEntryInit(mMediaEntry);
@@ -208,6 +243,31 @@
verify(mIconManager, never()).updateIcons(eq(mMediaEntry));
}
+ @Test
+ @EnableFlags(Flags.FLAG_NOTIFICATIONS_BACKGROUND_MEDIA_ICONS)
+ public void inflationException_new() throws InflationException {
+ finishSetupWithMediaFeatureFlagEnabled(true);
+
+ doThrow(InflationException.class).when(mIconManager).createIcons(eq(mMediaEntry));
+
+ mListener.onEntryInit(mMediaEntry);
+ mListener.onEntryAdded(mMediaEntry);
+ verify(mIconManager).createIcons(eq(mMediaEntry));
+ verify(mIconManager, never()).updateIcons(eq(mMediaEntry));
+ clearInvocations(mIconManager);
+
+ mListener.onEntryUpdated(mMediaEntry);
+ verify(mIconManager).createIcons(eq(mMediaEntry));
+ verify(mIconManager, never()).updateIcons(eq(mMediaEntry));
+ clearInvocations(mIconManager);
+
+ doNothing().when(mIconManager).createIcons(eq(mMediaEntry));
+
+ mListener.onEntryUpdated(mMediaEntry);
+ verify(mIconManager).createIcons(eq(mMediaEntry));
+ verify(mIconManager, never()).updateIcons(eq(mMediaEntry));
+ }
+
private void finishSetupWithMediaFeatureFlagEnabled(boolean mediaFeatureFlagEnabled) {
when(mMediaFeatureFlag.getEnabled()).thenReturn(mediaFeatureFlagEnabled);
mCoordinator = new MediaCoordinator(mMediaFeatureFlag, mStatusBarService, mIconManager);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt
index 0830191..b1d2ea21 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ViewConfigCoordinatorTest.kt
@@ -23,6 +23,7 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.NotificationLockscreenUserManager.UserChangedListener
+import com.android.systemui.statusbar.notification.ColorUpdateLogger
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
@@ -57,6 +58,7 @@
private val lockscreenUserManager: NotificationLockscreenUserManager = mock()
private val gutsManager: NotificationGutsManager = mock()
private val keyguardUpdateMonitor: KeyguardUpdateMonitor = mock()
+ private val colorUpdateLogger: ColorUpdateLogger = mock()
@Before
fun setUp() {
@@ -66,7 +68,9 @@
configurationController,
lockscreenUserManager,
gutsManager,
- keyguardUpdateMonitor)
+ keyguardUpdateMonitor,
+ colorUpdateLogger,
+ )
coordinator.attach(pipeline)
userChangedListener = withArgCaptor {
verify(lockscreenUserManager).addUserChangedListener(capture())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java
index 3811f04..06410cd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/interruption/KeyguardNotificationVisibilityProviderTest.java
@@ -431,28 +431,6 @@
@Test
public void publicMode_settingsDisallow() {
- mFeatureFlags.set(Flags.NOTIF_LS_BACKGROUND_THREAD, true);
- // GIVEN an 'unfiltered-keyguard-showing' state
- setupUnfilteredState(mEntry);
-
- // WHEN the notification's user is in public mode and settings are configured to disallow
- // notifications in public mode
- when(mLockscreenUserManager.isLockscreenPublicMode(NOTIF_USER_ID)).thenReturn(true);
- when(mLockscreenUserManager.userAllowsNotificationsInPublic(NOTIF_USER_ID))
- .thenReturn(false);
-
- mEntry.setRanking(new RankingBuilder()
- .setChannel(new NotificationChannel("1", "1", 4))
- .setVisibilityOverride(VISIBILITY_NO_OVERRIDE)
- .setKey(mEntry.getKey()).build());
-
- // THEN filter out the entry
- assertTrue(mKeyguardNotificationVisibilityProvider.shouldHideNotification(mEntry));
- }
-
- @Test
- public void publicMode_settingsDisallow_mainThread() {
- mFeatureFlags.set(Flags.NOTIF_LS_BACKGROUND_THREAD, false);
// GIVEN an 'unfiltered-keyguard-showing' state
setupUnfilteredState(mEntry);
@@ -473,7 +451,6 @@
@Test
public void publicMode_nullChannel_allowed() {
- mFeatureFlags.set(Flags.NOTIF_LS_BACKGROUND_THREAD, true);
// GIVEN an 'unfiltered-keyguard-showing' state
setupUnfilteredState(mEntry);
@@ -490,7 +467,6 @@
@Test
public void publicMode_notifDisallowed() {
- mFeatureFlags.set(Flags.NOTIF_LS_BACKGROUND_THREAD, true);
NotificationChannel channel = new NotificationChannel("1", "1", IMPORTANCE_HIGH);
channel.setLockscreenVisibility(VISIBILITY_SECRET);
// GIVEN an 'unfiltered-keyguard-showing' state
@@ -509,23 +485,6 @@
}
@Test
- public void publicMode_notifDisallowed_mainThread() {
- mFeatureFlags.set(Flags.NOTIF_LS_BACKGROUND_THREAD, false);
- // GIVEN an 'unfiltered-keyguard-showing' state
- setupUnfilteredState(mEntry);
-
- // WHEN the notification's user is in public mode and settings are configured to disallow
- // notifications in public mode
- when(mLockscreenUserManager.isLockscreenPublicMode(CURR_USER_ID)).thenReturn(true);
- mEntry.setRanking(new RankingBuilder()
- .setKey(mEntry.getKey())
- .setVisibilityOverride(VISIBILITY_SECRET).build());
-
- // THEN filter out the entry
- assertTrue(mKeyguardNotificationVisibilityProvider.shouldHideNotification(mEntry));
- }
-
- @Test
public void doesNotExceedThresholdToShow() {
// GIVEN an 'unfiltered-keyguard-showing' state
setupUnfilteredState(mEntry);
@@ -579,7 +538,6 @@
@Test
public void notificationChannelVisibilityNoOverride() {
- mFeatureFlags.set(Flags.NOTIF_LS_BACKGROUND_THREAD, true);
// GIVEN a VISIBILITY_PRIVATE notification
NotificationEntryBuilder entryBuilder = new NotificationEntryBuilder()
.setUser(new UserHandle(NOTIF_USER_ID));
@@ -602,7 +560,6 @@
@Test
public void notificationChannelVisibilitySecret() {
- mFeatureFlags.set(Flags.NOTIF_LS_BACKGROUND_THREAD, true);
// GIVEN a VISIBILITY_PRIVATE notification
NotificationEntryBuilder entryBuilder = new NotificationEntryBuilder()
.setUser(new UserHandle(NOTIF_USER_ID));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
index 8ac2a33..210b1a7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
@@ -34,6 +34,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.SbnBuilder
import com.android.systemui.statusbar.SmartReplyController
+import com.android.systemui.statusbar.notification.ColorUpdateLogger
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder
import com.android.systemui.statusbar.notification.collection.provider.NotificationDismissibilityProvider
@@ -82,6 +83,7 @@
private val rivSubComponentFactory: RemoteInputViewSubcomponent.Factory = mock()
private val metricsLogger: MetricsLogger = mock()
private val logBufferLogger = NotificationRowLogger(logcatLogBuffer(), logcatLogBuffer())
+ private val colorUpdateLogger: ColorUpdateLogger = mock()
private val listContainer: NotificationListContainer = mock()
private val childrenContainer: NotificationChildrenContainer = mock()
private val smartReplyConstants: SmartReplyConstants = mock()
@@ -117,6 +119,7 @@
activableNotificationViewController,
rivSubComponentFactory,
metricsLogger,
+ colorUpdateLogger,
logBufferLogger,
NotificationChildrenContainerLogger(logcatLogBuffer()),
listContainer,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index c717991..e78081f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -65,6 +65,7 @@
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.SmartReplyController;
+import com.android.systemui.statusbar.notification.ColorUpdateLogger;
import com.android.systemui.statusbar.notification.ConversationNotificationProcessor;
import com.android.systemui.statusbar.notification.SourceType;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -607,6 +608,7 @@
mDismissibilityProvider,
mock(MetricsLogger.class),
new NotificationChildrenContainerLogger(logcatLogBuffer()),
+ mock(ColorUpdateLogger.class),
mock(SmartReplyConstants.class),
mock(SmartReplyController.class),
mFeatureFlags,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index 354f3f6..f2ef4e1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -78,6 +78,7 @@
import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.RemoteInputController;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
+import com.android.systemui.statusbar.notification.ColorUpdateLogger;
import com.android.systemui.statusbar.notification.DynamicPrivacyController;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
@@ -148,6 +149,7 @@
@Mock private PrimaryBouncerInteractor mPrimaryBouncerInteractor;
@Mock private NotificationLockscreenUserManager mNotificationLockscreenUserManager;
@Mock private MetricsLogger mMetricsLogger;
+ @Mock private ColorUpdateLogger mColorUpdateLogger;
@Mock private DumpManager mDumpManager;
@Mock(answer = Answers.RETURNS_SELF)
private NotificationSwipeHelper.Builder mNotificationSwipeHelperBuilder;
@@ -1007,6 +1009,7 @@
mZenModeController,
mNotificationLockscreenUserManager,
mMetricsLogger,
+ mColorUpdateLogger,
mDumpManager,
new FalsingCollectorFake(),
new FalsingManagerFake(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
index 2da88e9..b9b8722 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt
@@ -21,7 +21,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
-import com.android.systemui.Flags.FLAG_CENTRALIZED_STATUS_BAR_DIMENS_REFACTOR
+import com.android.systemui.Flags.FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.NotificationContainerBounds
import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
@@ -134,7 +134,7 @@
@Test
fun validatePaddingTopInSplitShade_refactorFlagOff_usesLargeHeaderResource() =
testScope.runTest {
- mSetFlagsRule.disableFlags(FLAG_CENTRALIZED_STATUS_BAR_DIMENS_REFACTOR)
+ mSetFlagsRule.disableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight()).thenReturn(5)
whenever(splitShadeStateController.shouldUseSplitNotificationShade(any()))
.thenReturn(true)
@@ -153,7 +153,7 @@
@Test
fun validatePaddingTopInSplitShade_refactorFlagOn_usesLargeHeaderHelper() =
testScope.runTest {
- mSetFlagsRule.enableFlags(FLAG_CENTRALIZED_STATUS_BAR_DIMENS_REFACTOR)
+ mSetFlagsRule.enableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight()).thenReturn(5)
whenever(splitShadeStateController.shouldUseSplitNotificationShade(any()))
.thenReturn(true)
@@ -210,7 +210,7 @@
@Test
fun validateMarginTopWithLargeScreenHeader_refactorFlagOff_usesResource() =
testScope.runTest {
- mSetFlagsRule.disableFlags(FLAG_CENTRALIZED_STATUS_BAR_DIMENS_REFACTOR)
+ mSetFlagsRule.disableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
val headerResourceHeight = 50
val headerHelperHeight = 100
whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight())
@@ -229,7 +229,7 @@
@Test
fun validateMarginTopWithLargeScreenHeader_refactorFlagOn_usesHelper() =
testScope.runTest {
- mSetFlagsRule.enableFlags(FLAG_CENTRALIZED_STATUS_BAR_DIMENS_REFACTOR)
+ mSetFlagsRule.enableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
val headerResourceHeight = 50
val headerHelperHeight = 100
whenever(largeScreenHeaderHelper.getLargeScreenHeaderHeight())
@@ -449,7 +449,7 @@
@Test
fun boundsOnLockscreenInSplitShade_refactorFlagOff_usesLargeHeaderResource() =
testScope.runTest {
- mSetFlagsRule.disableFlags(FLAG_CENTRALIZED_STATUS_BAR_DIMENS_REFACTOR)
+ mSetFlagsRule.disableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
val bounds by collectLastValue(underTest.bounds)
// When in split shade
@@ -478,7 +478,7 @@
@Test
fun boundsOnLockscreenInSplitShade_refactorFlagOn_usesLargeHeaderHelper() =
testScope.runTest {
- mSetFlagsRule.enableFlags(FLAG_CENTRALIZED_STATUS_BAR_DIMENS_REFACTOR)
+ mSetFlagsRule.enableFlags(FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX)
val bounds by collectLastValue(underTest.bounds)
// When in split shade
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
index 38698f8..fd295b5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithmTest.java
@@ -298,7 +298,7 @@
@Test
public void notifPaddingMakesUpToFullMarginInSplitShade_refactorFlagOff_usesResource() {
- mSetFlagsRule.disableFlags(Flags.FLAG_CENTRALIZED_STATUS_BAR_DIMENS_REFACTOR);
+ mSetFlagsRule.disableFlags(Flags.FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX);
int keyguardSplitShadeTopMargin = 100;
int largeScreenHeaderHeightResource = 70;
when(mResources.getDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin))
@@ -317,7 +317,7 @@
@Test
public void notifPaddingMakesUpToFullMarginInSplitShade_refactorFlagOn_usesHelper() {
- mSetFlagsRule.enableFlags(Flags.FLAG_CENTRALIZED_STATUS_BAR_DIMENS_REFACTOR);
+ mSetFlagsRule.enableFlags(Flags.FLAG_CENTRALIZED_STATUS_BAR_HEIGHT_FIX);
int keyguardSplitShadeTopMargin = 100;
int largeScreenHeaderHeightHelper = 50;
int largeScreenHeaderHeightResource = 70;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImplTest.java
index 41b959e..9d53b9c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LegacyNotificationIconAreaControllerImplTest.java
@@ -119,7 +119,7 @@
@Test
public void testAppearResetsTranslation() {
- mSetFlagsRule.disableFlags(Flags.FLAG_KEYGUARD_SHADE_MIGRATION_NSSL);
+ mSetFlagsRule.disableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT);
mController.setupAodIcons(mAodIcons);
when(mDozeParameters.shouldControlScreenOff()).thenReturn(false);
mController.appearAodIcons();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java
index f1a2c28..ddd29c3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ZenModeControllerImplTest.java
@@ -79,6 +79,7 @@
mController = new ZenModeControllerImpl(
mContext,
Handler.createAsync(TestableLooper.get(this).getLooper()),
+ Handler.createAsync(TestableLooper.get(this).getLooper()),
mBroadcastDispatcher,
mDumpManager,
mGlobalSettings,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt
index ee2e5ad..28adbce 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt
@@ -321,4 +321,38 @@
verify(displaySwitchLatencyLogger, never()).log(any())
}
}
+
+ @Test
+ fun foldToScreenOff_capturesToStateAsScreenOff() {
+ testScope.runTest {
+ areAnimationEnabled.emit(true)
+ deviceState.emit(DeviceState.UNFOLDED)
+ isAodAvailable.emit(false)
+
+ displaySwitchLatencyTracker.start()
+ deviceState.emit(DeviceState.HALF_FOLDED)
+ systemClock.advanceTime(50)
+ runCurrent()
+ deviceState.emit(DeviceState.FOLDED)
+ lastWakefulnessEvent.emit(
+ WakefulnessModel(
+ internalWakefulnessState = WakefulnessState.ASLEEP,
+ lastSleepReason = WakeSleepReason.FOLD
+ )
+ )
+ screenPowerState.emit(ScreenPowerState.SCREEN_OFF)
+ runCurrent()
+
+ verify(displaySwitchLatencyLogger).log(capture(loggerArgumentCaptor))
+ val loggedEvent = loggerArgumentCaptor.value
+ val expectedLoggedEvent =
+ DisplaySwitchLatencyEvent(
+ latencyMs = 0,
+ fromFoldableDeviceState = FOLDABLE_DEVICE_STATE_HALF_OPEN,
+ toFoldableDeviceState = FOLDABLE_DEVICE_STATE_CLOSED,
+ toState = SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__TO_STATE__SCREEN_OFF
+ )
+ assertThat(loggedEvent).isEqualTo(expectedLoggedEvent)
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
index f924134..10aab96 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/FoldAodAnimationControllerTest.kt
@@ -246,7 +246,7 @@
private fun sendFoldEvent(folded: Boolean) {
val state = if (folded) deviceStates.folded else deviceStates.unfolded
- foldStateListenerCaptor.value.onStateChanged(state)
+ foldStateListenerCaptor.value.onDeviceStateChanged(state)
}
companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt
index c7dc0ab..ba72716 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt
@@ -174,7 +174,7 @@
private fun sendFoldEvent(folded: Boolean) {
val state = if (folded) deviceStates.folded else deviceStates.unfolded
- foldStateListenerCaptor.value.onStateChanged(state)
+ foldStateListenerCaptor.value.onDeviceStateChanged(state)
}
private fun sendScreenTurnedOnEvent() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/FoldableTestUtils.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/FoldableTestUtils.kt
index 01b5359..e499a3c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/FoldableTestUtils.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/FoldableTestUtils.kt
@@ -17,21 +17,29 @@
package com.android.systemui.unfold.util
import android.content.Context
+import android.hardware.devicestate.DeviceState
import org.junit.Assume.assumeTrue
object FoldableTestUtils {
/** Finds device state for folded and unfolded. */
fun findDeviceStates(context: Context): FoldableDeviceStates {
+ // TODO(b/325474477): Migrate clients to updated DeviceStateManager API's
val foldedDeviceStates: IntArray = context.resources.getIntArray(
com.android.internal.R.array.config_foldedDeviceStates)
assumeTrue("Test should be launched on a foldable device",
foldedDeviceStates.isNotEmpty())
- val folded = foldedDeviceStates.maxOrNull()!!
- val unfolded = folded + 1
+ val folded =
+ DeviceState(foldedDeviceStates.maxOrNull()!! /* identifier */,
+ "" /* name */,
+ emptySet() /* properties */)
+ val unfolded =
+ DeviceState(folded.identifier + 1 /* identifier */,
+ "" /* name */,
+ emptySet() /* properties */)
return FoldableDeviceStates(folded = folded, unfolded = unfolded)
}
}
-data class FoldableDeviceStates(val folded: Int, val unfolded: Int)
\ No newline at end of file
+data class FoldableDeviceStates(val folded: DeviceState, val unfolded: DeviceState)
\ No newline at end of file
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/FakeCredentialInteractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/FakeCredentialInteractor.kt
index fbe291e..a4c8a0b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/FakeCredentialInteractor.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/biometrics/domain/interactor/FakeCredentialInteractor.kt
@@ -14,10 +14,15 @@
/** Sets return value for [getCredentialOwnerOrSelfId]. */
var credentialOwnerId: Int? = null
+ /** Sets return value for [getParentProfileIdOrSelfId]. */
+ var userIdForPasswordEntry: Int? = null
+
override fun isStealthModeActive(userId: Int): Boolean = stealthMode
override fun getCredentialOwnerOrSelfId(userId: Int): Int = credentialOwnerId ?: userId
+ override fun getParentProfileIdOrSelfId(userId: Int): Int = userIdForPasswordEntry ?: userId
+
override fun verifyCredential(
request: BiometricPromptRequest.Credential,
credential: LockscreenCredential,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
index f7e9a11..566fc25 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/domain/interactor/CommunalInteractorKosmos.kt
@@ -21,12 +21,16 @@
import com.android.systemui.communal.data.repository.communalRepository
import com.android.systemui.communal.data.repository.communalWidgetRepository
import com.android.systemui.communal.widgets.EditWidgetsActivityStarter
+import com.android.systemui.flags.Flags
+import com.android.systemui.flags.fakeFeatureFlagsClassic
+import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.log.logcatLogBuffer
import com.android.systemui.smartspace.data.repository.smartspaceRepository
+import com.android.systemui.user.data.repository.fakeUserRepository
import com.android.systemui.util.mockito.mock
val Kosmos.communalInteractor by Fixture {
@@ -47,3 +51,14 @@
}
val Kosmos.editWidgetsActivityStarter by Fixture<EditWidgetsActivityStarter> { mock() }
+
+suspend fun Kosmos.setCommunalAvailable(available: Boolean) {
+ fakeFeatureFlagsClassic.set(Flags.COMMUNAL_SERVICE_ENABLED, available)
+ if (available) {
+ fakeUserRepository.asMainUser()
+ with(fakeKeyguardRepository) {
+ setIsEncryptedOrLockdown(false)
+ setKeyguardShowing(true)
+ }
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModelKosmos.kt
index 9fb3284..f1784a8 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AodAlphaViewModelKosmos.kt
@@ -18,6 +18,7 @@
package com.android.systemui.keyguard.ui.viewmodel
+import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.Kosmos.Fixture
@@ -28,5 +29,6 @@
keyguardTransitionInteractor = keyguardTransitionInteractor,
goneToAodTransitionViewModel = goneToAodTransitionViewModel,
goneToDozingTransitionViewModel = goneToDozingTransitionViewModel,
+ keyguardInteractor = keyguardInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
index 8882de0..832344d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt
@@ -25,6 +25,7 @@
import com.android.systemui.keyguard.ui.viewmodel.dozingToLockscreenTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.dreamingToLockscreenTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.glanceableHubToLockscreenTransitionViewModel
+import com.android.systemui.keyguard.ui.viewmodel.goneToAodTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.goneToDozingTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.goneToDreamingTransitionViewModel
import com.android.systemui.keyguard.ui.viewmodel.lockscreenToDreamingTransitionViewModel
@@ -41,7 +42,9 @@
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.sharedNotificationContainerInteractor
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+@OptIn(ExperimentalCoroutinesApi::class)
val Kosmos.sharedNotificationContainerViewModel by Fixture {
SharedNotificationContainerViewModel(
interactor = sharedNotificationContainerInteractor,
@@ -54,6 +57,7 @@
aodToLockscreenTransitionViewModel = aodToLockscreenTransitionViewModel,
dozingToLockscreenTransitionViewModel = dozingToLockscreenTransitionViewModel,
dreamingToLockscreenTransitionViewModel = dreamingToLockscreenTransitionViewModel,
+ goneToAodTransitionViewModel = goneToAodTransitionViewModel,
goneToDozingTransitionViewModel = goneToDozingTransitionViewModel,
goneToDreamingTransitionViewModel = goneToDreamingTransitionViewModel,
glanceableHubToLockscreenTransitionViewModel = glanceableHubToLockscreenTransitionViewModel,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt
index c51de33..46a1053 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/policy/FakeConfigurationController.kt
@@ -43,6 +43,7 @@
}
override fun isLayoutRtl(): Boolean = isRtl
+ override fun getNightModeName(): String = "undefined"
}
@Module
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt
index 1124425..931a59d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt
@@ -39,13 +39,19 @@
// User id to represent a non system (human) user id. We presume this is the main user.
private const val MAIN_USER_ID = 10
- private val DEFAULT_SELECTED_USER = 0
+ private const val DEFAULT_SELECTED_USER = 0
private val DEFAULT_SELECTED_USER_INFO =
UserInfo(
/* id= */ DEFAULT_SELECTED_USER,
/* name= */ "default selected user",
/* flags= */ 0,
)
+ private val MAIN_USER =
+ UserInfo(
+ /* id= */ MAIN_USER_ID,
+ /* name= */ "main user",
+ /* flags= */ UserInfo.FLAG_MAIN,
+ )
}
private val _userSwitcherSettings = MutableStateFlow(UserSwitcherSettingsModel())
@@ -113,6 +119,13 @@
yield()
}
+ /** Makes the current user [MAIN_USER]. */
+ suspend fun asMainUser(): UserInfo {
+ setUserInfos(listOf(MAIN_USER))
+ setSelectedUserInfo(MAIN_USER)
+ return MAIN_USER
+ }
+
suspend fun setSettings(settings: UserSwitcherSettingsModel) {
_userSwitcherSettings.value = settings
yield()
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeConfigurationController.java b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeConfigurationController.java
index 516eb6e..111c40d 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeConfigurationController.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/utils/leaks/FakeConfigurationController.java
@@ -38,4 +38,9 @@
public boolean isLayoutRtl() {
return false;
}
+
+ @Override
+ public String getNightModeName() {
+ return "undefined";
+ }
}
diff --git a/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java b/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java
index 053ed77..3ecdf3f 100644
--- a/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java
+++ b/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java
@@ -36,9 +36,11 @@
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
@@ -57,6 +59,7 @@
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.graphics.Rect;
import android.os.FileUtils;
import android.os.ParcelFileDescriptor;
import android.os.UserHandle;
@@ -78,6 +81,7 @@
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentMatcher;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -87,6 +91,7 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
@RunWith(AndroidJUnit4.class)
@@ -815,6 +820,48 @@
WallpaperEventLogger.ERROR_LIVE_PACKAGE_NOT_INSTALLED);
}
+ @Test
+ public void testOnRestore_noCropHints() throws Exception {
+ testParseCropHints(Map.of());
+ }
+
+ @Test
+ public void testOnRestore_singleCropHint() throws Exception {
+ Map<Integer, Rect> testMap = Map.of(WallpaperManager.PORTRAIT, new Rect(1, 2, 3, 4));
+ testParseCropHints(testMap);
+ }
+
+ @Test
+ public void testOnRestore_multipleCropHints() throws Exception {
+ Map<Integer, Rect> testMap = Map.of(
+ WallpaperManager.PORTRAIT, new Rect(1, 2, 3, 4),
+ WallpaperManager.SQUARE_PORTRAIT, new Rect(5, 6, 7, 8),
+ WallpaperManager.SQUARE_LANDSCAPE, new Rect(9, 10, 11, 12));
+ testParseCropHints(testMap);
+ }
+
+ private void testParseCropHints(Map<Integer, Rect> testMap) throws Exception {
+ assumeTrue(multiCrop());
+ mockRestoredStaticWallpaperFile(testMap);
+ mockStagedWallpaperFile(SYSTEM_WALLPAPER_STAGE);
+ mWallpaperBackupAgent.onCreate(USER_HANDLE, BackupAnnotations.BackupDestination.CLOUD,
+ BackupAnnotations.OperationType.RESTORE);
+
+ mWallpaperBackupAgent.onRestoreFinished();
+
+ ArgumentMatcher<SparseArray<Rect>> matcher = array -> {
+ boolean result = testMap.entrySet().stream().allMatch(entry -> {
+ int key = entry.getKey();
+ return (array.contains(key) && array.get(key).equals(testMap.get(key)));
+ });
+ for (int i = 0; i < array.size(); i++) {
+ if (!testMap.containsKey(array.keyAt(i))) result = false;
+ }
+ return result;
+ };
+ verify(mWallpaperManager).setStreamWithCrops(any(), argThat(matcher), eq(true), anyInt());
+ }
+
private void mockCurrentWallpaperIds(int systemWallpaperId, int lockWallpaperId) {
when(mWallpaperManager.getWallpaperId(eq(FLAG_SYSTEM))).thenReturn(systemWallpaperId);
when(mWallpaperManager.getWallpaperId(eq(FLAG_LOCK))).thenReturn(lockWallpaperId);
@@ -880,6 +927,34 @@
fstream.close();
}
+ private void mockRestoredStaticWallpaperFile(Map<Integer, Rect> crops) throws Exception {
+ File wallpaperFile = new File(mContext.getFilesDir(), WALLPAPER_INFO_STAGE);
+ wallpaperFile.createNewFile();
+ FileOutputStream fstream = new FileOutputStream(wallpaperFile, false);
+ TypedXmlSerializer out = Xml.resolveSerializer(fstream);
+ out.startDocument(null, true);
+ out.startTag(null, "wp");
+ for (Map.Entry<Integer, Rect> entry: crops.entrySet()) {
+ String orientation = switch (entry.getKey()) {
+ case WallpaperManager.PORTRAIT -> "Portrait";
+ case WallpaperManager.LANDSCAPE -> "Landscape";
+ case WallpaperManager.SQUARE_PORTRAIT -> "SquarePortrait";
+ case WallpaperManager.SQUARE_LANDSCAPE -> "SquareLandscape";
+ default -> throw new IllegalArgumentException("Invalid orientation");
+ };
+ Rect rect = entry.getValue();
+ out.attributeInt(null, "cropLeft" + orientation, rect.left);
+ out.attributeInt(null, "cropTop" + orientation, rect.top);
+ out.attributeInt(null, "cropRight" + orientation, rect.right);
+ out.attributeInt(null, "cropBottom" + orientation, rect.bottom);
+ }
+ out.endTag(null, "wp");
+ out.endDocument();
+ fstream.flush();
+ FileUtils.sync(fstream);
+ fstream.close();
+ }
+
private WallpaperInfo getFakeWallpaperInfo() throws Exception {
Context context = InstrumentationRegistry.getTargetContext();
Intent intent = new Intent(WallpaperService.SERVICE_INTERFACE);
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 7b5932b..1d5c79c 100644
--- a/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
+++ b/ravenwood/junit-impl-src/android/platform/test/ravenwood/RavenwoodRuleImpl.java
@@ -16,6 +16,8 @@
package android.platform.test.ravenwood;
+import static org.junit.Assert.assertFalse;
+
import android.app.ActivityManager;
import android.app.Instrumentation;
import android.os.Build;
@@ -28,12 +30,19 @@
import com.android.internal.os.RuntimeInit;
+import org.junit.After;
import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
import org.junit.runner.Description;
import org.junit.runner.RunWith;
import org.junit.runners.model.Statement;
import java.io.PrintStream;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Executors;
@@ -183,6 +192,7 @@
public static void validate(Statement base, Description description,
boolean enableOptionalValidation) {
validateTestRunner(base, description, enableOptionalValidation);
+ validateTestAnnotations(base, description, enableOptionalValidation);
}
private static void validateTestRunner(Statement base, Description description,
@@ -206,4 +216,63 @@
}
}
}
+
+ private static void validateTestAnnotations(Statement base, Description description,
+ boolean enableOptionalValidation) {
+ final var testClass = description.getTestClass();
+
+ final var message = new StringBuilder();
+
+ boolean hasErrors = false;
+ for (Method m : collectMethods(testClass)) {
+ if (Modifier.isPublic(m.getModifiers()) && m.getName().startsWith("test")) {
+ if (m.getAnnotation(Test.class) == null) {
+ message.append("\nMethod " + m.getName() + "() doesn't have @Test");
+ hasErrors = true;
+ }
+ }
+ if ("setUp".equals(m.getName())) {
+ if (m.getAnnotation(Before.class) == null) {
+ message.append("\nMethod " + m.getName() + "() doesn't have @Before");
+ hasErrors = true;
+ }
+ if (!Modifier.isPublic(m.getModifiers())) {
+ message.append("\nMethod " + m.getName() + "() must be public");
+ hasErrors = true;
+ }
+ }
+ if ("tearDown".equals(m.getName())) {
+ if (m.getAnnotation(After.class) == null) {
+ message.append("\nMethod " + m.getName() + "() doesn't have @After");
+ hasErrors = true;
+ }
+ if (!Modifier.isPublic(m.getModifiers())) {
+ message.append("\nMethod " + m.getName() + "() must be public");
+ hasErrors = true;
+ }
+ }
+ }
+ assertFalse("Problem(s) detected in class " + testClass.getCanonicalName() + ":"
+ + message, hasErrors);
+ }
+
+ /**
+ * Collect all (public or private or any) methods in a class, including inherited methods.
+ */
+ private static List<Method> collectMethods(Class<?> clazz) {
+ var ret = new ArrayList<Method>();
+ collectMethods(clazz, ret);
+ return ret;
+ }
+
+ private static void collectMethods(Class<?> clazz, List<Method> result) {
+ // Class.getMethods() only return public methods, so we need to use getDeclaredMethods()
+ // instead, and recurse.
+ for (var m : clazz.getDeclaredMethods()) {
+ result.add(m);
+ }
+ if (clazz.getSuperclass() != null) {
+ collectMethods(clazz.getSuperclass(), result);
+ }
+ }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/BrailleDisplayConnection.java b/services/accessibility/java/com/android/server/accessibility/BrailleDisplayConnection.java
index 1f18e15..9b27dd3 100644
--- a/services/accessibility/java/com/android/server/accessibility/BrailleDisplayConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/BrailleDisplayConnection.java
@@ -42,7 +42,6 @@
import java.io.File;
import java.io.FileInputStream;
-import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -112,7 +111,7 @@
BrailleDisplayConnection(@NonNull Object lock,
@NonNull AccessibilityServiceConnection serviceConnection) {
this.mLock = Objects.requireNonNull(lock);
- this.mScanner = getDefaultNativeScanner(getDefaultNativeInterface());
+ this.mScanner = getDefaultNativeScanner(new DefaultNativeInterface());
this.mServiceConnection = Objects.requireNonNull(serviceConnection);
}
@@ -128,7 +127,7 @@
*/
@VisibleForTesting
interface BrailleDisplayScanner {
- Collection<Path> getHidrawNodePaths();
+ Collection<Path> getHidrawNodePaths(@NonNull Path directory);
byte[] getDeviceReportDescriptor(@NonNull Path path);
@@ -158,8 +157,9 @@
Objects.requireNonNull(expectedUniqueId);
this.mController = Objects.requireNonNull(controller);
+ final Path devicePath = Path.of("/dev");
final List<Pair<File, byte[]>> result = new ArrayList<>();
- final Collection<Path> hidrawNodePaths = mScanner.getHidrawNodePaths();
+ final Collection<Path> hidrawNodePaths = mScanner.getHidrawNodePaths(devicePath);
if (hidrawNodePaths == null) {
Slog.w(LOG_TAG, "Unable to access the HIDRAW node directory");
sendConnectionErrorLocked(FLAG_ERROR_CANNOT_ACCESS);
@@ -285,6 +285,9 @@
if (buffer.length > IBinder.getSuggestedMaxIpcSizeBytes()) {
Slog.e(LOG_TAG, "Requested write of size " + buffer.length
+ " which is larger than maximum " + IBinder.getSuggestedMaxIpcSizeBytes());
+ // The caller only got here by bypassing the AccessibilityService-side check with
+ // reflection, so disconnect this connection to prevent further attempts.
+ disconnect();
return;
}
synchronized (mLock) {
@@ -292,7 +295,7 @@
if (mOutputThread == null) {
try {
mOutputStream = new FileOutputStream(mHidrawNode);
- } catch (FileNotFoundException e) {
+ } catch (Exception e) {
Slog.e(LOG_TAG, "Unable to create write stream", e);
disconnect();
return;
@@ -387,14 +390,13 @@
BrailleDisplayScanner getDefaultNativeScanner(@NonNull NativeInterface nativeInterface) {
Objects.requireNonNull(nativeInterface);
return new BrailleDisplayScanner() {
- private static final Path DEVICE_DIR = Path.of("/dev");
private static final String HIDRAW_DEVICE_GLOB = "hidraw*";
@Override
- public Collection<Path> getHidrawNodePaths() {
+ public Collection<Path> getHidrawNodePaths(@NonNull Path directory) {
final List<Path> result = new ArrayList<>();
try (DirectoryStream<Path> hidrawNodePaths = Files.newDirectoryStream(
- DEVICE_DIR, HIDRAW_DEVICE_GLOB)) {
+ directory, HIDRAW_DEVICE_GLOB)) {
for (Path path : hidrawNodePaths) {
result.add(path);
}
@@ -458,8 +460,8 @@
synchronized (mLock) {
mScanner = new BrailleDisplayScanner() {
@Override
- public Collection<Path> getHidrawNodePaths() {
- return brailleDisplayMap.keySet();
+ public Collection<Path> getHidrawNodePaths(@NonNull Path directory) {
+ return brailleDisplayMap.isEmpty() ? null : brailleDisplayMap.keySet();
}
@Override
@@ -490,38 +492,56 @@
*/
@VisibleForTesting
interface NativeInterface {
+ /**
+ * Returns the HIDRAW descriptor size for the file descriptor.
+ *
+ * @return the result of ioctl(HIDIOCGRDESCSIZE), or -1 if the ioctl fails.
+ */
int getHidrawDescSize(int fd);
+ /**
+ * Returns the HIDRAW descriptor for the file descriptor.
+ *
+ * @return the result of ioctl(HIDIOCGRDESC), or null if the ioctl fails.
+ */
byte[] getHidrawDesc(int fd, int descSize);
+ /**
+ * Returns the HIDRAW unique identifier for the file descriptor.
+ *
+ * @return the result of ioctl(HIDIOCGRAWUNIQ), or null if the ioctl fails.
+ */
String getHidrawUniq(int fd);
+ /**
+ * Returns the HIDRAW bus type for the file descriptor.
+ *
+ * @return the result of ioctl(HIDIOCGRAWINFO).bustype, or -1 if the ioctl fails.
+ */
int getHidrawBusType(int fd);
}
/** Native interface that actually calls native HIDRAW ioctls. */
- private NativeInterface getDefaultNativeInterface() {
- return new NativeInterface() {
- @Override
- public int getHidrawDescSize(int fd) {
- return nativeGetHidrawDescSize(fd);
- }
+ private class DefaultNativeInterface implements NativeInterface {
+ @Override
+ public int getHidrawDescSize(int fd) {
+ return nativeGetHidrawDescSize(fd);
+ }
- @Override
- public byte[] getHidrawDesc(int fd, int descSize) {
- return nativeGetHidrawDesc(fd, descSize);
- }
+ @Override
+ public byte[] getHidrawDesc(int fd, int descSize) {
+ return nativeGetHidrawDesc(fd, descSize);
+ }
- @Override
- public String getHidrawUniq(int fd) {
- return nativeGetHidrawUniq(fd);
- }
+ @Override
+ public String getHidrawUniq(int fd) {
+ return nativeGetHidrawUniq(fd);
+ }
- @Override
- public int getHidrawBusType(int fd) {
- return nativeGetHidrawBusType(fd);
- }
- };
+ @Override
+ public int getHidrawBusType(int fd) {
+ return nativeGetHidrawBusType(fd);
+ }
}
private native int nativeGetHidrawDescSize(int fd);
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 5407af7..eb661ce 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -327,10 +327,13 @@
synchronized (mLock) {
// No need to enforce unlocked state when there is no caller. User can be in the
// stopping state or removed by the time the message is processed
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "convert_state_to_bytes");
ensureGroupStateLoadedLocked(userId, false /* enforceUserUnlockingOrUnlocked */);
userIdToBytesMapping = saveStateToByteArrayLocked(userId);
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "byte_to_disk_io");
for (int i = 0; i < userIdToBytesMapping.size(); i++) {
int currentProfileId = userIdToBytesMapping.keyAt(i);
byte[] currentStateByteArray = userIdToBytesMapping.valueAt(i);
@@ -351,6 +354,7 @@
currentFile.failWrite(fileStream);
}
}
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
return true;
}
@@ -2409,6 +2413,16 @@
}
AppWidgetProviderInfo info = createPartialProviderInfo(providerId, ri, existing);
+
+ if (android.os.Flags.allowPrivateProfile()
+ && android.multiuser.Flags.disablePrivateSpaceItemsOnHome()) {
+ // Do not add widget providers for profiles with items restricted on home screen.
+ if (mUserManager
+ .getUserProperties(info.getProfile()).areItemsRestrictedOnHomeScreen()) {
+ return false;
+ }
+ }
+
if (info != null) {
if (existing != null) {
if (existing.zombie && !mSafeMode) {
@@ -4787,8 +4801,10 @@
synchronized (mLock) {
// No need to enforce unlocked state when there is no caller. User can be in the
// stopping state or removed by the time the message is processed
+ Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "convert_state_and_io");
ensureGroupStateLoadedLocked(mUserId, false /* enforceUserUnlockingOrUnlocked */ );
saveStateLocked(mUserId);
+ Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
}
}
diff --git a/services/autofill/Android.bp b/services/autofill/Android.bp
index eb23f2f..3965869 100644
--- a/services/autofill/Android.bp
+++ b/services/autofill/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_autofill",
// 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"
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index d47245e..5a34217 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -2770,6 +2770,10 @@
+ id + " destroyed");
return;
}
+ if (sDebug) {
+ Slog.d(TAG, "setAuthenticationResultLocked(): id= " + authenticationId
+ + ", data=" + data);
+ }
final int requestId = AutofillManager.getRequestIdFromAuthenticationId(authenticationId);
if (requestId == AUGMENTED_AUTOFILL_REQUEST_ID) {
setAuthenticationResultForAugmentedAutofillLocked(data, authenticationId);
@@ -2823,12 +2827,18 @@
+ ", clientState=" + newClientState + ", authenticationId=" + authenticationId);
}
if (result instanceof FillResponse) {
+ if (sDebug) {
+ Slog.d(TAG, "setAuthenticationResultLocked(): received FillResponse from"
+ + " authentication flow");
+ }
logAuthenticationStatusLocked(requestId, MetricsEvent.AUTOFILL_AUTHENTICATED);
mPresentationStatsEventLogger.maybeSetAuthenticationResult(
AUTHENTICATION_RESULT_SUCCESS);
replaceResponseLocked(authenticatedResponse, (FillResponse) result, newClientState);
} else if (result instanceof GetCredentialResponse) {
- Slog.d(TAG, "Received GetCredentialResponse from authentication flow");
+ if (sDebug) {
+ Slog.d(TAG, "Received GetCredentialResponse from authentication flow");
+ }
boolean isCredmanCallbackInvoked = false;
if (Flags.autofillCredmanIntegration()) {
GetCredentialResponse response = (GetCredentialResponse) result;
@@ -2843,6 +2853,10 @@
}
}
} else if (result instanceof Dataset) {
+ if (sDebug) {
+ Slog.d(TAG, "setAuthenticationResultLocked(): received Dataset from"
+ + " authentication flow");
+ }
if (datasetIdx != AutofillManager.AUTHENTICATION_ID_DATASET_ID_UNDEFINED) {
logAuthenticationStatusLocked(requestId,
MetricsEvent.AUTOFILL_DATASET_AUTHENTICATED);
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index b43f1a9..ba1f51b 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -40,6 +40,7 @@
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
import static com.android.server.companion.AssociationStore.CHANGE_TYPE_UPDATED_ADDRESS_UNCHANGED;
import static com.android.server.companion.MetricUtils.logRemoveAssociation;
+import static com.android.server.companion.PackageUtils.isRestrictedSettingsAllowed;
import static com.android.server.companion.PackageUtils.enforceUsesCompanionDeviceFeature;
import static com.android.server.companion.PackageUtils.getPackageInfo;
import static com.android.server.companion.PermissionsUtils.checkCallerCanManageCompanionDevice;
@@ -871,13 +872,22 @@
@Override
public PendingIntent requestNotificationAccess(ComponentName component, int userId)
throws RemoteException {
- String callingPackage = component.getPackageName();
+ int callingUid = getCallingUid();
+ final String callingPackage = component.getPackageName();
+
checkCanCallNotificationApi(callingPackage, userId);
+
if (component.flattenToString().length() > MAX_CN_LENGTH) {
throw new IllegalArgumentException("Component name is too long.");
}
+
final long identity = Binder.clearCallingIdentity();
try {
+ if (!isRestrictedSettingsAllowed(getContext(), callingPackage, callingUid)) {
+ Slog.e(TAG, "Side loaded app must enable restricted "
+ + "setting before request the notification access");
+ return null;
+ }
return PendingIntent.getActivityAsUser(getContext(),
0 /* request code */,
NotificationAccessConfirmationActivityContract.launcherIntent(
diff --git a/services/companion/java/com/android/server/companion/PackageUtils.java b/services/companion/java/com/android/server/companion/PackageUtils.java
index 6c77018..3aae1ec 100644
--- a/services/companion/java/com/android/server/companion/PackageUtils.java
+++ b/services/companion/java/com/android/server/companion/PackageUtils.java
@@ -28,6 +28,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.app.AppOpsManager;
import android.companion.CompanionDeviceService;
import android.content.ComponentName;
import android.content.Context;
@@ -222,4 +223,15 @@
return requestingPackageSignatureAllowlisted;
}
+
+ /**
+ * Check if restricted settings is enabled for a side-loaded app.
+ */
+ public static boolean isRestrictedSettingsAllowed(
+ Context context, String packageName, int uid) {
+ final int mode = context.getSystemService(AppOpsManager.class).noteOpNoThrow(
+ AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS, uid,
+ packageName, /* attributionTag= */ null, /* message= */ null);
+ return mode == AppOpsManager.MODE_ALLOWED;
+ }
}
diff --git a/services/companion/java/com/android/server/companion/virtual/Android.bp b/services/companion/java/com/android/server/companion/virtual/Android.bp
index 6526c78..4a2030f 100644
--- a/services/companion/java/com/android/server/companion/virtual/Android.bp
+++ b/services/companion/java/com/android/server/companion/virtual/Android.bp
@@ -1,3 +1,7 @@
+package {
+ default_team: "trendy_team_xr_framework",
+}
+
java_aconfig_library {
name: "virtualdevice_flags_lib",
aconfig_declarations: "virtualdevice_flags",
diff --git a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
index 31766f2..ff07619 100644
--- a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
+++ b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
@@ -29,6 +29,7 @@
import android.companion.virtual.VirtualDeviceManager.ActivityListener;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
+import android.content.AttributionSource;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ActivityInfo;
@@ -44,6 +45,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.BlockedAppStreamingActivity;
+import com.android.modules.expresslog.Counter;
import java.util.Set;
@@ -104,6 +106,8 @@
@EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
public static final long ALLOW_SECURE_ACTIVITY_DISPLAY_ON_REMOTE_DEVICE = 201712607L;
@NonNull
+ private final AttributionSource mAttributionSource;
+ @NonNull
private final ArraySet<UserHandle> mAllowedUsers;
@GuardedBy("mGenericWindowPolicyControllerLock")
private boolean mActivityLaunchAllowedByDefault;
@@ -144,6 +148,7 @@
*
* @param windowFlags The window flags that this controller is interested in.
* @param systemWindowFlags The system window flags that this controller is interested in.
+ * @param attributionSource The AttributionSource of the VirtualDevice owner application.
* @param allowedUsers The set of users that are allowed to stream in this display.
* @param activityLaunchAllowedByDefault Whether activities are default allowed to be launched
* or blocked.
@@ -169,6 +174,7 @@
public GenericWindowPolicyController(
int windowFlags,
int systemWindowFlags,
+ AttributionSource attributionSource,
@NonNull ArraySet<UserHandle> allowedUsers,
boolean activityLaunchAllowedByDefault,
@NonNull Set<ComponentName> activityPolicyExemptions,
@@ -184,6 +190,7 @@
boolean showTasksInHostDeviceRecents,
@Nullable ComponentName customHomeComponent) {
super();
+ mAttributionSource = attributionSource;
mAllowedUsers = allowedUsers;
mActivityLaunchAllowedByDefault = activityLaunchAllowedByDefault;
mActivityPolicyExemptions = activityPolicyExemptions;
@@ -436,6 +443,12 @@
if (!mIsMirrorDisplay && mActivityBlockedCallback != null) {
mActivityBlockedCallback.onActivityBlocked(mDisplayId, activityInfo);
}
+ if (android.companion.virtualdevice.flags.Flags.metricsCollection()) {
+ Counter.logIncrementWithUid(
+ "virtual_devices.value_activity_blocked_count",
+ mAttributionSource.getUid());
+ }
+
}
private static boolean isAllowedByPolicy(boolean allowedByDefault,
diff --git a/services/companion/java/com/android/server/companion/virtual/InputController.java b/services/companion/java/com/android/server/companion/virtual/InputController.java
index 1b49f18e..9b72288 100644
--- a/services/companion/java/com/android/server/companion/virtual/InputController.java
+++ b/services/companion/java/com/android/server/companion/virtual/InputController.java
@@ -21,6 +21,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.StringDef;
+import android.content.AttributionSource;
import android.graphics.PointF;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.input.InputDeviceIdentifier;
@@ -38,6 +39,7 @@
import android.os.IInputConstants;
import android.os.RemoteException;
import android.util.ArrayMap;
+import android.util.Log;
import android.util.Slog;
import android.view.Display;
import android.view.InputDevice;
@@ -45,6 +47,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.modules.expresslog.Counter;
import com.android.server.LocalServices;
import com.android.server.input.InputManagerInternal;
@@ -98,11 +101,12 @@
private final DisplayManagerInternal mDisplayManagerInternal;
private final InputManagerInternal mInputManagerInternal;
private final WindowManager mWindowManager;
+ private final AttributionSource mAttributionSource;
private final DeviceCreationThreadVerifier mThreadVerifier;
InputController(@NonNull Handler handler,
- @NonNull WindowManager windowManager) {
- this(new NativeWrapper(), handler, windowManager,
+ @NonNull WindowManager windowManager, AttributionSource attributionSource) {
+ this(new NativeWrapper(), handler, windowManager, attributionSource,
// Verify that virtual devices are not created on the handler thread.
() -> !handler.getLooper().isCurrentThread());
}
@@ -110,12 +114,14 @@
@VisibleForTesting
InputController(@NonNull NativeWrapper nativeWrapper,
@NonNull Handler handler, @NonNull WindowManager windowManager,
+ AttributionSource attributionSource,
@NonNull DeviceCreationThreadVerifier threadVerifier) {
mHandler = handler;
mNativeWrapper = nativeWrapper;
mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
mWindowManager = windowManager;
+ mAttributionSource = attributionSource;
mThreadVerifier = threadVerifier;
}
@@ -838,6 +844,33 @@
new InputDeviceDescriptor(ptr, binderDeathRecipient, type, displayId, phys,
deviceName, inputDeviceId));
}
+
+ if (android.companion.virtualdevice.flags.Flags.metricsCollection()) {
+ String metricId = getMetricIdForInputType(type);
+ if (metricId != null) {
+ Counter.logIncrementWithUid(metricId, mAttributionSource.getUid());
+ }
+ }
+ }
+
+ private static String getMetricIdForInputType(@InputDeviceDescriptor.Type int type) {
+ switch (type) {
+ case InputDeviceDescriptor.TYPE_KEYBOARD:
+ return "virtual_devices.value_virtual_keyboard_created_count";
+ case InputDeviceDescriptor.TYPE_MOUSE:
+ return "virtual_devices.value_virtual_mouse_created_count";
+ case InputDeviceDescriptor.TYPE_TOUCHSCREEN:
+ return "virtual_devices.value_virtual_touchscreen_created_count";
+ case InputDeviceDescriptor.TYPE_DPAD:
+ return "virtual_devices.value_virtual_dpad_created_count";
+ case InputDeviceDescriptor.TYPE_NAVIGATION_TOUCHPAD:
+ return "virtual_devices.value_virtual_navigationtouchpad_created_count";
+ case InputDeviceDescriptor.TYPE_STYLUS:
+ return "virtual_devices.value_virtual_stylus_created_count";
+ default:
+ Log.e(TAG, "No metric known for input type: " + type);
+ return null;
+ }
}
@VisibleForTesting
diff --git a/services/companion/java/com/android/server/companion/virtual/SensorController.java b/services/companion/java/com/android/server/companion/virtual/SensorController.java
index e9241dd..cf48180 100644
--- a/services/companion/java/com/android/server/companion/virtual/SensorController.java
+++ b/services/companion/java/com/android/server/companion/virtual/SensorController.java
@@ -23,6 +23,7 @@
import android.companion.virtual.sensor.VirtualSensor;
import android.companion.virtual.sensor.VirtualSensorConfig;
import android.companion.virtual.sensor.VirtualSensorEvent;
+import android.content.AttributionSource;
import android.hardware.SensorDirectChannel;
import android.os.Binder;
import android.os.IBinder;
@@ -35,6 +36,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.modules.expresslog.Counter;
import com.android.server.LocalServices;
import com.android.server.sensors.SensorManagerInternal;
@@ -71,14 +73,18 @@
private List<VirtualSensor> mVirtualSensorList = null;
@NonNull
+ private final AttributionSource mAttributionSource;
+ @NonNull
private final SensorManagerInternal.RuntimeSensorCallback mRuntimeSensorCallback;
private final SensorManagerInternal mSensorManagerInternal;
private final VirtualDeviceManagerInternal mVdmInternal;
public SensorController(@NonNull IVirtualDevice virtualDevice, int virtualDeviceId,
+ @NonNull AttributionSource attributionSource,
@Nullable IVirtualSensorCallback virtualSensorCallback,
@NonNull List<VirtualSensorConfig> sensors) {
mVirtualDeviceId = virtualDeviceId;
+ mAttributionSource = attributionSource;
mRuntimeSensorCallback = new RuntimeSensorCallbackWrapper(virtualSensorCallback);
mSensorManagerInternal = LocalServices.getService(SensorManagerInternal.class);
mVdmInternal = LocalServices.getService(VirtualDeviceManagerInternal.class);
@@ -139,6 +145,11 @@
mSensorDescriptors.put(sensorToken, sensorDescriptor);
mVirtualSensors.put(handle, sensor);
}
+ if (android.companion.virtualdevice.flags.Flags.metricsCollection()) {
+ Counter.logIncrementWithUid(
+ "virtual_devices.value_virtual_sensors_created_count",
+ mAttributionSource.getUid());
+ }
}
boolean sendSensorEvent(@NonNull IBinder token, @NonNull VirtualSensorEvent event) {
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index f13f49a..6d731b2 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -104,6 +104,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.BlockedAppStreamingActivity;
+import com.android.modules.expresslog.Counter;
import com.android.server.LocalServices;
import com.android.server.companion.virtual.GenericWindowPolicyController.RunningAppsChangedListener;
import com.android.server.companion.virtual.audio.VirtualAudioController;
@@ -152,6 +153,8 @@
private final int mOwnerUid;
private final VirtualDeviceLog mVirtualDeviceLog;
private final String mOwnerPackageName;
+ @NonNull
+ private final AttributionSource mAttributionSource;
private final int mDeviceId;
@Nullable
private final String mPersistentDeviceId;
@@ -288,6 +291,7 @@
super(PermissionEnforcer.fromContext(context));
mVirtualDeviceLog = virtualDeviceLog;
mOwnerPackageName = attributionSource.getPackageName();
+ mAttributionSource = attributionSource;
UserHandle ownerUserHandle = UserHandle.getUserHandleForUid(attributionSource.getUid());
mContext = context.createContextAsUser(ownerUserHandle, 0);
mAssociationInfo = associationInfo;
@@ -307,11 +311,11 @@
if (inputController == null) {
mInputController = new InputController(
context.getMainThreadHandler(),
- context.getSystemService(WindowManager.class));
+ context.getSystemService(WindowManager.class), mAttributionSource);
} else {
mInputController = inputController;
}
- mSensorController = new SensorController(this, mDeviceId,
+ mSensorController = new SensorController(this, mDeviceId, mAttributionSource,
mParams.getVirtualSensorCallback(), mParams.getVirtualSensorConfigs());
mCameraAccessController = cameraAccessController;
if (mCameraAccessController != null) {
@@ -620,7 +624,7 @@
}
if (mVirtualAudioController == null) {
- mVirtualAudioController = new VirtualAudioController(mContext);
+ mVirtualAudioController = new VirtualAudioController(mContext, mAttributionSource);
GenericWindowPolicyController gwpc = mVirtualDisplays.get(
displayId).getWindowPolicyController();
mVirtualAudioController.startListening(gwpc, routingCallback,
@@ -1028,7 +1032,7 @@
if (mVirtualCameraController == null) {
throw new UnsupportedOperationException("Virtual camera controller is not available");
}
- mVirtualCameraController.registerCamera(cameraConfig);
+ mVirtualCameraController.registerCamera(cameraConfig, mAttributionSource);
}
@Override // Binder call
@@ -1110,6 +1114,7 @@
final GenericWindowPolicyController gwpc = new GenericWindowPolicyController(
FLAG_SECURE,
SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS,
+ mAttributionSource,
getAllowedUserHandles(),
activityLaunchAllowedByDefault,
mActivityPolicyExemptions,
@@ -1179,6 +1184,11 @@
Binder.restoreCallingIdentity(token);
}
+ if (android.companion.virtualdevice.flags.Flags.metricsCollection()) {
+ Counter.logIncrementWithUid(
+ "virtual_devices.value_virtual_display_created_count",
+ mAttributionSource.getUid());
+ }
return displayId;
}
@@ -1220,6 +1230,12 @@
if ((display.getFlags() & FLAG_SECURE) == 0) {
showToastWhereUidIsRunning(uid, com.android.internal.R.string.vdm_secure_window,
Toast.LENGTH_LONG, mContext.getMainLooper());
+
+ if (android.companion.virtualdevice.flags.Flags.metricsCollection()) {
+ Counter.logIncrementWithUid(
+ "virtual_devices.value_secure_window_blocked_count",
+ mAttributionSource.getUid());
+ }
}
}
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index 2168cb2..9ad73ca 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -482,6 +482,11 @@
}
});
}
+ if (android.companion.virtualdevice.flags.Flags.metricsCollection()) {
+ Counter.logIncrementWithUid(
+ "virtual_devices.value_virtual_devices_created_with_uid_count",
+ attributionSource.getUid());
+ }
return virtualDevice;
}
diff --git a/services/companion/java/com/android/server/companion/virtual/audio/VirtualAudioController.java b/services/companion/java/com/android/server/companion/virtual/audio/VirtualAudioController.java
index c91877a..4bffb76 100644
--- a/services/companion/java/com/android/server/companion/virtual/audio/VirtualAudioController.java
+++ b/services/companion/java/com/android/server/companion/virtual/audio/VirtualAudioController.java
@@ -23,6 +23,7 @@
import android.annotation.RequiresPermission;
import android.companion.virtual.audio.IAudioConfigChangedCallback;
import android.companion.virtual.audio.IAudioRoutingCallback;
+import android.content.AttributionSource;
import android.content.Context;
import android.media.AudioManager;
import android.media.AudioPlaybackConfiguration;
@@ -35,6 +36,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.modules.expresslog.Counter;
import com.android.server.companion.virtual.GenericWindowPolicyController;
import com.android.server.companion.virtual.GenericWindowPolicyController.RunningAppsChangedListener;
import com.android.server.companion.virtual.audio.AudioPlaybackDetector.AudioPlaybackCallback;
@@ -70,10 +72,16 @@
@GuardedBy("mCallbackLock")
private IAudioConfigChangedCallback mConfigChangedCallback;
- public VirtualAudioController(Context context) {
+ public VirtualAudioController(Context context, AttributionSource attributionSource) {
mContext = context;
mAudioPlaybackDetector = new AudioPlaybackDetector(context);
mAudioRecordingDetector = new AudioRecordingDetector(context);
+
+ if (android.companion.virtualdevice.flags.Flags.metricsCollection()) {
+ Counter.logIncrementWithUid(
+ "virtual_devices.value_virtual_audio_created_count",
+ attributionSource.getUid());
+ }
}
/**
diff --git a/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraController.java b/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraController.java
index 2d82b5e..3bb1e33 100644
--- a/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraController.java
+++ b/services/companion/java/com/android/server/companion/virtual/camera/VirtualCameraController.java
@@ -26,6 +26,7 @@
import android.companion.virtual.camera.VirtualCameraConfig;
import android.companion.virtualcamera.IVirtualCameraService;
import android.companion.virtualcamera.VirtualCameraConfiguration;
+import android.content.AttributionSource;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
@@ -35,6 +36,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.modules.expresslog.Counter;
import java.io.PrintWriter;
import java.util.Map;
@@ -76,7 +78,8 @@
*
* @param cameraConfig The {@link VirtualCameraConfig} sent by the client.
*/
- public void registerCamera(@NonNull VirtualCameraConfig cameraConfig) {
+ public void registerCamera(@NonNull VirtualCameraConfig cameraConfig,
+ AttributionSource attributionSource) {
checkConfigByPolicy(cameraConfig);
connectVirtualCameraServiceIfNeeded();
@@ -97,6 +100,11 @@
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
+ if (android.companion.virtualdevice.flags.Flags.metricsCollection()) {
+ Counter.logIncrementWithUid(
+ "virtual_devices.value_virtual_camera_created_count",
+ attributionSource.getUid());
+ }
}
/**
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index 05010f88..f1776f4 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -17,7 +17,7 @@
package com.android.server;
import static android.app.Flags.modesApi;
-import static android.app.Flags.enableNightModeCache;
+import static android.app.Flags.enableNightModeBinderCache;
import static android.app.UiModeManager.ContrastUtils.CONTRAST_DEFAULT_VALUE;
import static android.app.UiModeManager.DEFAULT_PRIORITY;
import static android.app.UiModeManager.MODE_ATTENTION_THEME_OVERLAY_OFF;
@@ -148,7 +148,7 @@
@Override
public void set(int mode) {
mNightModeValue = mode;
- if (enableNightModeCache()) {
+ if (enableNightModeBinderCache()) {
UiModeManager.invalidateNightModeCache();
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index d97731c..ff83797 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -243,7 +243,7 @@
/**
* The default value to {@link #KEY_ENABLE_NEW_OOMADJ}.
*/
- private static final boolean DEFAULT_ENABLE_NEW_OOM_ADJ = false;
+ private static final boolean DEFAULT_ENABLE_NEW_OOM_ADJ = Flags.oomadjusterCorrectnessRewrite();
/**
* Same as {@link TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index bfdcb95..bbd3ad0b 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -930,7 +930,7 @@
* Tracks all users with computed color resources by ThemeOverlaycvontroller
*/
@GuardedBy("this")
- private final Set<Integer> mThemeOverlayReadiness = new HashSet<>();
+ private final Set<Integer> mThemeOverlayReadyUsers = new HashSet<>();
/**
* Tracks association information for a particular package along with debuggability.
@@ -2351,7 +2351,7 @@
mService.startBroadcastObservers();
} else if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
mService.mPackageWatchdog.onPackagesReady();
- mService.setHomeTimeout();
+ mService.scheduleHomeTimeout();
}
}
@@ -5327,40 +5327,43 @@
/**
* Starts Home if there is no completion signal from ThemeOverlayController
*/
- private void setHomeTimeout() {
+ private void scheduleHomeTimeout() {
if (enableHomeDelay() && mHasHomeDelay.compareAndSet(false, true)) {
+ int userId = mUserController.getCurrentUserId();
mHandler.postDelayed(() -> {
- if (!getThemeOverlayReadiness()) {
+ if (!isThemeOverlayReady(userId)) {
Slog.d(TAG,
"ThemeHomeDelay: ThemeOverlayController not responding, launching "
+ "Home after "
+ HOME_LAUNCH_TIMEOUT_MS + "ms");
- setThemeOverlayReady(true);
+ setThemeOverlayReady(userId);
}
}, HOME_LAUNCH_TIMEOUT_MS);
}
}
/**
- * Used by ThemeOverlayController to notify all listeners for
- * color palette readiness.
+ * Used by ThemeOverlayController to notify when color
+ * palette is ready.
+ *
+ * @param userId The ID of the user where ThemeOverlayController is ready.
+ *
+ * @throws RemoteException
+ *
* @hide
*/
@Override
- public void setThemeOverlayReady(boolean readiness) {
+ public void setThemeOverlayReady(@UserIdInt int userId) {
enforceCallingPermission(Manifest.permission.SET_THEME_OVERLAY_CONTROLLER_READY,
"setThemeOverlayReady");
- int currentUserId = mUserController.getCurrentUserId();
-
- boolean updateReadiness;
- synchronized (mThemeOverlayReadiness) {
- updateReadiness = readiness ? mThemeOverlayReadiness.add(currentUserId)
- : mThemeOverlayReadiness.remove(currentUserId);
+ boolean updateUser;
+ synchronized (mThemeOverlayReadyUsers) {
+ updateUser = mThemeOverlayReadyUsers.add(userId);
}
- if (updateReadiness && readiness && enableHomeDelay()) {
- mAtmInternal.startHomeOnAllDisplays(currentUserId, "setThemeOverlayReady");
+ if (updateUser && enableHomeDelay()) {
+ mAtmInternal.startHomeOnAllDisplays(userId, "setThemeOverlayReady");
}
}
@@ -5370,10 +5373,9 @@
*
* @hide
*/
- public boolean getThemeOverlayReadiness() {
- int uid = mUserController.getCurrentUserId();
- synchronized (mThemeOverlayReadiness) {
- return mThemeOverlayReadiness.contains(uid);
+ public boolean isThemeOverlayReady(int userId) {
+ synchronized (mThemeOverlayReadyUsers) {
+ return mThemeOverlayReadyUsers.contains(userId);
}
}
@@ -18114,8 +18116,8 @@
// Clean up various services by removing the user
mBatteryStatsService.onUserRemoved(userId);
- synchronized (mThemeOverlayReadiness) {
- mThemeOverlayReadiness.remove(userId);
+ synchronized (mThemeOverlayReadyUsers) {
+ mThemeOverlayReadyUsers.remove(userId);
}
}
@@ -19477,8 +19479,8 @@
}
@Override
- public boolean getThemeOverlayReadiness() {
- return ActivityManagerService.this.getThemeOverlayReadiness();
+ public boolean isThemeOverlayReady(int userId) {
+ return ActivityManagerService.this.isThemeOverlayReady(userId);
}
}
diff --git a/services/core/java/com/android/server/am/AppProfiler.java b/services/core/java/com/android/server/am/AppProfiler.java
index e4956b3..0ce1407 100644
--- a/services/core/java/com/android/server/am/AppProfiler.java
+++ b/services/core/java/com/android/server/am/AppProfiler.java
@@ -2305,6 +2305,8 @@
}
ps.addCpuTimeLocked(st.rel_utime, st.rel_stime);
}
+ EventLogTags.writeAmCpu(st.pid, st.uid, st.baseName,
+ st.rel_uptime, st.rel_utime, st.rel_stime);
}
}
diff --git a/services/core/java/com/android/server/am/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags
index 0f75ad48..8038732 100644
--- a/services/core/java/com/android/server/am/EventLogTags.logtags
+++ b/services/core/java/com/android/server/am/EventLogTags.logtags
@@ -127,6 +127,9 @@
30102 am_foreground_service_stop (User|1|5),(Component Name|3),(allowWhileInUse|1),(startReasonCode|3),(targetSdk|1|1),(callerTargetSdk|1|1),(notificationWasDeferred|1),(notificationShown|1),(durationMs|1|3),(startForegroundCount|1|1),(stopReason|3),(fgsType|1)
30103 am_foreground_service_timed_out (User|1|5),(Component Name|3),(allowWhileInUse|1),(startReasonCode|3),(targetSdk|1|1),(callerTargetSdk|1|1),(notificationWasDeferred|1),(notificationShown|1),(durationMs|1|3),(startForegroundCount|1|1),(stopReason|3),(fgsType|1)
+# Report collection of cpu used by a process
+30104 am_cpu (Pid|2|5),(UID|2|5),(Base Name|3),(Uptime|2|3),(Stime|2|3),(Utime|2|3)
+
# Intent Sender redirect for UserHandle.USER_CURRENT
30110 am_intent_sender_redirect_user (userId|1|5)
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index fca1199..0cc6494 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -1241,15 +1241,15 @@
Map<String, PackageState> packageStates) {
synchronized (this) {
// Remove what may have been added during persistence parsing
- for (int i = mUidStates.size() - 1; i >= 0; i--) {
- int uid = mUidStates.keyAt(i);
+ for (int uidIdx = mUidStates.size() - 1; uidIdx >= 0; uidIdx--) {
+ int uid = mUidStates.keyAt(uidIdx);
if (knownUids.get(uid, false)) {
if (uid >= Process.FIRST_APPLICATION_UID) {
- ArrayMap<String, Ops> pkgOps = mUidStates.valueAt(i).pkgOps;
- for (int j = 0; j < pkgOps.size(); j++) {
- String pkgName = pkgOps.keyAt(j);
+ ArrayMap<String, Ops> pkgOps = mUidStates.valueAt(uidIdx).pkgOps;
+ for (int pkgIdx = pkgOps.size() - 1; pkgIdx >= 0; pkgIdx--) {
+ String pkgName = pkgOps.keyAt(pkgIdx);
if (!packageStates.containsKey(pkgName)) {
- pkgOps.removeAt(j);
+ pkgOps.removeAt(pkgIdx);
continue;
}
AndroidPackage pkg = packageStates.get(pkgName).getAndroidPackage();
@@ -1258,11 +1258,11 @@
}
}
if (pkgOps.isEmpty()) {
- mUidStates.remove(i);
+ mUidStates.removeAt(uidIdx);
}
}
} else {
- mUidStates.removeAt(i);
+ mUidStates.removeAt(uidIdx);
}
}
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 559a1d6..ef93400 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -964,6 +964,8 @@
private final HardeningEnforcer mHardeningEnforcer;
+ private final AudioVolumeGroupHelperBase mAudioVolumeGroupHelper;
+
private final Object mSupportedSystemUsagesLock = new Object();
@GuardedBy("mSupportedSystemUsagesLock")
private @AttributeSystemUsage int[] mSupportedSystemUsages =
@@ -974,6 +976,13 @@
return "card=" + card + ";device=" + device;
}
+ private static class AudioVolumeGroupHelper extends AudioVolumeGroupHelperBase {
+ @Override
+ public List<AudioVolumeGroup> getAudioVolumeGroups() {
+ return AudioVolumeGroup.getAudioVolumeGroups();
+ }
+ }
+
public static final class Lifecycle extends SystemService {
private AudioService mService;
@@ -983,6 +992,7 @@
AudioSystemAdapter.getDefaultAdapter(),
SystemServerAdapter.getDefaultAdapter(context),
SettingsAdapter.getDefaultAdapter(),
+ new AudioVolumeGroupHelper(),
new DefaultAudioPolicyFacade(),
null);
@@ -1062,16 +1072,19 @@
/**
* @param context
* @param audioSystem Adapter for {@link AudioSystem}
- * @param systemServer Adapter for privilieged functionality for system server components
+ * @param systemServer Adapter for privileged functionality for system server components
* @param settings Adapter for {@link Settings}
+ * @param audioVolumeGroupHelper Adapter for {@link AudioVolumeGroup}
+ * @param audioPolicy Interface of a facade to IAudioPolicyManager
* @param looper Looper to use for the service's message handler. If this is null, an
* {@link AudioSystemThread} is created as the messaging thread instead.
*/
public AudioService(Context context, AudioSystemAdapter audioSystem,
SystemServerAdapter systemServer, SettingsAdapter settings,
- AudioPolicyFacade audioPolicy, @Nullable Looper looper) {
- this (context, audioSystem, systemServer, settings, audioPolicy, looper,
- context.getSystemService(AppOpsManager.class),
+ AudioVolumeGroupHelperBase audioVolumeGroupHelper, AudioPolicyFacade audioPolicy,
+ @Nullable Looper looper) {
+ this (context, audioSystem, systemServer, settings, audioVolumeGroupHelper,
+ audioPolicy, looper, context.getSystemService(AppOpsManager.class),
PermissionEnforcer.fromContext(context));
}
@@ -1080,14 +1093,18 @@
* @param audioSystem Adapter for {@link AudioSystem}
* @param systemServer Adapter for privilieged functionality for system server components
* @param settings Adapter for {@link Settings}
+ * @param audioVolumeGroupHelper Adapter for {@link AudioVolumeGroup}
+ * @param audioPolicy Interface of a facade to IAudioPolicyManager
* @param looper Looper to use for the service's message handler. If this is null, an
* {@link AudioSystemThread} is created as the messaging thread instead.
+ * @param appOps {@link AppOpsManager} system service
+ * @param enforcer Used for permission enforcing
*/
@RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
public AudioService(Context context, AudioSystemAdapter audioSystem,
SystemServerAdapter systemServer, SettingsAdapter settings,
- AudioPolicyFacade audioPolicy, @Nullable Looper looper, AppOpsManager appOps,
- @NonNull PermissionEnforcer enforcer) {
+ AudioVolumeGroupHelperBase audioVolumeGroupHelper, AudioPolicyFacade audioPolicy,
+ @Nullable Looper looper, AppOpsManager appOps, @NonNull PermissionEnforcer enforcer) {
super(enforcer);
sLifecycleLogger.enqueue(new EventLogger.StringEvent("AudioService()"));
mContext = context;
@@ -1096,6 +1113,7 @@
mAudioSystem = audioSystem;
mSystemServer = systemServer;
+ mAudioVolumeGroupHelper = audioVolumeGroupHelper;
mSettings = settings;
mAudioPolicy = audioPolicy;
mPlatformType = AudioSystem.getPlatformType(context);
@@ -2104,7 +2122,7 @@
// verify permissions
super.getAudioVolumeGroups_enforcePermission();
- return AudioVolumeGroup.getAudioVolumeGroups();
+ return mAudioVolumeGroupHelper.getAudioVolumeGroups();
}
private void checkAllAliasStreamVolumes() {
@@ -3803,7 +3821,7 @@
}
/**
- * Loops on aliasted stream, update the mute cache attribute of each
+ * Loops on aliased stream, update the mute cache attribute of each
* {@see AudioService#VolumeStreamState}, and then apply the change.
* It prevents to unnecessary {@see AudioSystem#setStreamVolume} done for each stream
* and aliases before mute change changed and after.
@@ -4040,18 +4058,6 @@
}
}
- @Nullable
- private AudioVolumeGroup getAudioVolumeGroupById(int volumeGroupId) {
- for (AudioVolumeGroup avg : AudioVolumeGroup.getAudioVolumeGroups()) {
- if (avg.getId() == volumeGroupId) {
- return avg;
- }
- }
-
- Log.e(TAG, ": invalid volume group id: " + volumeGroupId + " requested");
- return null;
- }
-
@Override
@android.annotation.EnforcePermission(anyOf = {
MODIFY_AUDIO_SETTINGS_PRIVILEGED,
@@ -8252,7 +8258,7 @@
index = 1;
}
// Set the volume index
- AudioSystem.setVolumeIndexForAttributes(mAudioAttributes, index, device);
+ mAudioSystem.setVolumeIndexForAttributes(mAudioAttributes, index, device);
}
@GuardedBy("AudioService.VolumeStreamState.class")
diff --git a/services/core/java/com/android/server/audio/AudioSystemAdapter.java b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
index 49ab19a..7202fa2 100644
--- a/services/core/java/com/android/server/audio/AudioSystemAdapter.java
+++ b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
@@ -551,6 +551,11 @@
return AudioSystem.setStreamVolumeIndexAS(stream, index, device);
}
+ /** Same as {@link AudioSystem#setVolumeIndexForAttributes(AudioAttributes, int, int)} */
+ public int setVolumeIndexForAttributes(AudioAttributes attributes, int index, int device) {
+ return AudioSystem.setVolumeIndexForAttributes(attributes, index, device);
+ }
+
/**
* Same as {@link AudioSystem#setPhoneState(int, int)}
* @param state
diff --git a/services/core/java/com/android/server/audio/AudioVolumeGroupHelperBase.java b/services/core/java/com/android/server/audio/AudioVolumeGroupHelperBase.java
new file mode 100644
index 0000000..6f4de5bc
--- /dev/null
+++ b/services/core/java/com/android/server/audio/AudioVolumeGroupHelperBase.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 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.server.audio;
+
+import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
+
+import android.media.audiopolicy.AudioVolumeGroup;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/** Abstract class for {@link AudioVolumeGroup} related helper methods. */
+@VisibleForTesting(visibility = PACKAGE)
+public class AudioVolumeGroupHelperBase {
+ public List<AudioVolumeGroup> getAudioVolumeGroups() {
+ return new ArrayList<>();
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/AuthService.java b/services/core/java/com/android/server/biometrics/AuthService.java
index 3f3540e..48bf9f4 100644
--- a/services/core/java/com/android/server/biometrics/AuthService.java
+++ b/services/core/java/com/android/server/biometrics/AuthService.java
@@ -216,6 +216,13 @@
public String[] getFaceAidlInstances() {
return ServiceManager.getDeclaredInstances(IFace.DESCRIPTOR);
}
+
+ /**
+ * Allows to test with handlers.
+ */
+ public BiometricHandlerProvider getBiometricHandlerProvider() {
+ return BiometricHandlerProvider.getInstance();
+ }
}
private final class AuthServiceImpl extends IAuthService.Stub {
@@ -772,7 +779,6 @@
}
if (com.android.server.biometrics.Flags.deHidl()) {
- Slog.d(TAG, "deHidl flag is on.");
registerAuthenticators();
} else {
// Registers HIDL and AIDL authenticators, but only HIDL configs need to be provided.
@@ -783,10 +789,16 @@
}
private void registerAuthenticators() {
- registerFingerprintSensors(mInjector.getFingerprintAidlInstances(),
- mInjector.getFingerprintConfiguration(getContext()));
- registerFaceSensors(mInjector.getFaceAidlInstances(),
- mInjector.getFaceConfiguration(getContext()));
+ BiometricHandlerProvider handlerProvider = mInjector.getBiometricHandlerProvider();
+
+ handlerProvider.getFingerprintHandler().post(() ->
+ registerFingerprintSensors(mInjector.getFingerprintAidlInstances(),
+ mInjector.getFingerprintConfiguration(getContext()), getContext(),
+ mInjector.getFingerprintService()));
+ handlerProvider.getFaceHandler().post(() ->
+ registerFaceSensors(mInjector.getFaceAidlInstances(),
+ mInjector.getFaceConfiguration(getContext()), getContext(),
+ mInjector.getFaceService()));
registerIrisSensors(mInjector.getIrisConfiguration(getContext()));
}
@@ -837,15 +849,18 @@
}
}
- private void registerFaceSensors(final String[] faceAidlInstances,
- final String[] hidlConfigStrings) {
+ /**
+ * This method is invoked on {@link BiometricHandlerProvider.mFaceHandler}.
+ */
+ private static void registerFaceSensors(final String[] faceAidlInstances,
+ final String[] hidlConfigStrings, final Context context,
+ final IFaceService faceService) {
final FaceSensorConfigurations mFaceSensorConfigurations =
new FaceSensorConfigurations(hidlConfigStrings != null
&& hidlConfigStrings.length > 0);
if (hidlConfigStrings != null && hidlConfigStrings.length > 0) {
- mFaceSensorConfigurations.addHidlConfigs(
- hidlConfigStrings, getContext());
+ mFaceSensorConfigurations.addHidlConfigs(hidlConfigStrings, context);
}
if (faceAidlInstances != null && faceAidlInstances.length > 0) {
@@ -854,7 +869,6 @@
ServiceManager.waitForDeclaredService(name))));
}
- final IFaceService faceService = mInjector.getFaceService();
if (faceService != null) {
try {
faceService.registerAuthenticatorsLegacy(mFaceSensorConfigurations);
@@ -866,14 +880,18 @@
}
}
- private void registerFingerprintSensors(final String[] fingerprintAidlInstances,
- final String[] hidlConfigStrings) {
+ /**
+ * This method is invoked on {@link BiometricHandlerProvider.mFingerprintHandler}.
+ */
+ private static void registerFingerprintSensors(final String[] fingerprintAidlInstances,
+ final String[] hidlConfigStrings, final Context context,
+ final IFingerprintService fingerprintService) {
final FingerprintSensorConfigurations mFingerprintSensorConfigurations =
new FingerprintSensorConfigurations(!(hidlConfigStrings != null
&& hidlConfigStrings.length > 0));
if (hidlConfigStrings != null && hidlConfigStrings.length > 0) {
- mFingerprintSensorConfigurations.addHidlSensors(hidlConfigStrings, getContext());
+ mFingerprintSensorConfigurations.addHidlSensors(hidlConfigStrings, context);
}
if (fingerprintAidlInstances != null && fingerprintAidlInstances.length > 0) {
@@ -882,7 +900,6 @@
ServiceManager.waitForDeclaredService(name))));
}
- final IFingerprintService fingerprintService = mInjector.getFingerprintService();
if (fingerprintService != null) {
try {
fingerprintService.registerAuthenticatorsLegacy(mFingerprintSensorConfigurations);
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index cd064ae..38051c1 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -21,8 +21,8 @@
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.hardware.devicestate.DeviceState.FLAG_CANCEL_OVERRIDE_REQUESTS;
import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
-import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE;
-import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE;
+import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE_IDENTIFIER;
+import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE_IDENTIFIER;
import static com.android.server.devicestate.OverrideRequest.OVERRIDE_REQUEST_TYPE_BASE_STATE;
import static com.android.server.devicestate.OverrideRequest.OVERRIDE_REQUEST_TYPE_EMULATED_STATE;
@@ -1065,7 +1065,8 @@
}
private final class DeviceStateProviderListener implements DeviceStateProvider.Listener {
- @IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE) int mCurrentBaseState;
+ @IntRange(from = MINIMUM_DEVICE_STATE_IDENTIFIER, to = MAXIMUM_DEVICE_STATE_IDENTIFIER)
+ int mCurrentBaseState;
@Override
public void onSupportedDeviceStatesChanged(DeviceState[] newDeviceStates,
@@ -1078,8 +1079,10 @@
@Override
public void onStateChanged(
- @IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE) int identifier) {
- if (identifier < MINIMUM_DEVICE_STATE || identifier > MAXIMUM_DEVICE_STATE) {
+ @IntRange(from = MINIMUM_DEVICE_STATE_IDENTIFIER, to =
+ MAXIMUM_DEVICE_STATE_IDENTIFIER) int identifier) {
+ if (identifier < MINIMUM_DEVICE_STATE_IDENTIFIER
+ || identifier > MAXIMUM_DEVICE_STATE_IDENTIFIER) {
throw new IllegalArgumentException("Invalid identifier: " + identifier);
}
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateProvider.java b/services/core/java/com/android/server/devicestate/DeviceStateProvider.java
index 65b393a..b865c1d9 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateProvider.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateProvider.java
@@ -16,8 +16,8 @@
package com.android.server.devicestate;
-import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE;
-import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE;
+import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE_IDENTIFIER;
+import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE_IDENTIFIER;
import android.annotation.IntDef;
import android.annotation.IntRange;
@@ -133,10 +133,12 @@
*
* @param identifier the identifier of the new device state.
*
- * @throws IllegalArgumentException if the state is less than {@link MINIMUM_DEVICE_STATE}
- * or greater than {@link MAXIMUM_DEVICE_STATE}.
+ * @throws IllegalArgumentException if the state is less than
+ * {@link MINIMUM_DEVICE_STATE_IDENTIFIER} or greater than
+ * {@link MAXIMUM_DEVICE_STATE_IDENTIFIER}.
*/
void onStateChanged(
- @IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE) int identifier);
+ @IntRange(from = MINIMUM_DEVICE_STATE_IDENTIFIER, to =
+ MAXIMUM_DEVICE_STATE_IDENTIFIER) int identifier);
}
}
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 fab769e..40e9198 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
@@ -75,6 +75,6 @@
protected enum Type {
THERMAL,
POWER,
- BEDTIME_MODE,
+ 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 2c02fc6..bc5fcb4 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
@@ -156,6 +156,8 @@
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) {
+ return BrightnessInfo.BRIGHTNESS_MAX_REASON_WEAR_BEDTIME_MODE;
} else {
Slog.wtf(TAG, "BrightnessMaxReason not mapped for type=" + mClamperType);
return BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE;
diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamper.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamper.java
index 7e853bf..1902e35 100644
--- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamper.java
+++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamper.java
@@ -64,7 +64,7 @@
@NonNull
@Override
Type getType() {
- return Type.BEDTIME_MODE;
+ return Type.WEAR_BEDTIME_MODE;
}
@Override
diff --git a/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionManagerInternal.java b/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionManagerInternal.java
index 6a6e6ab..c2c82ed 100644
--- a/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionManagerInternal.java
+++ b/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionManagerInternal.java
@@ -16,6 +16,7 @@
package com.android.server.grammaticalinflection;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.res.Configuration;
@@ -55,11 +56,11 @@
*
*/
public abstract @Configuration.GrammaticalGender int retrieveSystemGrammaticalGender(
- Configuration configuration);
+ @NonNull Configuration configuration);
/**
* Whether the package can get the system grammatical gender or not.
*/
- public abstract boolean canGetSystemGrammaticalGender(int uid, String packageName);
+ public abstract boolean canGetSystemGrammaticalGender(int uid, @Nullable String packageName);
}
diff --git a/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionService.java b/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionService.java
index d01f54f..0bcb26d 100644
--- a/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionService.java
+++ b/services/core/java/com/android/server/grammaticalinflection/GrammaticalInflectionService.java
@@ -329,8 +329,9 @@
private void checkCallerIsSystem() {
int callingUid = Binder.getCallingUid();
- if (callingUid != Process.SYSTEM_UID && callingUid != Process.SHELL_UID) {
- throw new SecurityException("Caller is not system and shell.");
+ if (callingUid != Process.SYSTEM_UID && callingUid != Process.SHELL_UID
+ && callingUid != Process.ROOT_UID) {
+ throw new SecurityException("Caller is not system, shell and root.");
}
}
@@ -354,12 +355,11 @@
final File file = getGrammaticalGenderFile(userId);
synchronized (mLock) {
if (!file.exists()) {
- Log.d(TAG, "User " + userId + "doesn't have the grammatical gender file.");
+ Log.d(TAG, "User " + userId + " doesn't have the grammatical gender file.");
return;
}
if (mGrammaticalGenderCache.indexOfKey(userId) < 0) {
- try {
- InputStream in = new FileInputStream(file);
+ try (FileInputStream in = new FileInputStream(file)) {
final TypedXmlPullParser parser = Xml.resolvePullParser(in);
mGrammaticalGenderCache.put(userId, getGrammaticalGenderFromXml(parser));
} catch (IOException | XmlPullParserException e) {
diff --git a/services/core/java/com/android/server/input/debug/FocusEventDebugView.java b/services/core/java/com/android/server/input/debug/FocusEventDebugView.java
index 6eec0de..3ffd2e1 100644
--- a/services/core/java/com/android/server/input/debug/FocusEventDebugView.java
+++ b/services/core/java/com/android/server/input/debug/FocusEventDebugView.java
@@ -34,6 +34,7 @@
import android.util.TypedValue;
import android.view.Gravity;
import android.view.InputDevice;
+import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.RoundedCorner;
@@ -335,7 +336,15 @@
final int unicodeChar = event.getUnicodeChar();
if (unicodeChar != 0) {
- return new String(Character.toChars(unicodeChar));
+ if ((unicodeChar & KeyCharacterMap.COMBINING_ACCENT) != 0) {
+ // Show combining character
+ final int combiningChar = KeyCharacterMap.getCombiningChar(
+ unicodeChar & KeyCharacterMap.COMBINING_ACCENT_MASK);
+ // Return the Unicode dotted circle as part of the label as it is used is used to
+ // illustrate the effect of a combining marks
+ return "\u25cc" + String.valueOf((char) combiningChar);
+ }
+ return String.valueOf((char) unicodeChar);
}
final var label = KeyEvent.keyCodeToString(event.getKeyCode());
diff --git a/services/core/java/com/android/server/inputmethod/IInputMethodClientInvoker.java b/services/core/java/com/android/server/inputmethod/IInputMethodClientInvoker.java
index 977dbff..84a59b4 100644
--- a/services/core/java/com/android/server/inputmethod/IInputMethodClientInvoker.java
+++ b/services/core/java/com/android/server/inputmethod/IInputMethodClientInvoker.java
@@ -117,6 +117,30 @@
}
@AnyThread
+ void onStartInputResult(@NonNull InputBindResult res, int startInputSeq) {
+ if (mIsProxy) {
+ onStartInputResultInternal(res, startInputSeq);
+ } else {
+ mHandler.post(() -> onStartInputResultInternal(res, startInputSeq));
+ }
+ }
+
+ @AnyThread
+ private void onStartInputResultInternal(@NonNull InputBindResult res, int startInputSeq) {
+ try {
+ mTarget.onStartInputResult(res, startInputSeq);
+ } catch (RemoteException e) {
+ logRemoteException(e);
+ } finally {
+ // Dispose the channel if the input method is not local to this process
+ // because the remote proxy will get its own copy when unparceled.
+ if (res.channel != null && mIsProxy) {
+ res.channel.dispose();
+ }
+ }
+ }
+
+ @AnyThread
void onBindAccessibilityService(@NonNull InputBindResult res, int id) {
if (mIsProxy) {
onBindAccessibilityServiceInternal(res, id);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index bc169ca..5574d18 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -1539,7 +1539,13 @@
@Override
public void onStart() {
mService.publishLocalService();
- publishBinderService(Context.INPUT_METHOD_SERVICE, mService, false /*allowIsolated*/,
+ IInputMethodManager.Stub service;
+ if (Flags.useZeroJankProxy()) {
+ service = new ZeroJankProxy(mService.mHandler::post, mService);
+ } else {
+ service = mService;
+ }
+ publishBinderService(Context.INPUT_METHOD_SERVICE, service, false /*allowIsolated*/,
DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL | DUMP_FLAG_PROTO);
}
@@ -2216,6 +2222,14 @@
}
}
+ @Nullable
+ ClientState getClientState(IInputMethodClient client) {
+ synchronized (ImfLock.class) {
+ return mClientController.getClient(client.asBinder());
+ }
+ }
+
+ // TODO(b/314150112): Move this to ClientController.
@GuardedBy("ImfLock.class")
void unbindCurrentClientLocked(@UnbindReason int unbindClientReason) {
if (mCurClient != null) {
@@ -3741,6 +3755,20 @@
return imeClientFocus == WindowManagerInternal.ImeClientFocusResult.HAS_IME_FOCUS;
}
+ //TODO(b/293640003): merge with startInputOrWindowGainedFocus once Flags.useZeroJankProxy()
+ // is enabled.
+ @Override
+ public void startInputOrWindowGainedFocusAsync(
+ @StartInputReason int startInputReason, IInputMethodClient client, IBinder windowToken,
+ @StartInputFlags int startInputFlags, @SoftInputModeFlags int softInputMode,
+ int windowFlags, @Nullable EditorInfo editorInfo,
+ IRemoteInputConnection inputConnection,
+ IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
+ int unverifiedTargetSdkVersion, @UserIdInt int userId,
+ @NonNull ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq) {
+ // implemented by ZeroJankProxy
+ }
+
@NonNull
@Override
public InputBindResult startInputOrWindowGainedFocus(
diff --git a/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java b/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java
new file mode 100644
index 0000000..692fd7dc
--- /dev/null
+++ b/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java
@@ -0,0 +1,406 @@
+/*
+ * 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.
+ */
+
+/*
+ * 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.server.inputmethod;
+
+import static com.android.server.inputmethod.InputMethodManagerService.TAG;
+
+import android.Manifest;
+import android.annotation.BinderThread;
+import android.annotation.EnforcePermission;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.UserIdInt;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
+import android.util.ExceptionUtils;
+import android.util.Slog;
+import android.view.WindowManager;
+import android.view.inputmethod.CursorAnchorInfo;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.ImeTracker;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodManager;
+import android.view.inputmethod.InputMethodSubtype;
+import android.window.ImeOnBackInvokedDispatcher;
+
+import com.android.internal.inputmethod.DirectBootAwareness;
+import com.android.internal.inputmethod.IConnectionlessHandwritingCallback;
+import com.android.internal.inputmethod.IImeTracker;
+import com.android.internal.inputmethod.IInputMethodClient;
+import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
+import com.android.internal.inputmethod.IRemoteInputConnection;
+import com.android.internal.inputmethod.InputBindResult;
+import com.android.internal.inputmethod.SoftInputShowHideReason;
+import com.android.internal.inputmethod.StartInputFlags;
+import com.android.internal.inputmethod.StartInputReason;
+import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
+import com.android.internal.view.IInputMethodManager;
+
+import java.io.FileDescriptor;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+
+/**
+ * A proxy that processes all {@link IInputMethodManager} calls asynchronously.
+ * @hide
+ */
+public class ZeroJankProxy extends IInputMethodManager.Stub {
+
+ private final IInputMethodManager mInner;
+ private final Executor mExecutor;
+
+ ZeroJankProxy(Executor executor, IInputMethodManager inner) {
+ mInner = inner;
+ mExecutor = executor;
+ }
+
+ private void offload(ThrowingRunnable r) {
+ offloadInner(r);
+ }
+
+ private void offload(Runnable r) {
+ offloadInner(r);
+ }
+
+ private void offloadInner(Runnable r) {
+ boolean useThrowingRunnable = r instanceof ThrowingRunnable;
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(() -> {
+ final long inner = Binder.clearCallingIdentity();
+ // Restoring calling identity, so we can still do permission checks on caller.
+ Binder.restoreCallingIdentity(identity);
+ try {
+ try {
+ if (useThrowingRunnable) {
+ ((ThrowingRunnable) r).runOrThrow();
+ } else {
+ r.run();
+ }
+ } catch (Exception e) {
+ Slog.e(TAG, "Error in async call", e);
+ throw ExceptionUtils.propagate(e);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(inner);
+ }
+ });
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
+ @Override
+ public void addClient(IInputMethodClient client, IRemoteInputConnection inputConnection,
+ int selfReportedDisplayId) throws RemoteException {
+ offload(() -> mInner.addClient(client, inputConnection, selfReportedDisplayId));
+ }
+
+ @Override
+ public InputMethodInfo getCurrentInputMethodInfoAsUser(int userId) throws RemoteException {
+ return mInner.getCurrentInputMethodInfoAsUser(userId);
+ }
+
+ @Override
+ public List<InputMethodInfo> getInputMethodList(
+ int userId, @DirectBootAwareness int directBootAwareness) throws RemoteException {
+ return mInner.getInputMethodList(userId, directBootAwareness);
+ }
+
+ @Override
+ public List<InputMethodInfo> getEnabledInputMethodList(int userId) throws RemoteException {
+ return mInner.getEnabledInputMethodList(userId);
+ }
+
+ @Override
+ public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(String imiId,
+ boolean allowsImplicitlyEnabledSubtypes, int userId)
+ throws RemoteException {
+ return mInner.getEnabledInputMethodSubtypeList(imiId, allowsImplicitlyEnabledSubtypes,
+ userId);
+ }
+
+ @Override
+ public InputMethodSubtype getLastInputMethodSubtype(int userId) throws RemoteException {
+ return mInner.getLastInputMethodSubtype(userId);
+ }
+
+ @Override
+ public boolean showSoftInput(IInputMethodClient client, IBinder windowToken,
+ @Nullable ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags,
+ int lastClickTooType, ResultReceiver resultReceiver,
+ @SoftInputShowHideReason int reason)
+ throws RemoteException {
+ offload(() -> mInner.showSoftInput(client, windowToken, statsToken, flags, lastClickTooType,
+ resultReceiver, reason));
+ return true;
+ }
+
+ @Override
+ public boolean hideSoftInput(IInputMethodClient client, IBinder windowToken,
+ @Nullable ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags,
+ ResultReceiver resultReceiver, @SoftInputShowHideReason int reason)
+ throws RemoteException {
+ offload(() -> mInner.hideSoftInput(client, windowToken, statsToken, flags, resultReceiver,
+ reason));
+ return true;
+ }
+
+ @Override
+ public void startInputOrWindowGainedFocusAsync(
+ @StartInputReason int startInputReason,
+ IInputMethodClient client, IBinder windowToken,
+ @StartInputFlags int startInputFlags,
+ @WindowManager.LayoutParams.SoftInputModeFlags int softInputMode,
+ int windowFlags, @Nullable EditorInfo editorInfo,
+ IRemoteInputConnection inputConnection,
+ IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
+ int unverifiedTargetSdkVersion, @UserIdInt int userId,
+ @NonNull ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq)
+ throws RemoteException {
+ offload(() -> {
+ InputBindResult result = mInner.startInputOrWindowGainedFocus(startInputReason, client,
+ windowToken, startInputFlags, softInputMode, windowFlags,
+ editorInfo,
+ inputConnection, remoteAccessibilityInputConnection,
+ unverifiedTargetSdkVersion,
+ userId, imeDispatcher);
+ sendOnStartInputResult(client, result, startInputSeq);
+ });
+ }
+
+ @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+ @Override
+ public InputBindResult startInputOrWindowGainedFocus(
+ @StartInputReason int startInputReason,
+ IInputMethodClient client, IBinder windowToken,
+ @StartInputFlags int startInputFlags,
+ @WindowManager.LayoutParams.SoftInputModeFlags int softInputMode,
+ int windowFlags, @Nullable EditorInfo editorInfo,
+ IRemoteInputConnection inputConnection,
+ IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
+ int unverifiedTargetSdkVersion, @UserIdInt int userId,
+ @NonNull ImeOnBackInvokedDispatcher imeDispatcher)
+ throws RemoteException {
+ // Should never be called when flag is enabled i.e. when this proxy is used.
+ return null;
+ }
+
+ @Override
+ public void showInputMethodPickerFromClient(IInputMethodClient client,
+ int auxiliarySubtypeMode)
+ throws RemoteException {
+ offload(() -> mInner.showInputMethodPickerFromClient(client, auxiliarySubtypeMode));
+ }
+
+ @EnforcePermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+ @Override
+ public void showInputMethodPickerFromSystem(int auxiliarySubtypeMode, int displayId)
+ throws RemoteException {
+ mInner.showInputMethodPickerFromSystem(auxiliarySubtypeMode, displayId);
+ }
+
+ @EnforcePermission(Manifest.permission.TEST_INPUT_METHOD)
+ @Override
+ public boolean isInputMethodPickerShownForTest() throws RemoteException {
+ super.isInputMethodPickerShownForTest_enforcePermission();
+ return mInner.isInputMethodPickerShownForTest();
+ }
+
+ @Override
+ public InputMethodSubtype getCurrentInputMethodSubtype(int userId) throws RemoteException {
+ return mInner.getCurrentInputMethodSubtype(userId);
+ }
+
+ @Override
+ public void setAdditionalInputMethodSubtypes(String imiId, InputMethodSubtype[] subtypes,
+ @UserIdInt int userId) throws RemoteException {
+ mInner.setAdditionalInputMethodSubtypes(imiId, subtypes, userId);
+ }
+
+ @Override
+ public void setExplicitlyEnabledInputMethodSubtypes(String imeId,
+ @NonNull int[] subtypeHashCodes, @UserIdInt int userId) throws RemoteException {
+ mInner.setExplicitlyEnabledInputMethodSubtypes(imeId, subtypeHashCodes, userId);
+ }
+
+ @Override
+ public int getInputMethodWindowVisibleHeight(IInputMethodClient client)
+ throws RemoteException {
+ return mInner.getInputMethodWindowVisibleHeight(client);
+ }
+
+ @Override
+ public void reportPerceptibleAsync(IBinder windowToken, boolean perceptible)
+ throws RemoteException {
+ // Already async TODO(b/293640003): ordering issues?
+ mInner.reportPerceptibleAsync(windowToken, perceptible);
+ }
+
+ @EnforcePermission(Manifest.permission.INTERNAL_SYSTEM_WINDOW)
+ @Override
+ public void removeImeSurface() throws RemoteException {
+ mInner.removeImeSurface();
+ }
+
+ @Override
+ public void removeImeSurfaceFromWindowAsync(IBinder windowToken) throws RemoteException {
+ mInner.removeImeSurfaceFromWindowAsync(windowToken);
+ }
+
+ @Override
+ public void startProtoDump(byte[] bytes, int i, String s) throws RemoteException {
+ mInner.startProtoDump(bytes, i, s);
+ }
+
+ @Override
+ public boolean isImeTraceEnabled() throws RemoteException {
+ return mInner.isImeTraceEnabled();
+ }
+
+ @EnforcePermission(Manifest.permission.CONTROL_UI_TRACING)
+ @Override
+ public void startImeTrace() throws RemoteException {
+ mInner.startImeTrace();
+ }
+
+ @EnforcePermission(Manifest.permission.CONTROL_UI_TRACING)
+ @Override
+ public void stopImeTrace() throws RemoteException {
+ mInner.stopImeTrace();
+ }
+
+ @Override
+ public void startStylusHandwriting(IInputMethodClient client)
+ throws RemoteException {
+ offload(() -> mInner.startStylusHandwriting(client));
+ }
+
+ @Override
+ public void startConnectionlessStylusHandwriting(IInputMethodClient client, int userId,
+ @Nullable CursorAnchorInfo cursorAnchorInfo, @Nullable String delegatePackageName,
+ @Nullable String delegatorPackageName,
+ @NonNull IConnectionlessHandwritingCallback callback) throws RemoteException {
+ offload(() -> mInner.startConnectionlessStylusHandwriting(
+ client, userId, cursorAnchorInfo, delegatePackageName, delegatorPackageName,
+ callback));
+ }
+
+ @Override
+ public boolean acceptStylusHandwritingDelegation(
+ @NonNull IInputMethodClient client,
+ @UserIdInt int userId,
+ @NonNull String delegatePackageName,
+ @NonNull String delegatorPackageName,
+ @InputMethodManager.HandwritingDelegateFlags int flags) {
+ try {
+ return CompletableFuture.supplyAsync(() -> {
+ try {
+ return mInner.acceptStylusHandwritingDelegation(
+ client, userId, delegatePackageName, delegatorPackageName, flags);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }, this::offload).get();
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ } catch (ExecutionException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public void prepareStylusHandwritingDelegation(
+ @NonNull IInputMethodClient client,
+ @UserIdInt int userId,
+ @NonNull String delegatePackageName,
+ @NonNull String delegatorPackageName) {
+ offload(() -> mInner.prepareStylusHandwritingDelegation(
+ client, userId, delegatePackageName, delegatorPackageName));
+ }
+
+ @Override
+ public boolean isStylusHandwritingAvailableAsUser(int userId, boolean connectionless)
+ throws RemoteException {
+ return mInner.isStylusHandwritingAvailableAsUser(userId, connectionless);
+ }
+
+ @EnforcePermission("android.permission.TEST_INPUT_METHOD")
+ @Override
+ public void addVirtualStylusIdForTestSession(IInputMethodClient client)
+ throws RemoteException {
+ mInner.addVirtualStylusIdForTestSession(client);
+ }
+
+ @EnforcePermission("android.permission.TEST_INPUT_METHOD")
+ @Override
+ public void setStylusWindowIdleTimeoutForTest(IInputMethodClient client, long timeout)
+ throws RemoteException {
+ mInner.setStylusWindowIdleTimeoutForTest(client, timeout);
+ }
+
+ @Override
+ public IImeTracker getImeTrackerService() throws RemoteException {
+ return mInner.getImeTrackerService();
+ }
+
+ @BinderThread
+ @Override
+ public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
+ @Nullable FileDescriptor err,
+ @NonNull String[] args, @Nullable ShellCallback callback,
+ @NonNull ResultReceiver resultReceiver) throws RemoteException {
+ ((InputMethodManagerService) mInner).onShellCommand(
+ in, out, err, args, callback, resultReceiver);
+ }
+
+ private void sendOnStartInputResult(
+ IInputMethodClient client, InputBindResult res, int startInputSeq) {
+ InputMethodManagerService service = (InputMethodManagerService) mInner;
+ final ClientState cs = service.getClientState(client);
+ if (cs != null && cs.mClient != null) {
+ cs.mClient.onStartInputResult(res, startInputSeq);
+ } else {
+ // client is unbound.
+ Slog.i(TAG, "Client that requested startInputOrWindowGainedFocus is no longer"
+ + " bound. InputBindResult: " + res + " for startInputSeq: " + startInputSeq);
+ }
+ }
+}
+
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index f97f6d2..e7e8096 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -105,7 +105,6 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
-import java.util.Map;
import java.util.Set;
/**
@@ -154,7 +153,7 @@
private UsageStatsManagerInternal mUsageStatsManagerInternal;
/* Maps uid with all user engaging session tokens associated to it */
- private final Map<Integer, Set<MediaSession.Token>> mUserEngagingSessions = new HashMap<>();
+ private final SparseArray<Set<MediaSession.Token>> mUserEngagingSessions = new SparseArray<>();
// The FullUserRecord of the current users. (i.e. The foreground user that isn't a profile)
// It's always not null after the MediaSessionService is started.
@@ -313,14 +312,15 @@
}
user.mPriorityStack.onSessionActiveStateChanged(record);
}
- boolean allowRunningInForeground = record.isActive()
- && (playbackState == null || playbackState.isActive());
+ boolean isUserEngaged =
+ record.isActive() && (playbackState == null || playbackState.isActive());
Log.d(TAG, "onSessionActiveStateChanged: "
+ "record=" + record
+ "playbackState=" + playbackState
- + "allowRunningInForeground=" + allowRunningInForeground);
- setForegroundServiceAllowance(record, allowRunningInForeground);
+ + "allowRunningInForeground=" + isUserEngaged);
+ setForegroundServiceAllowance(record, /* allowRunningInForeground= */ isUserEngaged);
+ reportMediaInteractionEvent(record, isUserEngaged);
mHandler.postSessionsChanged(record);
}
}
@@ -418,12 +418,14 @@
}
user.mPriorityStack.onPlaybackStateChanged(record, shouldUpdatePriority);
if (playbackState != null) {
- boolean allowRunningInForeground = playbackState.isActive() && record.isActive();
+ boolean isUserEngaged = playbackState.isActive() && record.isActive();
Log.d(TAG, "onSessionPlaybackStateChanged: "
+ "record=" + record
+ "playbackState=" + playbackState
- + "allowRunningInForeground=" + allowRunningInForeground);
- setForegroundServiceAllowance(record, allowRunningInForeground);
+ + "allowRunningInForeground=" + isUserEngaged);
+ setForegroundServiceAllowance(
+ record, /* allowRunningInForeground= */ isUserEngaged);
+ reportMediaInteractionEvent(record, isUserEngaged);
}
}
}
@@ -591,6 +593,7 @@
Log.d(TAG, "destroySessionLocked: record=" + session);
setForegroundServiceAllowance(session, /* allowRunningInForeground= */ false);
+ reportMediaInteractionEvent(session, /* userEngaged= */ false);
mHandler.postSessionsChanged(session);
}
@@ -609,11 +612,9 @@
if (allowRunningInForeground) {
mActivityManagerInternal.startForegroundServiceDelegate(
foregroundServiceDelegationOptions, /* connection= */ null);
- reportMediaInteractionEvent(record, /* userEngaged= */ true);
} else {
mActivityManagerInternal.stopForegroundServiceDelegate(
foregroundServiceDelegationOptions);
- reportMediaInteractionEvent(record, /* userEngaged= */ false);
}
}
@@ -625,29 +626,27 @@
String packageName = record.getPackageName();
int sessionUid = record.getUid();
- String actionToLog = null;
MediaSession.Token token = ((MediaSessionRecord) record).getSessionToken();
if (userEngaged) {
- if (!mUserEngagingSessions.containsKey(sessionUid)) {
+ if (!mUserEngagingSessions.contains(sessionUid)) {
mUserEngagingSessions.put(sessionUid, new HashSet<>());
- actionToLog = "start";
+ reportUserInteractionEvent(/* action= */ "start", record.getUserId(), packageName);
}
mUserEngagingSessions.get(sessionUid).add(token);
- } else if (mUserEngagingSessions.containsKey(sessionUid)) {
+ } else if (mUserEngagingSessions.contains(sessionUid)) {
mUserEngagingSessions.get(sessionUid).remove(token);
if (mUserEngagingSessions.get(sessionUid).isEmpty()) {
- actionToLog = "stop";
+ reportUserInteractionEvent(/* action= */ "stop", record.getUserId(), packageName);
mUserEngagingSessions.remove(sessionUid);
}
}
+ }
- if (actionToLog != null) {
- PersistableBundle extras = new PersistableBundle();
- extras.putString(UsageStatsManager.EXTRA_EVENT_CATEGORY, "android.media");
- extras.putString(UsageStatsManager.EXTRA_EVENT_ACTION, actionToLog);
- mUsageStatsManagerInternal.reportUserInteractionEvent(
- packageName, record.getUserId(), extras);
- }
+ private void reportUserInteractionEvent(String action, int userId, String packageName) {
+ PersistableBundle extras = new PersistableBundle();
+ extras.putString(UsageStatsManager.EXTRA_EVENT_CATEGORY, "android.media");
+ extras.putString(UsageStatsManager.EXTRA_EVENT_ACTION, action);
+ mUsageStatsManagerInternal.reportUserInteractionEvent(packageName, userId, extras);
}
void tempAllowlistTargetPkgIfPossible(int targetUid, String targetPackage,
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index b5c51af..796d8d7 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -340,6 +340,8 @@
static final String TAG = NetworkPolicyLogger.TAG;
private static final boolean LOGD = NetworkPolicyLogger.LOGD;
private static final boolean LOGV = NetworkPolicyLogger.LOGV;
+ // TODO: b/304347838 - Remove once the feature is in staging.
+ private static final boolean ALWAYS_RESTRICT_BACKGROUND_NETWORK = false;
/**
* No opportunistic quota could be calculated from user data plan or data settings.
@@ -1061,7 +1063,8 @@
}
// The flag is boot-stable.
- mBackgroundNetworkRestricted = Flags.networkBlockedForTopSleepingAndAbove();
+ mBackgroundNetworkRestricted = ALWAYS_RESTRICT_BACKGROUND_NETWORK
+ && Flags.networkBlockedForTopSleepingAndAbove();
if (mBackgroundNetworkRestricted) {
// Firewall rules and UidBlockedState will get updated in
// updateRulesForGlobalChangeAL below.
diff --git a/services/core/java/com/android/server/notification/Android.bp b/services/core/java/com/android/server/notification/Android.bp
index f26a25b..9be4358 100644
--- a/services/core/java/com/android/server/notification/Android.bp
+++ b/services/core/java/com/android/server/notification/Android.bp
@@ -1,3 +1,7 @@
+package {
+ default_team: "trendy_team_system_ui_please_use_a_more_specific_subteam_if_possible_",
+}
+
java_aconfig_library {
name: "notification_flags_lib",
aconfig_declarations: "notification_flags",
@@ -9,4 +13,4 @@
srcs: [
"flags.aconfig",
],
-}
\ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 53ae60b..7455fe0 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -5720,6 +5720,14 @@
}
@Override
+ @Condition.State
+ public int getAutomaticZenRuleState(@NonNull String id) {
+ Objects.requireNonNull(id, "id is null");
+ enforcePolicyAccess(Binder.getCallingUid(), "getAutomaticZenRuleState");
+ return mZenModeHelper.getAutomaticZenRuleState(id);
+ }
+
+ @Override
public void setAutomaticZenRuleState(String id, Condition condition) {
Objects.requireNonNull(id, "id is null");
Objects.requireNonNull(condition, "Condition is null");
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 54de197..efb8c84 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -856,6 +856,20 @@
}
}
+ @Condition.State
+ int getAutomaticZenRuleState(String id) {
+ synchronized (mConfigLock) {
+ if (mConfig == null) {
+ return Condition.STATE_UNKNOWN;
+ }
+ ZenRule rule = mConfig.automaticRules.get(id);
+ if (rule == null || !canManageAutomaticZenRule(rule)) {
+ return Condition.STATE_UNKNOWN;
+ }
+ return rule.condition != null ? rule.condition.state : Condition.STATE_FALSE;
+ }
+ }
+
void setAutomaticZenRuleState(String id, Condition condition, @ConfigChangeOrigin int origin,
int callingUid) {
requirePublicOrigin("setAutomaticZenRuleState", origin);
diff --git a/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java b/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
new file mode 100644
index 0000000..a4c4347
--- /dev/null
+++ b/services/core/java/com/android/server/ondeviceintelligence/OnDeviceIntelligenceManagerService.java
@@ -0,0 +1,419 @@
+/*
+ * 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.server.ondeviceintelligence;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.app.AppGlobals;
+import android.app.ondeviceintelligence.Content;
+import android.app.ondeviceintelligence.DownloadCallback;
+import android.app.ondeviceintelligence.Feature;
+import android.app.ondeviceintelligence.IDownloadCallback;
+import android.app.ondeviceintelligence.IFeatureCallback;
+import android.app.ondeviceintelligence.IFeatureDetailsCallback;
+import android.app.ondeviceintelligence.IListFeaturesCallback;
+import android.app.ondeviceintelligence.IOnDeviceIntelligenceManager;
+import android.app.ondeviceintelligence.IProcessingSignal;
+import android.app.ondeviceintelligence.IResponseCallback;
+import android.app.ondeviceintelligence.IStreamingResponseCallback;
+import android.app.ondeviceintelligence.ITokenCountCallback;
+import android.app.ondeviceintelligence.OnDeviceIntelligenceManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
+import android.os.ICancellationSignal;
+import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.provider.DeviceConfig;
+import android.service.ondeviceintelligence.IOnDeviceTrustedInferenceService;
+import android.service.ondeviceintelligence.IRemoteStorageService;
+import android.text.TextUtils;
+import android.util.Slog;
+
+import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.infra.AndroidFuture;
+import com.android.internal.infra.ServiceConnector;
+import com.android.internal.os.BackgroundThread;
+import com.android.server.SystemService;
+
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * This is the system service for handling calls on the {@link OnDeviceIntelligenceManager}. This
+ * service holds connection references to the underlying remote services i.e. the isolated service
+ * {@link android.service.ondeviceintelligence.OnDeviceTrustedInferenceService} and a regular
+ * service counter part {@link android.service.ondeviceintelligence.OnDeviceIntelligenceService}.
+ *
+ * Note: Both the remote services run under the SYSTEM user, as we cannot have separate instance of
+ * the Inference service for each user, due to possible high memory footprint.
+ *
+ * @hide
+ */
+public class OnDeviceIntelligenceManagerService extends SystemService {
+
+ private static final String TAG = OnDeviceIntelligenceManagerService.class.getSimpleName();
+ private static final String KEY_SERVICE_ENABLED = "service_enabled";
+
+ /** Default value in absence of {@link DeviceConfig} override. */
+ private static final boolean DEFAULT_SERVICE_ENABLED = true;
+ private static final String NAMESPACE_ON_DEVICE_INTELLIGENCE = "ondeviceintelligence";
+
+ private final Context mContext;
+ protected final Object mLock = new Object();
+
+
+ private RemoteOnDeviceTrustedInferenceService mRemoteInferenceService;
+ private RemoteOnDeviceIntelligenceService mRemoteOnDeviceIntelligenceService;
+ volatile boolean mIsServiceEnabled;
+
+ public OnDeviceIntelligenceManagerService(Context context) {
+ super(context);
+ mContext = context;
+ }
+
+ @Override
+ public void onStart() {
+ publishBinderService(
+ Context.ON_DEVICE_INTELLIGENCE_SERVICE, new OnDeviceIntelligenceManagerInternal(),
+ /* allowIsolated = */true);
+ }
+
+
+ @Override
+ public void onBootPhase(int phase) {
+ if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
+ DeviceConfig.addOnPropertiesChangedListener(
+ NAMESPACE_ON_DEVICE_INTELLIGENCE,
+ BackgroundThread.getExecutor(),
+ (properties) -> onDeviceConfigChange(properties.getKeyset()));
+
+ mIsServiceEnabled = isServiceEnabled();
+ }
+ }
+
+ private void onDeviceConfigChange(@NonNull Set<String> keys) {
+ if (keys.contains(KEY_SERVICE_ENABLED)) {
+ mIsServiceEnabled = isServiceEnabled();
+ }
+ }
+
+ private boolean isServiceEnabled() {
+ return DeviceConfig.getBoolean(
+ NAMESPACE_ON_DEVICE_INTELLIGENCE,
+ KEY_SERVICE_ENABLED, DEFAULT_SERVICE_ENABLED);
+ }
+
+ private final class OnDeviceIntelligenceManagerInternal extends
+ IOnDeviceIntelligenceManager.Stub {
+ @Override
+ public void getVersion(RemoteCallback remoteCallback) throws RemoteException {
+ Slog.i(TAG, "OnDeviceIntelligenceManagerInternal getVersion");
+ Objects.requireNonNull(remoteCallback);
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG);
+ if (!mIsServiceEnabled) {
+ Slog.w(TAG, "Service not available");
+ remoteCallback.sendResult(null);
+ return;
+ }
+ ensureRemoteIntelligenceServiceInitialized();
+ mRemoteOnDeviceIntelligenceService.post(
+ service -> service.getVersion(remoteCallback));
+ }
+
+ @Override
+ public void getFeature(int id, IFeatureCallback featureCallback) throws RemoteException {
+ Slog.i(TAG, "OnDeviceIntelligenceManagerInternal getFeatures");
+ Objects.requireNonNull(featureCallback);
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG);
+ if (!mIsServiceEnabled) {
+ Slog.w(TAG, "Service not available");
+ featureCallback.onFailure(
+ OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException.ON_DEVICE_INTELLIGENCE_SERVICE_UNAVAILABLE,
+ "OnDeviceIntelligenceManagerService is unavailable",
+ new PersistableBundle());
+ return;
+ }
+ ensureRemoteIntelligenceServiceInitialized();
+ mRemoteOnDeviceIntelligenceService.post(
+ service -> service.getFeature(id, featureCallback));
+ }
+
+ @Override
+ public void listFeatures(IListFeaturesCallback listFeaturesCallback)
+ throws RemoteException {
+ Slog.i(TAG, "OnDeviceIntelligenceManagerInternal getFeatures");
+ Objects.requireNonNull(listFeaturesCallback);
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG);
+ if (!mIsServiceEnabled) {
+ Slog.w(TAG, "Service not available");
+ listFeaturesCallback.onFailure(
+ OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException.ON_DEVICE_INTELLIGENCE_SERVICE_UNAVAILABLE,
+ "OnDeviceIntelligenceManagerService is unavailable",
+ new PersistableBundle());
+ return;
+ }
+ ensureRemoteIntelligenceServiceInitialized();
+ mRemoteOnDeviceIntelligenceService.post(
+ service -> service.listFeatures(listFeaturesCallback));
+ }
+
+ @Override
+ public void getFeatureDetails(Feature feature,
+ IFeatureDetailsCallback featureDetailsCallback)
+ throws RemoteException {
+ Slog.i(TAG, "OnDeviceIntelligenceManagerInternal getFeatureStatus");
+ Objects.requireNonNull(feature);
+ Objects.requireNonNull(featureDetailsCallback);
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG);
+ if (!mIsServiceEnabled) {
+ Slog.w(TAG, "Service not available");
+ featureDetailsCallback.onFailure(
+ OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException.ON_DEVICE_INTELLIGENCE_SERVICE_UNAVAILABLE,
+ "OnDeviceIntelligenceManagerService is unavailable",
+ new PersistableBundle());
+ return;
+ }
+ ensureRemoteIntelligenceServiceInitialized();
+ mRemoteOnDeviceIntelligenceService.post(
+ service -> service.getFeatureDetails(feature, featureDetailsCallback));
+ }
+
+ @Override
+ public void requestFeatureDownload(Feature feature, ICancellationSignal cancellationSignal,
+ IDownloadCallback downloadCallback) throws RemoteException {
+ Slog.i(TAG, "OnDeviceIntelligenceManagerInternal requestFeatureDownload");
+ Objects.requireNonNull(feature);
+ Objects.requireNonNull(downloadCallback);
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG);
+ if (!mIsServiceEnabled) {
+ Slog.w(TAG, "Service not available");
+ downloadCallback.onDownloadFailed(
+ DownloadCallback.DOWNLOAD_FAILURE_STATUS_UNAVAILABLE,
+ "OnDeviceIntelligenceManagerService is unavailable",
+ new PersistableBundle());
+ }
+ ensureRemoteIntelligenceServiceInitialized();
+ mRemoteOnDeviceIntelligenceService.post(
+ service -> service.requestFeatureDownload(feature, cancellationSignal,
+ downloadCallback));
+ }
+
+
+ @Override
+ public void requestTokenCount(Feature feature,
+ Content request, ICancellationSignal cancellationSignal,
+ ITokenCountCallback tokenCountcallback) throws RemoteException {
+ Slog.i(TAG, "OnDeviceIntelligenceManagerInternal prepareFeatureProcessing");
+ Objects.requireNonNull(feature);
+ Objects.requireNonNull(request);
+ Objects.requireNonNull(tokenCountcallback);
+
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG);
+ if (!mIsServiceEnabled) {
+ Slog.w(TAG, "Service not available");
+ tokenCountcallback.onFailure(
+ OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerException.ON_DEVICE_INTELLIGENCE_SERVICE_UNAVAILABLE,
+ "OnDeviceIntelligenceManagerService is unavailable",
+ new PersistableBundle());
+ }
+ ensureRemoteTrustedInferenceServiceInitialized();
+ mRemoteInferenceService.post(
+ service -> service.requestTokenCount(feature, request, cancellationSignal,
+ tokenCountcallback));
+ }
+
+ @Override
+ public void processRequest(Feature feature,
+ Content request,
+ int requestType,
+ ICancellationSignal cancellationSignal,
+ IProcessingSignal processingSignal,
+ IResponseCallback responseCallback)
+ throws RemoteException {
+ Slog.i(TAG, "OnDeviceIntelligenceManagerInternal processRequest");
+ Objects.requireNonNull(feature);
+ Objects.requireNonNull(responseCallback);
+ Objects.requireNonNull(request);
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG);
+ if (!mIsServiceEnabled) {
+ Slog.w(TAG, "Service not available");
+ responseCallback.onFailure(
+ OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException.PROCESSING_ERROR_SERVICE_UNAVAILABLE,
+ "OnDeviceIntelligenceManagerService is unavailable",
+ new PersistableBundle());
+ }
+ ensureRemoteTrustedInferenceServiceInitialized();
+ mRemoteInferenceService.post(
+ service -> service.processRequest(feature, request, requestType,
+ cancellationSignal, processingSignal,
+ responseCallback));
+ }
+
+ @Override
+ public void processRequestStreaming(Feature feature,
+ Content request,
+ int requestType,
+ ICancellationSignal cancellationSignal,
+ IProcessingSignal processingSignal,
+ IStreamingResponseCallback streamingCallback) throws RemoteException {
+ Slog.i(TAG, "OnDeviceIntelligenceManagerInternal processRequestStreaming");
+ Objects.requireNonNull(feature);
+ Objects.requireNonNull(request);
+ Objects.requireNonNull(streamingCallback);
+ mContext.enforceCallingOrSelfPermission(
+ Manifest.permission.USE_ON_DEVICE_INTELLIGENCE, TAG);
+ if (!mIsServiceEnabled) {
+ Slog.w(TAG, "Service not available");
+ streamingCallback.onFailure(
+ OnDeviceIntelligenceManager.OnDeviceIntelligenceManagerProcessingException.PROCESSING_ERROR_SERVICE_UNAVAILABLE,
+ "OnDeviceIntelligenceManagerService is unavailable",
+ new PersistableBundle());
+ }
+ ensureRemoteTrustedInferenceServiceInitialized();
+ mRemoteInferenceService.post(
+ service -> service.processRequestStreaming(feature, request, requestType,
+ cancellationSignal, processingSignal,
+ streamingCallback));
+ }
+ }
+
+ private void ensureRemoteIntelligenceServiceInitialized() throws RemoteException {
+ synchronized (mLock) {
+ if (mRemoteOnDeviceIntelligenceService == null) {
+ String serviceName = mContext.getResources().getString(
+ R.string.config_defaultOnDeviceIntelligenceService);
+ validateService(serviceName, false);
+ mRemoteOnDeviceIntelligenceService = new RemoteOnDeviceIntelligenceService(mContext,
+ ComponentName.unflattenFromString(serviceName),
+ UserHandle.SYSTEM.getIdentifier());
+ }
+ }
+ }
+
+ private void ensureRemoteTrustedInferenceServiceInitialized() throws RemoteException {
+ synchronized (mLock) {
+ if (mRemoteInferenceService == null) {
+ String serviceName = mContext.getResources().getString(
+ R.string.config_defaultOnDeviceTrustedInferenceService);
+ validateService(serviceName, true);
+ mRemoteInferenceService = new RemoteOnDeviceTrustedInferenceService(mContext,
+ ComponentName.unflattenFromString(serviceName),
+ UserHandle.SYSTEM.getIdentifier());
+ mRemoteInferenceService.setServiceLifecycleCallbacks(
+ new ServiceConnector.ServiceLifecycleCallbacks<>() {
+ @Override
+ public void onConnected(
+ @NonNull IOnDeviceTrustedInferenceService service) {
+ try {
+ service.registerRemoteStorageService(
+ getIRemoteStorageService());
+ } catch (RemoteException ex) {
+ Slog.w(TAG, "Failed to send connected event", ex);
+ }
+ }
+ });
+ }
+ }
+ }
+
+ @NonNull
+ private IRemoteStorageService.Stub getIRemoteStorageService() {
+ return new IRemoteStorageService.Stub() {
+ @Override
+ public void getReadOnlyFileDescriptor(
+ String filePath,
+ AndroidFuture<ParcelFileDescriptor> future) {
+ mRemoteOnDeviceIntelligenceService.post(
+ service -> service.getReadOnlyFileDescriptor(
+ filePath, future));
+ }
+
+ @Override
+ public void getReadOnlyFeatureFileDescriptorMap(
+ Feature feature,
+ RemoteCallback remoteCallback)
+ throws RemoteException {
+ mRemoteOnDeviceIntelligenceService.post(
+ service -> service.getReadOnlyFeatureFileDescriptorMap(
+ feature, remoteCallback));
+ }
+ };
+ }
+
+ @GuardedBy("mLock")
+ private void validateService(String serviceName, boolean checkIsolated)
+ throws RemoteException {
+ if (TextUtils.isEmpty(serviceName)) {
+ throw new RuntimeException("");
+ }
+ ComponentName serviceComponent = ComponentName.unflattenFromString(
+ serviceName);
+ ServiceInfo serviceInfo = AppGlobals.getPackageManager().getServiceInfo(
+ serviceComponent,
+ PackageManager.MATCH_DIRECT_BOOT_AWARE
+ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, 0);
+ if (serviceInfo != null) {
+ if (!checkIsolated) {
+ checkServiceRequiresPermission(serviceInfo,
+ Manifest.permission.BIND_ON_DEVICE_INTELLIGENCE_SERVICE);
+ return;
+ }
+
+ checkServiceRequiresPermission(serviceInfo,
+ Manifest.permission.BIND_ON_DEVICE_TRUSTED_SERVICE);
+ if (!isIsolatedService(serviceInfo)) {
+ throw new SecurityException(
+ "Call required an isolated service, but the configured service: "
+ + serviceName + ", is not isolated");
+ }
+ } else {
+ throw new RuntimeException(
+ "Could not find service info for serviceName: " + serviceName);
+ }
+ }
+
+ private static void checkServiceRequiresPermission(ServiceInfo serviceInfo,
+ String requiredPermission) {
+ final String permission = serviceInfo.permission;
+ if (!requiredPermission.equals(permission)) {
+ throw new SecurityException(String.format(
+ "Service %s requires %s permission. Found %s permission",
+ serviceInfo.getComponentName(),
+ requiredPermission,
+ serviceInfo.permission));
+ }
+ }
+
+ @GuardedBy("mLock")
+ private boolean isIsolatedService(@NonNull ServiceInfo serviceInfo) {
+ return (serviceInfo.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) != 0
+ && (serviceInfo.flags & ServiceInfo.FLAG_EXTERNAL_SERVICE) == 0;
+ }
+}
diff --git a/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceIntelligenceService.java b/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceIntelligenceService.java
new file mode 100644
index 0000000..48258d7
--- /dev/null
+++ b/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceIntelligenceService.java
@@ -0,0 +1,56 @@
+/*
+ * 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.server.ondeviceintelligence;
+
+import static android.content.Context.BIND_FOREGROUND_SERVICE;
+import static android.content.Context.BIND_INCLUDE_CAPABILITIES;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.service.ondeviceintelligence.IOnDeviceIntelligenceService;
+import android.service.ondeviceintelligence.OnDeviceIntelligenceService;
+
+import com.android.internal.infra.ServiceConnector;
+
+/**
+ * Manages the connection to the remote on-device intelligence service. Also, handles unbinding
+ * logic set by the service implementation via a Secure Settings flag.
+ */
+public class RemoteOnDeviceIntelligenceService extends
+ ServiceConnector.Impl<IOnDeviceIntelligenceService> {
+ private static final String TAG =
+ RemoteOnDeviceIntelligenceService.class.getSimpleName();
+
+ RemoteOnDeviceIntelligenceService(Context context, ComponentName serviceName,
+ int userId) {
+ super(context, new Intent(
+ OnDeviceIntelligenceService.SERVICE_INTERFACE).setComponent(serviceName),
+ BIND_FOREGROUND_SERVICE | BIND_INCLUDE_CAPABILITIES, userId,
+ IOnDeviceIntelligenceService.Stub::asInterface);
+
+ // Bind right away
+ connect();
+ }
+
+ @Override
+ protected long getAutoDisconnectTimeoutMs() {
+ // Disable automatic unbinding.
+ // TODO: add logic to fetch this flag via SecureSettings.
+ return -1;
+ }
+}
diff --git a/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceTrustedInferenceService.java b/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceTrustedInferenceService.java
new file mode 100644
index 0000000..cc8e788
--- /dev/null
+++ b/services/core/java/com/android/server/ondeviceintelligence/RemoteOnDeviceTrustedInferenceService.java
@@ -0,0 +1,64 @@
+/*
+ * 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.server.ondeviceintelligence;
+
+import static android.content.Context.BIND_FOREGROUND_SERVICE;
+import static android.content.Context.BIND_INCLUDE_CAPABILITIES;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.service.ondeviceintelligence.IOnDeviceTrustedInferenceService;
+import android.service.ondeviceintelligence.OnDeviceTrustedInferenceService;
+
+import com.android.internal.infra.ServiceConnector;
+
+
+/**
+ * Manages the connection to the remote on-device trusted inference service. Also, handles unbinding
+ * logic set by the service implementation via a SecureSettings flag.
+ */
+public class RemoteOnDeviceTrustedInferenceService extends
+ ServiceConnector.Impl<IOnDeviceTrustedInferenceService> {
+ /**
+ * Creates an instance of {@link ServiceConnector}
+ *
+ * See {@code protected} methods for optional parameters you can override.
+ *
+ * @param context to be used for {@link Context#bindServiceAsUser binding} and
+ * {@link Context#unbindService unbinding}
+ * @param userId to be used for {@link Context#bindServiceAsUser binding}
+ */
+ RemoteOnDeviceTrustedInferenceService(Context context, ComponentName serviceName,
+ int userId) {
+ super(context, new Intent(
+ OnDeviceTrustedInferenceService.SERVICE_INTERFACE).setComponent(serviceName),
+ BIND_FOREGROUND_SERVICE | BIND_INCLUDE_CAPABILITIES, userId,
+ IOnDeviceTrustedInferenceService.Stub::asInterface);
+
+ // Bind right away
+ connect();
+ }
+
+
+ @Override
+ protected long getAutoDisconnectTimeoutMs() {
+ // Disable automatic unbinding.
+ // TODO: add logic to fetch this flag via SecureSettings.
+ return -1;
+ }
+}
diff --git a/services/core/java/com/android/server/pdb/PersistentDataBlockService.java b/services/core/java/com/android/server/pdb/PersistentDataBlockService.java
index 133fc8f..59d3d17 100644
--- a/services/core/java/com/android/server/pdb/PersistentDataBlockService.java
+++ b/services/core/java/com/android/server/pdb/PersistentDataBlockService.java
@@ -143,7 +143,8 @@
// Magic number to mark block device as adhering to the format consumed by this service
private static final int PARTITION_TYPE_MARKER = 0x19901873;
/** Size of the block reserved for FRP credential, including 4 bytes for the size header. */
- private static final int FRP_CREDENTIAL_RESERVED_SIZE = 1000;
+ @VisibleForTesting
+ static final int FRP_CREDENTIAL_RESERVED_SIZE = 1000;
/** Maximum size of the FRP credential handle that can be stored. */
@VisibleForTesting
static final int MAX_FRP_CREDENTIAL_HANDLE_SIZE = FRP_CREDENTIAL_RESERVED_SIZE - 4;
@@ -158,7 +159,8 @@
/**
* Size of the block reserved for Test Harness Mode data, including 4 bytes for the size header.
*/
- private static final int TEST_MODE_RESERVED_SIZE = 10000;
+ @VisibleForTesting
+ static final int TEST_MODE_RESERVED_SIZE = 10000;
/** Maximum size of the Test Harness Mode data that can be stored. */
@VisibleForTesting
static final int MAX_TEST_MODE_DATA_SIZE = TEST_MODE_RESERVED_SIZE - 4;
@@ -393,7 +395,8 @@
return totalDataSize;
}
- private long getBlockDeviceSize() {
+ @VisibleForTesting
+ long getBlockDeviceSize() {
synchronized (mLock) {
if (mBlockDeviceSize == -1) {
if (mIsFileBacked) {
@@ -553,26 +556,33 @@
channel.write(buf);
channel.force(true);
- // 3. skip the test mode data and leave it unformatted.
+ // 3. Write the default FRP secret (all zeros).
+ if (mFrpEnforced) {
+ Slog.i(TAG, "Writing FRP secret magic");
+ channel.write(ByteBuffer.wrap(FRP_SECRET_MAGIC));
+
+ Slog.i(TAG, "Writing default FRP secret");
+ channel.write(ByteBuffer.allocate(FRP_SECRET_SIZE));
+ channel.force(true);
+
+ mFrpActive = false;
+ }
+
+ // 4. skip the test mode data and leave it unformatted.
// This is for a feature that enables testing.
channel.position(channel.position() + TEST_MODE_RESERVED_SIZE);
- // 4. wipe the FRP_CREDENTIAL explicitly
+ // 5. wipe the FRP_CREDENTIAL explicitly
buf = ByteBuffer.allocate(FRP_CREDENTIAL_RESERVED_SIZE);
channel.write(buf);
channel.force(true);
- // 5. set unlock = 0 because it's a formatPartitionLocked
+ // 6. set unlock = 0 because it's a formatPartitionLocked
buf = ByteBuffer.allocate(FRP_CREDENTIAL_RESERVED_SIZE);
buf.put((byte)0);
buf.flip();
channel.write(buf);
channel.force(true);
-
- // 6. Write the default FRP secret (all zeros).
- if (mFrpEnforced) {
- writeFrpMagicAndDefaultSecret();
- }
} catch (IOException e) {
Slog.e(TAG, "failed to format block", e);
return;
@@ -616,7 +626,7 @@
// version. If so, we deactivate FRP and set the secret to the default value.
if (isUpgradingFromPreVRelease()) {
Slog.w(TAG, "Upgrading from Android 14 or lower, defaulting FRP secret");
- writeFrpMagicAndDefaultSecret();
+ writeFrpMagicAndDefaultSecretLocked();
mFrpActive = false;
return true;
}
@@ -630,7 +640,7 @@
try {
return deactivateFrp(Files.readAllBytes(Paths.get(frpSecretFile)));
} catch (IOException e) {
- Slog.w(TAG, "Failed to read FRP secret file: " + frpSecretFile + " "
+ Slog.i(TAG, "Failed to read FRP secret file: " + frpSecretFile + " "
+ e.getClass().getSimpleName());
return false;
}
@@ -653,7 +663,8 @@
}
/**
- * Write the provided secret to the FRP secret file in /data and to the /persist partition.
+ * Write the provided secret to the FRP secret file in /data and to the persistent data block
+ * partition.
*
* Writing is a three-step process, to ensure that we can recover from a crash at any point.
*/
@@ -713,7 +724,7 @@
synchronized (mLock) {
if (!hasFrpSecretMagic()) {
Slog.i(TAG, "No FRP secret magic, system must have been upgraded.");
- writeFrpMagicAndDefaultSecret();
+ writeFrpMagicAndDefaultSecretLocked();
}
}
@@ -735,11 +746,9 @@
}
}
- private void writeFrpMagicAndDefaultSecret() {
+ private void writeFrpMagicAndDefaultSecretLocked() {
try (FileChannel channel = getBlockOutputChannelIgnoringFrp()) {
synchronized (mLock) {
- // Write secret first in case we crash between the writes, causing the first write
- // to be synced but the second to be lost.
Slog.i(TAG, "Writing default FRP secret");
channel.position(getFrpSecretDataOffset());
channel.write(ByteBuffer.allocate(FRP_SECRET_SIZE));
@@ -755,6 +764,7 @@
} catch (IOException e) {
Slog.e(TAG, "Failed to write FRP magic and default secret", e);
}
+ computeAndWriteDigestLocked();
}
@VisibleForTesting
@@ -879,7 +889,7 @@
if (printSecret) {
try {
pw.println("FRP secret in " + frpSecretFile + ": " + HexFormat.of()
- .formatHex(Files.readAllBytes(Paths.get(mFrpSecretFile))));
+ .formatHex(Files.readAllBytes(Paths.get(frpSecretFile))));
} catch (IOException e) {
Slog.e(TAG, "Failed to read " + frpSecretFile, e);
}
@@ -1230,6 +1240,7 @@
@Override
public boolean setFactoryResetProtectionSecret(byte[] secret) {
+ enforceConfigureFrpPermission();
enforceUid(Binder.getCallingUid());
if (secret == null || secret.length != FRP_SECRET_SIZE) {
throw new IllegalArgumentException(
diff --git a/services/core/java/com/android/server/pm/BroadcastHelper.java b/services/core/java/com/android/server/pm/BroadcastHelper.java
index e984e9c..23d48e8 100644
--- a/services/core/java/com/android/server/pm/BroadcastHelper.java
+++ b/services/core/java/com/android/server/pm/BroadcastHelper.java
@@ -45,6 +45,8 @@
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
+import android.content.pm.UserProperties;
+import android.multiuser.Flags;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
@@ -361,14 +363,13 @@
final UserInfo parent = ums.getProfileParent(userId);
final int launcherUserId = (parent != null) ? parent.id : userId;
final ComponentName launcherComponent = snapshot.getDefaultHomeActivity(launcherUserId);
- if (launcherComponent != null) {
+ if (launcherComponent != null && canLauncherAccessProfile(launcherComponent, userId)) {
Intent launcherIntent = new Intent(PackageInstaller.ACTION_SESSION_COMMITTED)
.putExtra(PackageInstaller.EXTRA_SESSION, sessionInfo)
.putExtra(Intent.EXTRA_USER, UserHandle.of(userId))
.setPackage(launcherComponent.getPackageName());
mContext.sendBroadcastAsUser(launcherIntent, UserHandle.of(launcherUserId));
}
- // TODO(b/122900055) Change/Remove this and replace with new permission role.
if (appPredictionServicePackage != null) {
Intent predictorIntent = new Intent(PackageInstaller.ACTION_SESSION_COMMITTED)
.putExtra(PackageInstaller.EXTRA_SESSION, sessionInfo)
@@ -378,6 +379,36 @@
}
}
+ /**
+ * A Profile is accessible to launcher in question if:
+ * - It's not hidden for API visibility.
+ * - Hidden, but launcher application has either
+ * {@link Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL} or
+ * {@link Manifest.permission.ACCESS_HIDDEN_PROFILES}
+ * granted.
+ */
+ boolean canLauncherAccessProfile(ComponentName launcherComponent, int userId) {
+ if (android.os.Flags.allowPrivateProfile()
+ && Flags.enablePermissionToAccessHiddenProfiles()) {
+ if (mUmInternal.getUserProperties(userId).getProfileApiVisibility()
+ != UserProperties.PROFILE_API_VISIBILITY_HIDDEN) {
+ return true;
+ }
+ if (mContext.getPackageManager().checkPermission(
+ Manifest.permission.ACCESS_HIDDEN_PROFILES_FULL,
+ launcherComponent.getPackageName())
+ == PackageManager.PERMISSION_GRANTED) {
+ return true;
+ }
+ // TODO(b/122900055) Change/Remove this and replace with new permission role.
+ return mContext.getPackageManager().checkPermission(
+ Manifest.permission.ACCESS_HIDDEN_PROFILES,
+ launcherComponent.getPackageName())
+ == PackageManager.PERMISSION_GRANTED;
+ }
+ return true;
+ }
+
void sendPreferredActivityChangedBroadcast(int userId) {
mHandler.post(() -> {
final IActivityManager am = ActivityManager.getService();
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index f9d8112..76d87ff 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -798,6 +798,10 @@
public ParceledListSlice getShortcutConfigActivities(
String callingPackage, String packageName, UserHandle user)
throws RemoteException {
+ // Not supported for user-profiles with items restricted on home screen.
+ if (!mShortcutServiceInternal.areShortcutsSupportedOnHomeScreen(user.getIdentifier())) {
+ return null;
+ }
return queryActivitiesForUser(callingPackage,
new Intent(Intent.ACTION_CREATE_SHORTCUT).setPackage(packageName), user);
}
@@ -1256,6 +1260,14 @@
@Override
public void pinShortcuts(String callingPackage, String packageName, List<String> ids,
UserHandle targetUser) {
+ if (!mShortcutServiceInternal
+ .areShortcutsSupportedOnHomeScreen(targetUser.getIdentifier())) {
+ // Requires strict ACCESS_SHORTCUTS permission for user-profiles with items
+ // restricted on home screen.
+ ensureStrictAccessShortcutsPermission(callingPackage);
+ } else {
+ ensureShortcutPermission(callingPackage);
+ }
ensureShortcutPermission(callingPackage);
if (!canAccessProfile(targetUser.getIdentifier(), "Cannot pin shortcuts")) {
return;
diff --git a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
index bbce26c..9db4d33 100644
--- a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
+++ b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
@@ -53,7 +53,8 @@
import java.io.File;
import java.io.IOException;
-import java.util.Set;
+import java.util.ArrayList;
+import java.util.List;
final class PackageAbiHelperImpl implements PackageAbiHelper {
@@ -314,7 +315,7 @@
@NonNull
private static String[] getNativelySupportedAbis(@NonNull String[] supportedAbis) {
- Set<String> nativelySupportedAbis = new ArraySet<>();
+ List<String> nativelySupportedAbis = new ArrayList<>();
for (int i = 0; i < supportedAbis.length; i++) {
final String currentAbi = supportedAbis[i];
// In presence of a native bridge this means the Abi is emulated.
diff --git a/services/core/java/com/android/server/pm/PackageArchiver.java b/services/core/java/com/android/server/pm/PackageArchiver.java
index dc97e5f..df4e5a3 100644
--- a/services/core/java/com/android/server/pm/PackageArchiver.java
+++ b/services/core/java/com/android/server/pm/PackageArchiver.java
@@ -122,6 +122,7 @@
public class PackageArchiver {
private static final String TAG = "PackageArchiverService";
+ private static final boolean DEBUG = true;
public static final String EXTRA_UNARCHIVE_INTENT_SENDER =
"android.content.pm.extra.UNARCHIVE_INTENT_SENDER";
@@ -203,6 +204,9 @@
Objects.requireNonNull(intentSender);
Objects.requireNonNull(userHandle);
+ Slog.i(TAG,
+ TextUtils.formatSimple("Requested archival of package %s for user %s.", packageName,
+ userHandle.getIdentifier()));
Computer snapshot = mPm.snapshotComputer();
int binderUserId = userHandle.getIdentifier();
int binderUid = Binder.getCallingUid();
@@ -227,7 +231,7 @@
archiveStateStored[i] = createAndStoreArchiveState(packageName, users[i]);
}
} catch (PackageManager.NameNotFoundException e) {
- Slog.d(TAG, TextUtils.formatSimple("Failed to archive %s with message %s",
+ Slog.e(TAG, TextUtils.formatSimple("Failed to archive %s with message %s",
packageName, e.getMessage()));
throw new ParcelableException(e);
}
@@ -247,7 +251,7 @@
binderPid)
).exceptionally(
e -> {
- Slog.d(TAG, TextUtils.formatSimple("Failed to archive %s with message %s",
+ Slog.e(TAG, TextUtils.formatSimple("Failed to archive %s with message %s",
packageName, e.getMessage()));
sendFailureStatus(intentSender, packageName, e.getMessage());
return null;
@@ -350,17 +354,18 @@
ps.setArchiveState(/* archiveState= */ null, userId);
}
}
- mPm.mBackgroundHandler.post(
- () -> {
- File iconsDir = getIconsDir(packageName, userId);
- if (!iconsDir.exists()) {
- return;
- }
- // TODO(b/319238030) Move this into installd.
- if (!FileUtils.deleteContentsAndDir(iconsDir)) {
- Slog.e(TAG, "Failed to clean up archive files for " + packageName);
- }
- });
+ File iconsDir = getIconsDir(packageName, userId);
+ if (!iconsDir.exists()) {
+ return;
+ }
+ // TODO(b/319238030) Move this into installd.
+ if (!FileUtils.deleteContentsAndDir(iconsDir)) {
+ Slog.e(TAG, "Failed to clean up archive files for " + packageName);
+ } else {
+ if (DEBUG) {
+ Slog.e(TAG, "Deleted icons at " + iconsDir.getAbsolutePath());
+ }
+ }
}
@Nullable
@@ -521,6 +526,9 @@
}
out.flush();
}
+ if (DEBUG && iconFile.exists()) {
+ Slog.i(TAG, "Stored icon at " + iconFile.getAbsolutePath());
+ }
return iconFile.toPath();
}
@@ -1191,6 +1199,9 @@
if (!iconsDir.isDirectory()) {
throw new IOException("Unable to create directory " + iconsDir);
}
+ if (DEBUG) {
+ Slog.i(TAG, "Created icons directory at " + iconsDir.getAbsolutePath());
+ }
}
SELinux.restorecon(iconsDir);
return iconsDir;
diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java
index 6e4f199..29320ae 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerService.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerService.java
@@ -1702,6 +1702,10 @@
Objects.requireNonNull(installerPackageName);
Objects.requireNonNull(userHandle);
+ Slog.i(TAG,
+ TextUtils.formatSimple("Requested archived install of package %s for user %s.",
+ archivedPackageParcel.packageName,
+ userHandle.getIdentifier()));
final int callingUid = Binder.getCallingUid();
final int userId = userHandle.getIdentifier();
final Computer snapshot = mPm.snapshotComputer();
@@ -1737,6 +1741,8 @@
session.addFile(LOCATION_DATA_APP, "base", 0 /*lengthBytes*/,
metadata.toByteArray(), null /*signature*/);
session.commit(statusReceiver, false /*forTransfer*/);
+ Slog.i(TAG, TextUtils.formatSimple("Installed archived app %s.",
+ archivedPackageParcel.packageName));
} catch (IOException e) {
throw ExceptionUtils.wrap(e);
} finally {
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 29242aa..fe65010 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -5358,9 +5358,16 @@
pw.println(sdf.format(date));
if (pus.getArchiveState() != null) {
+ final ArchiveState archiveState = pus.getArchiveState();
pw.print(" archiveTime=");
- date.setTime(pus.getArchiveState().getArchiveTimeMillis());
+ date.setTime(archiveState.getArchiveTimeMillis());
pw.println(sdf.format(date));
+ pw.print(" unarchiveInstallerTitle=");
+ pw.println(archiveState.getInstallerTitle());
+ for (ArchiveState.ArchiveActivityInfo activity : archiveState.getActivityInfos()) {
+ pw.print(" archiveActivityInfo=");
+ pw.println(activity.toString());
+ }
}
pw.print(" uninstallReason=");
@@ -5475,10 +5482,6 @@
}
}
}
- ArchiveState archiveState = userState.getArchiveState();
- if (archiveState != null) {
- pw.print(archiveState.toString());
- }
}
}
diff --git a/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java b/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java
index 1e1f178..47a140a 100644
--- a/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java
+++ b/services/core/java/com/android/server/pm/ShortcutRequestPinProcessor.java
@@ -416,6 +416,10 @@
@VisibleForTesting
Pair<ComponentName, Integer> getRequestPinConfirmationActivity(
int callingUserId, int requestType) {
+ // Pinning is not supported for user-profiles with items restricted on home screen.
+ if (!mService.areShortcutsSupportedOnHomeScreen(callingUserId)) {
+ return null;
+ }
// Find the default launcher.
final int launcherUserId = mService.getParentOrSelfUserId(callingUserId);
final String defaultLauncher = mService.getDefaultLauncher(launcherUserId);
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index c23d2ab..a600eea 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -70,6 +70,7 @@
import android.graphics.RectF;
import android.graphics.drawable.AdaptiveIconDrawable;
import android.graphics.drawable.Icon;
+import android.multiuser.Flags;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
@@ -2830,6 +2831,26 @@
}
}
+ @VisibleForTesting
+ boolean areShortcutsSupportedOnHomeScreen(@UserIdInt int userId) {
+ if (!android.os.Flags.allowPrivateProfile() || !Flags.disablePrivateSpaceItemsOnHome()) {
+ return true;
+ }
+ final long start = getStatStartTime();
+ final long token = injectClearCallingIdentity();
+ boolean isSupported;
+ try {
+ synchronized (mLock) {
+ isSupported = !mUserManagerInternal.getUserProperties(userId)
+ .areItemsRestrictedOnHomeScreen();
+ }
+ } finally {
+ injectRestoreCallingIdentity(token);
+ logDurationStat(Stats.GET_DEFAULT_LAUNCHER, start);
+ }
+ return isSupported;
+ }
+
@Nullable
String getDefaultLauncher(@UserIdInt int userId) {
final long start = getStatStartTime();
@@ -3660,6 +3681,10 @@
callingPid, callingUid);
}
+ public boolean areShortcutsSupportedOnHomeScreen(@UserIdInt int userId) {
+ return ShortcutService.this.areShortcutsSupportedOnHomeScreen(userId);
+ }
+
@Override
public void setShortcutHostPackage(@NonNull String type, @Nullable String packageName,
int userId) {
diff --git a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
index 76952b3..1b220a0 100644
--- a/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
+++ b/services/core/java/com/android/server/policy/DeviceStateProviderImpl.java
@@ -17,7 +17,7 @@
package com.android.server.policy;
import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
-import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE;
+import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE_IDENTIFIER;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -94,7 +94,7 @@
private static final BooleanSupplier FALSE_BOOLEAN_SUPPLIER = () -> false;
@VisibleForTesting
- static final DeviceState DEFAULT_DEVICE_STATE = new DeviceState(MINIMUM_DEVICE_STATE,
+ static final DeviceState DEFAULT_DEVICE_STATE = new DeviceState(MINIMUM_DEVICE_STATE_IDENTIFIER,
"DEFAULT", 0 /* flags */);
private static final String VENDOR_CONFIG_FILE_PATH = "etc/devicestate/";
diff --git a/services/core/java/com/android/server/search/Searchables.java b/services/core/java/com/android/server/search/Searchables.java
index 6e1e979..7b39775 100644
--- a/services/core/java/com/android/server/search/Searchables.java
+++ b/services/core/java/com/android/server/search/Searchables.java
@@ -147,6 +147,9 @@
Log.e(LOG_TAG, "Error getting activity info " + re);
return null;
}
+ if (ai == null) {
+ return null;
+ }
String refActivityName = null;
// First look for activity-specific reference
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index a4c6959..3c6baa8 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -255,4 +255,9 @@
* @param tile the ComponentName of the {@link android.service.quicksettings.TileService}
*/
void removeQsTile(ComponentName tile);
+
+ /**
+ * Called when requested to enter desktop from an app.
+ */
+ void enterDesktop(int displayId);
}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index fd316ea..14c38bd 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -830,6 +830,15 @@
}
@Override
+ public void enterDesktop(int displayId) {
+ IStatusBar bar = mBar;
+ if (bar != null) {
+ try {
+ bar.enterDesktop(displayId);
+ } catch (RemoteException ex) { }
+ }
+ }
+ @Override
public void showMediaOutputSwitcher(String packageName) {
IStatusBar bar = mBar;
if (bar != null) {
diff --git a/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java b/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java
index 5f4852f..a25d67a 100644
--- a/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java
+++ b/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java
@@ -252,7 +252,7 @@
}
getInboundTransformInternal()
- .getIpSecTransformState(
+ .requestIpSecTransformState(
new HandlerExecutor(mHandler), new IpSecTransformStateReceiver());
// Schedule for next poll
@@ -302,7 +302,8 @@
"packetLossRate: "
+ packetLossRate
+ "% in the past "
- + (state.getTimestamp() - mLastIpSecTransformState.getTimestamp())
+ + (state.getTimestampMillis()
+ - mLastIpSecTransformState.getTimestampMillis())
+ "ms";
mLastIpSecTransformState = state;
diff --git a/services/core/java/com/android/server/vcn/routeselection/NetworkMetricMonitor.java b/services/core/java/com/android/server/vcn/routeselection/NetworkMetricMonitor.java
index a79f188..1704aa1 100644
--- a/services/core/java/com/android/server/vcn/routeselection/NetworkMetricMonitor.java
+++ b/services/core/java/com/android/server/vcn/routeselection/NetworkMetricMonitor.java
@@ -138,10 +138,10 @@
}
/** Poll an IpSecTransformState */
- public void getIpSecTransformState(
+ public void requestIpSecTransformState(
@NonNull Executor executor,
@NonNull OutcomeReceiver<IpSecTransformState, RuntimeException> callback) {
- ipSecTransform.getIpSecTransformState(executor, callback);
+ ipSecTransform.requestIpSecTransformState(executor, callback);
}
/** Close this instance and release the underlying resources */
diff --git a/services/core/java/com/android/server/vibrator/VibrationSettings.java b/services/core/java/com/android/server/vibrator/VibrationSettings.java
index fab0430..99ce3e2 100644
--- a/services/core/java/com/android/server/vibrator/VibrationSettings.java
+++ b/services/core/java/com/android/server/vibrator/VibrationSettings.java
@@ -31,6 +31,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
+import android.app.SynchronousUserSwitchObserver;
import android.app.UidObserver;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -145,8 +146,6 @@
PowerManager.GO_TO_SLEEP_REASON_INATTENTIVE,
PowerManager.GO_TO_SLEEP_REASON_TIMEOUT));
- private static final IntentFilter USER_SWITCHED_INTENT_FILTER =
- new IntentFilter(Intent.ACTION_USER_SWITCHED);
private static final IntentFilter INTERNAL_RINGER_MODE_CHANGED_INTENT_FILTER =
new IntentFilter(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION);
@@ -162,9 +161,11 @@
@VisibleForTesting
final SettingsContentObserver mSettingObserver;
@VisibleForTesting
- final MyUidObserver mUidObserver;
- @VisibleForTesting
final SettingsBroadcastReceiver mSettingChangeReceiver;
+ @VisibleForTesting
+ final VibrationUidObserver mUidObserver;
+ @VisibleForTesting
+ final VibrationUserSwitchObserver mUserSwitchObserver;
@GuardedBy("mLock")
private final List<OnVibratorSettingsChanged> mListeners = new ArrayList<>();
@@ -205,8 +206,9 @@
mContext = context;
mVibrationConfig = config;
mSettingObserver = new SettingsContentObserver(handler);
- mUidObserver = new MyUidObserver();
mSettingChangeReceiver = new SettingsBroadcastReceiver();
+ mUidObserver = new VibrationUidObserver();
+ mUserSwitchObserver = new VibrationUserSwitchObserver();
mSystemUiPackage = LocalServices.getService(PackageManagerInternal.class)
.getSystemUiServiceComponent().getPackageName();
@@ -245,7 +247,13 @@
try {
ActivityManager.getService().registerUidObserver(mUidObserver,
ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE,
- ActivityManager.PROCESS_STATE_UNKNOWN, null);
+ ActivityManager.PROCESS_STATE_UNKNOWN, /* callingPackage= */ null);
+ } catch (RemoteException e) {
+ // ignored; both services live in system_server
+ }
+
+ try {
+ ActivityManager.getService().registerUserSwitchObserver(mUserSwitchObserver, TAG);
} catch (RemoteException e) {
// ignored; both services live in system_server
}
@@ -270,7 +278,6 @@
}
});
- registerSettingsChangeReceiver(USER_SWITCHED_INTENT_FILTER);
registerSettingsChangeReceiver(INTERNAL_RINGER_MODE_CHANGED_INTENT_FILTER);
// Listen to all settings that might affect the result of Vibrator.getVibrationIntensity.
@@ -540,41 +547,44 @@
/** Update all cached settings and triggers registered listeners. */
void update() {
- updateSettings();
+ updateSettings(UserHandle.USER_CURRENT);
updateRingerMode();
notifyListeners();
}
- private void updateSettings() {
+ private void updateSettings(int userHandle) {
synchronized (mLock) {
- mVibrateInputDevices = loadSystemSetting(Settings.System.VIBRATE_INPUT_DEVICES, 0) > 0;
- mVibrateOn = loadSystemSetting(Settings.System.VIBRATE_ON, 1) > 0;
+ mVibrateInputDevices =
+ loadSystemSetting(Settings.System.VIBRATE_INPUT_DEVICES, 0, userHandle) > 0;
+ mVibrateOn = loadSystemSetting(Settings.System.VIBRATE_ON, 1, userHandle) > 0;
mKeyboardVibrationOn = loadSystemSetting(Settings.System.KEYBOARD_VIBRATION_ENABLED,
- mVibrationConfig.isDefaultKeyboardVibrationEnabled() ? 1 : 0) > 0;
+ mVibrationConfig.isDefaultKeyboardVibrationEnabled() ? 1 : 0, userHandle) > 0;
int alarmIntensity = toIntensity(
- loadSystemSetting(Settings.System.ALARM_VIBRATION_INTENSITY, -1),
+ loadSystemSetting(Settings.System.ALARM_VIBRATION_INTENSITY, -1, userHandle),
getDefaultIntensity(USAGE_ALARM));
int defaultHapticFeedbackIntensity = getDefaultIntensity(USAGE_TOUCH);
int hapticFeedbackIntensity = toIntensity(
- loadSystemSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, -1),
+ loadSystemSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, -1, userHandle),
defaultHapticFeedbackIntensity);
int positiveHapticFeedbackIntensity = toPositiveIntensity(
hapticFeedbackIntensity, defaultHapticFeedbackIntensity);
int hardwareFeedbackIntensity = toIntensity(
- loadSystemSetting(Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY, -1),
+ loadSystemSetting(Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY, -1,
+ userHandle),
positiveHapticFeedbackIntensity);
int mediaIntensity = toIntensity(
- loadSystemSetting(Settings.System.MEDIA_VIBRATION_INTENSITY, -1),
+ loadSystemSetting(Settings.System.MEDIA_VIBRATION_INTENSITY, -1, userHandle),
getDefaultIntensity(USAGE_MEDIA));
int defaultNotificationIntensity = getDefaultIntensity(USAGE_NOTIFICATION);
int notificationIntensity = toIntensity(
- loadSystemSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, -1),
+ loadSystemSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY, -1,
+ userHandle),
defaultNotificationIntensity);
int positiveNotificationIntensity = toPositiveIntensity(
notificationIntensity, defaultNotificationIntensity);
int ringIntensity = toIntensity(
- loadSystemSetting(Settings.System.RING_VIBRATION_INTENSITY, -1),
+ loadSystemSetting(Settings.System.RING_VIBRATION_INTENSITY, -1, userHandle),
getDefaultIntensity(USAGE_RINGTONE));
mCurrentVibrationIntensities.clear();
@@ -593,7 +603,7 @@
mCurrentVibrationIntensities.put(USAGE_HARDWARE_FEEDBACK, hardwareFeedbackIntensity);
mCurrentVibrationIntensities.put(USAGE_PHYSICAL_EMULATION, hardwareFeedbackIntensity);
- if (!loadBooleanSetting(Settings.System.HAPTIC_FEEDBACK_ENABLED)) {
+ if (!loadBooleanSetting(Settings.System.HAPTIC_FEEDBACK_ENABLED, userHandle)) {
// Make sure deprecated boolean setting still disables touch vibrations.
mCurrentVibrationIntensities.put(USAGE_TOUCH, Vibrator.VIBRATION_INTENSITY_OFF);
} else {
@@ -744,14 +754,13 @@
return value;
}
- private boolean loadBooleanSetting(String settingKey) {
- return Settings.System.getIntForUser(mContext.getContentResolver(),
- settingKey, 0, UserHandle.USER_CURRENT) != 0;
+ private boolean loadBooleanSetting(String settingKey, int userHandle) {
+ return loadSystemSetting(settingKey, 0, userHandle) != 0;
}
- private int loadSystemSetting(String settingName, int defaultValue) {
+ private int loadSystemSetting(String settingName, int defaultValue, int userHandle) {
return Settings.System.getIntForUser(mContext.getContentResolver(),
- settingName, defaultValue, UserHandle.USER_CURRENT);
+ settingName, defaultValue, userHandle);
}
private void registerSettingsObserver(Uri settingUri) {
@@ -828,24 +837,18 @@
@Override
public void onChange(boolean selfChange) {
- updateSettings();
+ updateSettings(UserHandle.USER_CURRENT);
notifyListeners();
}
}
- /**
- * Implementation of {@link BroadcastReceiver} to update settings on current user or ringer
- * mode change.
- */
+ /** Implementation of {@link BroadcastReceiver} to update on ringer mode change. */
@VisibleForTesting
final class SettingsBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
- if (Intent.ACTION_USER_SWITCHED.equals(action)) {
- // Reload all settings, as they are user-based.
- update();
- } else if (AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION.equals(action)) {
+ if (AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION.equals(action)) {
updateRingerMode();
notifyListeners();
}
@@ -854,7 +857,7 @@
/** Implementation of {@link ContentObserver} to be registered to a setting {@link Uri}. */
@VisibleForTesting
- final class MyUidObserver extends UidObserver {
+ final class VibrationUidObserver extends UidObserver {
private final SparseArray<Integer> mProcStatesCache = new SparseArray<>();
public boolean isUidForeground(int uid) {
@@ -878,4 +881,23 @@
}
}
}
+
+ /** Implementation of {@link SynchronousUserSwitchObserver} to update on user switch. */
+ @VisibleForTesting
+ final class VibrationUserSwitchObserver extends SynchronousUserSwitchObserver {
+
+ @Override
+ public void onUserSwitching(int newUserId) {
+ // Reload settings early based on new user id.
+ updateSettings(newUserId);
+ notifyListeners();
+ }
+
+ @Override
+ public void onUserSwitchComplete(int newUserId) {
+ // Reload all settings including ones from AudioManager,
+ // as they are based on UserHandle.USER_CURRENT.
+ update();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/vibrator/VibratorManagerService.java b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
index 759450b..5d172a9 100644
--- a/services/core/java/com/android/server/vibrator/VibratorManagerService.java
+++ b/services/core/java/com/android/server/vibrator/VibratorManagerService.java
@@ -2220,9 +2220,12 @@
// only cancel background vibrations.
IBinder deathBinder = commonOptions.background ? VibratorManagerService.this
: mShellCallbacksToken;
- HalVibration vib = vibrateWithPermissionCheck(Binder.getCallingUid(),
- Context.DEVICE_ID_DEFAULT, SHELL_PACKAGE_NAME, combined, attrs,
- commonOptions.description, deathBinder);
+ int uid = Binder.getCallingUid();
+ // Resolve the package name for the client based on the process UID, to cover cases like
+ // rooted shell clients using ROOT_UID.
+ String resolvedPackageName = AppOpsManager.resolvePackageName(uid, SHELL_PACKAGE_NAME);
+ HalVibration vib = vibrateWithPermissionCheck(uid, Context.DEVICE_ID_DEFAULT,
+ resolvedPackageName, combined, attrs, commonOptions.description, deathBinder);
maybeWaitOnVibration(vib, commonOptions);
}
diff --git a/services/core/java/com/android/server/wearable/WearableSensingManagerService.java b/services/core/java/com/android/server/wearable/WearableSensingManagerService.java
index 00c3026..d05482d 100644
--- a/services/core/java/com/android/server/wearable/WearableSensingManagerService.java
+++ b/services/core/java/com/android/server/wearable/WearableSensingManagerService.java
@@ -55,6 +55,7 @@
import com.android.server.utils.quota.MultiRateLimiter;
import java.io.FileDescriptor;
+import java.time.Duration;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
@@ -106,7 +107,7 @@
private final Context mContext;
private final AtomicInteger mNextDataRequestObserverId = new AtomicInteger(1);
private final Set<DataRequestObserverContext> mDataRequestObserverContexts = new HashSet<>();
- private final MultiRateLimiter mDataRequestRateLimiter;
+ @NonNull private volatile MultiRateLimiter mDataRequestRateLimiter;
volatile boolean mIsServiceEnabled;
public WearableSensingManagerService(Context context) {
@@ -238,6 +239,57 @@
}
}
+ /**
+ * Sets the window size used in data request rate limiting.
+ *
+ * <p>The new value will not be reflected in {@link
+ * WearableSensingDataRequest#getRateLimitWindowSize()}.
+ *
+ * <p>{@code windowSize} will be automatically capped between
+ * com.android.server.utils.quota.QuotaTracker#MIN_WINDOW_SIZE_MS and
+ * com.android.server.utils.quota.QuotaTracker#MAX_WINDOW_SIZE_MS
+ *
+ * <p>The current rate limit will also be reset.
+ *
+ * <p>This method is only used for testing and must not be called in production code because
+ * it effectively bypasses the rate limiting introduced to enhance privacy protection.
+ */
+ @VisibleForTesting
+ void setDataRequestRateLimitWindowSize(@NonNull Duration windowSize) {
+ Slog.w(
+ TAG,
+ TextUtils.formatSimple(
+ "Setting the data request rate limit window size to %s. This also resets"
+ + " the current limit and should only be callable from a test.",
+ windowSize));
+ mDataRequestRateLimiter =
+ new MultiRateLimiter.Builder(mContext)
+ .addRateLimit(WearableSensingDataRequest.getRateLimit(), windowSize)
+ .build();
+ }
+
+ /**
+ * Resets the window size used in data request rate limiting back to the default value.
+ *
+ * <p>The current rate limit will also be reset.
+ *
+ * <p>This method is only used for testing and must not be called in production code because
+ * it effectively bypasses the rate limiting introduced to enhance privacy protection.
+ */
+ @VisibleForTesting
+ void resetDataRequestRateLimitWindowSize() {
+ Slog.w(
+ TAG,
+ "Resetting the data request rate limit window size back to the default value. This"
+ + " also resets the current limit and should only be callable from a test.");
+ mDataRequestRateLimiter =
+ new MultiRateLimiter.Builder(mContext)
+ .addRateLimit(
+ WearableSensingDataRequest.getRateLimit(),
+ WearableSensingDataRequest.getRateLimitWindowSize())
+ .build();
+ }
+
private DataRequestObserverContext getDataRequestObserverContext(
int dataType, int userId, PendingIntent dataRequestPendingIntent) {
synchronized (mDataRequestObserverContexts) {
diff --git a/services/core/java/com/android/server/wearable/WearableSensingShellCommand.java b/services/core/java/com/android/server/wearable/WearableSensingShellCommand.java
index 842bccb..0a9cf34 100644
--- a/services/core/java/com/android/server/wearable/WearableSensingShellCommand.java
+++ b/services/core/java/com/android/server/wearable/WearableSensingShellCommand.java
@@ -29,6 +29,7 @@
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
+import java.time.Duration;
final class WearableSensingShellCommand extends ShellCommand {
private static final String TAG = WearableSensingShellCommand.class.getSimpleName();
@@ -90,6 +91,8 @@
return getBoundPackageName();
case "set-temporary-service":
return setTemporaryService();
+ case "set-data-request-rate-limit-window-size":
+ return setDataRequestRateLimitWindowSize();
default:
return handleDefaultCommands(cmd);
}
@@ -114,6 +117,11 @@
pw.println(" set-temporary-service USER_ID [PACKAGE_NAME] [COMPONENT_NAME DURATION]");
pw.println(" Temporarily (for DURATION ms) changes the service implementation.");
pw.println(" To reset, call with just the USER_ID argument.");
+ pw.println(" set-data-request-rate-limit-window-size WINDOW_SIZE");
+ pw.println(" Set the window size used in data request rate limiting to WINDOW_SIZE"
+ + " seconds.");
+ pw.println(" positive WINDOW_SIZE smaller than 20 will be automatically set to 20.");
+ pw.println(" To reset, call with 0 or a negative WINDOW_SIZE.");
}
private int createDataStream() {
@@ -209,4 +217,20 @@
resultPrinter.println(componentName == null ? "" : componentName.getPackageName());
return 0;
}
+
+ private int setDataRequestRateLimitWindowSize() {
+ Slog.d(TAG, "setDataRequestRateLimitWindowSize");
+ int windowSizeSeconds = Integer.parseInt(getNextArgRequired());
+ if (windowSizeSeconds <= 0) {
+ mService.resetDataRequestRateLimitWindowSize();
+ } else {
+ // 20 is the minimum window size supported by the rate limiter.
+ // It is defined by com.android.server.utils.quota.QuotaTracker#MIN_WINDOW_SIZE_MS
+ if (windowSizeSeconds < 20) {
+ windowSizeSeconds = 20;
+ }
+ mService.setDataRequestRateLimitWindowSize(Duration.ofSeconds(windowSizeSeconds));
+ }
+ return 0;
+ }
}
diff --git a/services/core/java/com/android/server/webkit/SystemImpl.java b/services/core/java/com/android/server/webkit/SystemImpl.java
index ea8a801..3b2e69a 100644
--- a/services/core/java/com/android/server/webkit/SystemImpl.java
+++ b/services/core/java/com/android/server/webkit/SystemImpl.java
@@ -21,6 +21,7 @@
import android.app.ActivityManager;
import android.app.AppGlobals;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
@@ -39,11 +40,14 @@
import android.webkit.WebViewZygote;
import com.android.internal.util.XmlUtils;
+import com.android.server.LocalServices;
+import com.android.server.PinnerService;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
/**
@@ -60,6 +64,7 @@
private static final String TAG_AVAILABILITY = "availableByDefault";
private static final String TAG_SIGNATURE = "signature";
private static final String TAG_FALLBACK = "isFallback";
+ private static final String PIN_GROUP = "webview";
private final WebViewProviderInfo[] mWebViewProviderPackages;
// Initialization-on-demand holder idiom for getting the WebView provider packages once and
@@ -277,6 +282,36 @@
return true;
}
+ @Override
+ public void pinWebviewIfRequired(ApplicationInfo appInfo) {
+ PinnerService pinnerService = LocalServices.getService(PinnerService.class);
+ int webviewPinQuota = pinnerService.getWebviewPinQuota();
+ if (webviewPinQuota <= 0) {
+ return;
+ }
+
+ pinnerService.unpinGroup(PIN_GROUP);
+
+ ArrayList<String> apksToPin = new ArrayList<>();
+ boolean pinSharedFirst = appInfo.metaData.getBoolean("PIN_SHARED_LIBS_FIRST", true);
+ for (String sharedLib : appInfo.sharedLibraryFiles) {
+ apksToPin.add(sharedLib);
+ }
+ apksToPin.add(appInfo.sourceDir);
+ if (!pinSharedFirst) {
+ // We want to prioritize pinning of the native library that is most likely used by apps
+ // which in some build flavors live in the main apk and as a shared library for others.
+ Collections.reverse(apksToPin);
+ }
+ for (String apk : apksToPin) {
+ if (webviewPinQuota <= 0) {
+ break;
+ }
+ int bytesPinned = pinnerService.pinFile(apk, webviewPinQuota, appInfo, PIN_GROUP);
+ webviewPinQuota -= bytesPinned;
+ }
+ }
+
// flags declaring we want extra info from the package manager for webview providers
private final static int PACKAGE_FLAGS = PackageManager.GET_META_DATA
| PackageManager.GET_SIGNATURES | PackageManager.GET_SHARED_LIBRARY_FILES
diff --git a/services/core/java/com/android/server/webkit/SystemInterface.java b/services/core/java/com/android/server/webkit/SystemInterface.java
index 09c23a7..5ed2cfe 100644
--- a/services/core/java/com/android/server/webkit/SystemInterface.java
+++ b/services/core/java/com/android/server/webkit/SystemInterface.java
@@ -17,6 +17,7 @@
package com.android.server.webkit;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.webkit.UserPackage;
@@ -61,4 +62,6 @@
/** Start the zygote if it's not already running. */
public void ensureZygoteStarted();
public boolean isMultiProcessDefaultEnabled();
+
+ public void pinWebviewIfRequired(ApplicationInfo appInfo);
}
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
index d95b431..1d6ad6d 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl.java
@@ -17,7 +17,6 @@
import android.annotation.Nullable;
import android.content.Context;
-import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;
@@ -30,12 +29,8 @@
import android.webkit.WebViewProviderInfo;
import android.webkit.WebViewProviderResponse;
-import com.android.server.LocalServices;
-import com.android.server.PinnerService;
-
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
/**
@@ -93,8 +88,6 @@
private static final int MULTIPROCESS_SETTING_ON_VALUE = Integer.MAX_VALUE;
private static final int MULTIPROCESS_SETTING_OFF_VALUE = Integer.MIN_VALUE;
- private static final String PIN_GROUP = "webview";
-
private final SystemInterface mSystemInterface;
private final Context mContext;
@@ -346,38 +339,6 @@
return newPackage;
}
- private void pinWebviewIfRequired(ApplicationInfo appInfo) {
- PinnerService pinnerService = LocalServices.getService(PinnerService.class);
- if (pinnerService == null) {
- // This happens in unit tests which do not have services.
- return;
- }
- int webviewPinQuota = pinnerService.getWebviewPinQuota();
- if (webviewPinQuota <= 0) {
- return;
- }
-
- pinnerService.unpinGroup(PIN_GROUP);
-
- ArrayList<String> apksToPin = new ArrayList<>();
- boolean pinSharedFirst = appInfo.metaData.getBoolean("PIN_SHARED_LIBS_FIRST", true);
- for (String sharedLib : appInfo.sharedLibraryFiles) {
- apksToPin.add(sharedLib);
- }
- apksToPin.add(appInfo.sourceDir);
- if (!pinSharedFirst) {
- // We want to prioritize pinning of the native library that is most likely used by apps
- // which in some build flavors live in the main apk and as a shared library for others.
- Collections.reverse(apksToPin);
- }
- for (String apk : apksToPin) {
- if (webviewPinQuota <= 0) {
- break;
- }
- int bytesPinned = pinnerService.pinFile(apk, webviewPinQuota, appInfo, PIN_GROUP);
- webviewPinQuota -= bytesPinned;
- }
- }
/**
* This is called when we change WebView provider, either when the current provider is
* updated or a new provider is chosen / takes precedence.
@@ -386,7 +347,7 @@
synchronized (mLock) {
mAnyWebViewInstalled = true;
if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) {
- pinWebviewIfRequired(newPackage.applicationInfo);
+ mSystemInterface.pinWebviewIfRequired(newPackage.applicationInfo);
mCurrentWebViewPackage = newPackage;
// The relro creations might 'finish' (not start at all) before
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java
index 1bc635b..b3c8b0b 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdateServiceImpl2.java
@@ -17,7 +17,6 @@
import android.annotation.Nullable;
import android.content.Context;
-import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;
@@ -32,12 +31,8 @@
import android.webkit.WebViewProviderInfo;
import android.webkit.WebViewProviderResponse;
-import com.android.server.LocalServices;
-import com.android.server.PinnerService;
-
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
/**
@@ -88,10 +83,9 @@
private static final int VALIDITY_INCORRECT_SIGNATURE = 3;
private static final int VALIDITY_NO_LIBRARY_FLAG = 4;
- private static final String PIN_GROUP = "webview";
-
private final SystemInterface mSystemInterface;
private final Context mContext;
+ private final WebViewProviderInfo mDefaultProvider;
private long mMinimumVersionCode = -1;
@@ -112,6 +106,16 @@
WebViewUpdateServiceImpl2(Context context, SystemInterface systemInterface) {
mContext = context;
mSystemInterface = systemInterface;
+ WebViewProviderInfo[] webviewProviders = getWebViewPackages();
+ for (WebViewProviderInfo provider : webviewProviders) {
+ if (provider.availableByDefault) {
+ mDefaultProvider = provider;
+ break;
+ }
+ }
+ // This should be unreachable because the config parser enforces that there is at least one
+ // availableByDefault provider.
+ throw new AndroidRuntimeException("No available by default WebView Provider.");
}
@Override
@@ -170,11 +174,10 @@
if (mCurrentWebViewPackage == null) {
return true;
}
- WebViewProviderInfo defaultProvider = getDefaultWebViewPackage();
- if (mCurrentWebViewPackage.packageName.equals(defaultProvider.packageName)) {
+ if (mCurrentWebViewPackage.packageName.equals(mDefaultProvider.packageName)) {
List<UserPackage> userPackages =
mSystemInterface.getPackageInfoForProviderAllUsers(
- mContext, defaultProvider);
+ mContext, mDefaultProvider);
return !isInstalledAndEnabledForAllUsers(userPackages);
} else {
return false;
@@ -207,13 +210,12 @@
// default package for all users in case it was disabled, even if we already did the
// one-time migration before. If this actually changes the state, we will see the
// PackageManager broadcast shortly and try again.
- WebViewProviderInfo defaultProvider = getDefaultWebViewPackage();
Slog.w(
TAG,
"No provider available for all users, trying to enable "
- + defaultProvider.packageName);
+ + mDefaultProvider.packageName);
mSystemInterface.enablePackageForAllUsers(
- mContext, defaultProvider.packageName, true);
+ mContext, mDefaultProvider.packageName, true);
}
} catch (Throwable t) {
@@ -356,39 +358,6 @@
return newPackage;
}
- private void pinWebviewIfRequired(ApplicationInfo appInfo) {
- PinnerService pinnerService = LocalServices.getService(PinnerService.class);
- if (pinnerService == null) {
- // This happens in unit tests which do not have services.
- return;
- }
- int webviewPinQuota = pinnerService.getWebviewPinQuota();
- if (webviewPinQuota <= 0) {
- return;
- }
-
- pinnerService.unpinGroup(PIN_GROUP);
-
- ArrayList<String> apksToPin = new ArrayList<>();
- boolean pinSharedFirst = appInfo.metaData.getBoolean("PIN_SHARED_LIBS_FIRST", true);
- for (String sharedLib : appInfo.sharedLibraryFiles) {
- apksToPin.add(sharedLib);
- }
- apksToPin.add(appInfo.sourceDir);
- if (!pinSharedFirst) {
- // We want to prioritize pinning of the native library that is most likely used by apps
- // which in some build flavors live in the main apk and as a shared library for others.
- Collections.reverse(apksToPin);
- }
- for (String apk : apksToPin) {
- if (webviewPinQuota <= 0) {
- break;
- }
- int bytesPinned = pinnerService.pinFile(apk, webviewPinQuota, appInfo, PIN_GROUP);
- webviewPinQuota -= bytesPinned;
- }
- }
-
/**
* This is called when we change WebView provider, either when the current provider is
* updated or a new provider is chosen / takes precedence.
@@ -397,7 +366,7 @@
synchronized (mLock) {
mAnyWebViewInstalled = true;
if (mNumRelroCreationsStarted == mNumRelroCreationsFinished) {
- pinWebviewIfRequired(newPackage.applicationInfo);
+ mSystemInterface.pinWebviewIfRequired(newPackage.applicationInfo);
mCurrentWebViewPackage = newPackage;
// The relro creations might 'finish' (not start at all) before
@@ -438,15 +407,7 @@
*/
@Override
public WebViewProviderInfo getDefaultWebViewPackage() {
- WebViewProviderInfo[] webviewProviders = getWebViewPackages();
- for (WebViewProviderInfo provider : webviewProviders) {
- if (provider.availableByDefault) {
- return provider;
- }
- }
- // This should be unreachable because the config parser enforces that there is at least one
- // availableByDefault provider.
- throw new AndroidRuntimeException("No available by default WebView Provider.");
+ return mDefaultProvider;
}
private static class ProviderAndPackageInfo {
@@ -507,14 +468,13 @@
// User did not choose, or the choice failed; return the default provider even if it is not
// installed or enabled for all users.
- WebViewProviderInfo defaultProvider = getDefaultWebViewPackage();
try {
- PackageInfo packageInfo = mSystemInterface.getPackageInfoForProvider(defaultProvider);
- if (validityResult(defaultProvider, packageInfo) == VALIDITY_OK) {
+ PackageInfo packageInfo = mSystemInterface.getPackageInfoForProvider(mDefaultProvider);
+ if (validityResult(mDefaultProvider, packageInfo) == VALIDITY_OK) {
return packageInfo;
}
} catch (NameNotFoundException e) {
- Slog.w(TAG, "Default WebView package (" + defaultProvider.packageName + ") not found");
+ Slog.w(TAG, "Default WebView package (" + mDefaultProvider.packageName + ") not found");
}
// This should never happen during normal operation (only with modified system images).
diff --git a/services/core/java/com/android/server/wm/ActivityCallerState.java b/services/core/java/com/android/server/wm/ActivityCallerState.java
index e797290..fa0b176 100644
--- a/services/core/java/com/android/server/wm/ActivityCallerState.java
+++ b/services/core/java/com/android/server/wm/ActivityCallerState.java
@@ -30,6 +30,7 @@
import android.content.ContentResolver;
import android.content.Intent;
import android.net.Uri;
+import android.os.BadParcelableException;
import android.os.IBinder;
import android.os.UserHandle;
import android.util.ArraySet;
@@ -43,6 +44,7 @@
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.WeakHashMap;
/**
@@ -143,8 +145,8 @@
final boolean writeMet = callerInfo.mWritableContentUris.contains(grantUri);
if (!readMet && !writeMet) {
- throw new IllegalArgumentException("The supplied URI wasn't passed at launch: "
- + grantUri.uri.toSafeString());
+ throw new IllegalArgumentException("The supplied URI wasn't passed at launch in"
+ + " #getData, #EXTRA_STREAM, nor #getClipData: " + grantUri.uri.toSafeString());
}
final boolean checkRead = (modeFlags & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0;
@@ -184,6 +186,18 @@
// getData
addUriIfContentUri(intent.getData(), uris);
+ // EXTRA_STREAM
+ if (intent.hasExtra(Intent.EXTRA_STREAM)) {
+ final ArrayList<Uri> streams = tryToUnparcelArrayListExtraStreamsUri(intent);
+ if (streams == null) {
+ addUriIfContentUri(tryToUnparcelExtraStreamUri(intent), uris);
+ } else {
+ for (int i = streams.size() - 1; i >= 0; i--) {
+ addUriIfContentUri(streams.get(i), uris);
+ }
+ }
+ }
+
final ClipData clipData = intent.getClipData();
if (clipData == null) return uris;
@@ -199,6 +213,33 @@
return uris;
}
+ private static Uri tryToUnparcelExtraStreamUri(Intent intent) {
+ try {
+ return intent.getParcelableExtra(Intent.EXTRA_STREAM, Uri.class);
+ } catch (BadParcelableException e) {
+ // Even though the system "defuses" all the parsed Bundles, i.e. suppresses and logs
+ // instances of {@link BadParcelableException}, we still want to be on the safer side
+ // and catch the exception to ensure no breakages happen. If the unparcel fails, the
+ // item is still preserved with the underlying parcel.
+ Slog.w(TAG, "Failed to unparcel an URI in EXTRA_STREAM, returning null: " + e);
+ return null;
+ }
+ }
+
+ private static ArrayList<Uri> tryToUnparcelArrayListExtraStreamsUri(Intent intent) {
+ try {
+ return intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM, Uri.class);
+ } catch (BadParcelableException e) {
+ // Even though the system "defuses" all the parsed Bundles, i.e. suppresses and logs
+ // instances of {@link BadParcelableException}, we still want to be on the safer side
+ // and catch the exception to ensure no breakages happen. If the unparcel fails, the
+ // item is still preserved with the underlying parcel.
+ Slog.w(TAG, "Failed to unparcel an ArrayList of URIs in EXTRA_STREAM, returning null: "
+ + e);
+ return null;
+ }
+ }
+
private static void addUriIfContentUri(Uri uri, ArraySet<Uri> uris) {
if (uri != null && ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
uris.add(uri);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index c36df8d..2696fb6 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1846,20 +1846,20 @@
mLetterboxUiController.onMovedToDisplay(mDisplayContent.getDisplayId());
}
- void layoutLetterbox(WindowState winHint) {
- mLetterboxUiController.layoutLetterbox(winHint);
+ void layoutLetterboxIfNeeded(WindowState winHint) {
+ mLetterboxUiController.layoutLetterboxIfNeeded(winHint);
}
boolean hasWallpaperBackgroundForLetterbox() {
return mLetterboxUiController.hasWallpaperBackgroundForLetterbox();
}
- void updateLetterboxSurface(WindowState winHint, Transaction t) {
- mLetterboxUiController.updateLetterboxSurface(winHint, t);
+ void updateLetterboxSurfaceIfNeeded(WindowState winHint, Transaction t) {
+ mLetterboxUiController.updateLetterboxSurfaceIfNeeded(winHint, t);
}
- void updateLetterboxSurface(WindowState winHint) {
- mLetterboxUiController.updateLetterboxSurface(winHint);
+ void updateLetterboxSurfaceIfNeeded(WindowState winHint) {
+ mLetterboxUiController.updateLetterboxSurfaceIfNeeded(winHint);
}
/** Gets the letterbox insets. The insets will be empty if there is no letterbox. */
@@ -4546,7 +4546,7 @@
}
super.removeChild(child);
checkKeyguardFlagsChanged();
- updateLetterboxSurface(child);
+ updateLetterboxSurfaceIfNeeded(child);
}
void setAppLayoutChanges(int changes, String reason) {
@@ -6036,7 +6036,7 @@
if (destroyedSomething) {
final DisplayContent dc = getDisplayContent();
dc.assignWindowLayers(true /*setLayoutNeeded*/);
- updateLetterboxSurface(null);
+ updateLetterboxSurfaceIfNeeded(null);
}
}
@@ -7688,7 +7688,7 @@
}
if (mNeedsLetterboxedAnimation) {
- updateLetterboxSurface(findMainWindow(), t);
+ updateLetterboxSurfaceIfNeeded(findMainWindow(), t);
mNeedsAnimationBoundsLayer = true;
}
@@ -7856,7 +7856,7 @@
mNeedsAnimationBoundsLayer = false;
if (mNeedsLetterboxedAnimation) {
mNeedsLetterboxedAnimation = false;
- updateLetterboxSurface(findMainWindow(), t);
+ updateLetterboxSurfaceIfNeeded(findMainWindow(), t);
}
if (mAnimatingActivityRegistry != null) {
@@ -8943,6 +8943,15 @@
}
}
+ // Fixed orientation bounds are the same as its parent container, so clear the fixed
+ // orientation bounds. This can happen in close to square displays where the orientation
+ // is not respected with insets, but the display still matches or is less than the
+ // activity aspect ratio.
+ if (resolvedBounds.equals(parentBounds)) {
+ resolvedBounds.set(prevResolvedBounds);
+ return;
+ }
+
// Calculate app bounds using fixed orientation bounds because they will be needed later
// for comparison with size compat app bounds in {@link resolveSizeCompatModeConfiguration}.
getTaskFragment().computeConfigResourceOverrides(getResolvedOverrideConfiguration(),
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index 2c49203..533529a 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -639,6 +639,10 @@
return mPendingRemoteAnimationRegistry;
}
+ ActivityRecord getLastStartActivity() {
+ return mLastStarter != null ? mLastStarter.mStartActivity : null;
+ }
+
void dumpLastHomeActivityStartResult(PrintWriter pw, String prefix) {
pw.print(prefix);
pw.print("mLastHomeActivityStartResult=");
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 52fdfda..adbe800 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -3524,10 +3524,13 @@
}
final long callingIdentity = Binder.clearCallingIdentity();
try {
- hasRestrictedWindow = displayContent.forAllWindows(windowState -> {
- return windowState.isOnScreen() && UserManager.isUserTypePrivateProfile(
- getUserManager().getProfileType(windowState.mShowUserId));
- }, true /* traverseTopToBottom */);
+ hasRestrictedWindow = displayContent.forAllWindows(
+ windowState -> windowState.isOnScreen() && (
+ UserManager.isUserTypePrivateProfile(
+ getUserManager().getProfileType(windowState.mShowUserId))
+ || hasUserRestriction(
+ UserManager.DISALLOW_ASSIST_CONTENT,
+ windowState.mShowUserId)), true /* traverseTopToBottom */);
} finally {
Binder.restoreCallingIdentity(callingIdentity);
}
@@ -5551,7 +5554,7 @@
* Saves the current activity manager state and includes the saved state in the next dump of
* activity manager.
*/
- void saveANRState(String reason) {
+ void saveANRState(ActivityRecord activity, String reason) {
final StringWriter sw = new StringWriter();
final PrintWriter pw = new FastPrintWriter(sw, false, 1024);
pw.println(" ANR time: " + DateFormat.getDateTimeInstance().format(new Date()));
@@ -5559,14 +5562,25 @@
pw.println(" Reason: " + reason);
}
pw.println();
- getActivityStartController().dump(pw, " ", null);
- pw.println();
+ if (activity != null) {
+ final Task rootTask = activity.getRootTask();
+ if (rootTask != null) {
+ rootTask.forAllTaskFragments(
+ tf -> tf.dumpInner(" ", pw, true /* dumpAll */, null /* dumpPackage */));
+ pw.println();
+ }
+ mActivityStartController.dump(pw, " ", activity.packageName);
+ if (mActivityStartController.getLastStartActivity() != activity) {
+ activity.dump(pw, " ", true /* dumpAll */);
+ }
+ }
+ ActivityTaskSupervisor.printThisActivity(pw, mRootWindowContainer.getTopResumedActivity(),
+ null /* dumpPackage */, INVALID_DISPLAY, true /* needSep */,
+ " ResumedActivity: ", /* header= */ null /* header */);
+ mLockTaskController.dump(pw, " ");
+ mKeyguardController.dump(pw, " ");
pw.println("-------------------------------------------------------------------"
+ "------------");
- dumpActivitiesLocked(null /* fd */, pw, null /* args */, 0 /* opti */,
- true /* dumpAll */, false /* dumpClient */, null /* dumpPackage */,
- INVALID_DISPLAY, "" /* header */);
- pw.println();
pw.close();
mLastANRState = sw.toString();
diff --git a/services/core/java/com/android/server/wm/AnrController.java b/services/core/java/com/android/server/wm/AnrController.java
index b9f6e17..0013c5c 100644
--- a/services/core/java/com/android/server/wm/AnrController.java
+++ b/services/core/java/com/android/server/wm/AnrController.java
@@ -367,7 +367,7 @@
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "dumpAnrStateLocked()");
synchronized (mService.mGlobalLock) {
mService.saveANRStateLocked(activity, windowState, reason);
- mService.mAtmService.saveANRState(reason);
+ mService.mAtmService.saveANRState(activity, reason);
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index c2dfa21..38ee456 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -629,7 +629,7 @@
final ActivityRecord ar = openApps.valueAt(i);
if (mAnimationHandler.isTarget(ar, true /* open */)) {
openApps.removeAt(i);
- mAnimationHandler.markStartingSurfaceMatch();
+ mAnimationHandler.markStartingSurfaceMatch(null /* reparentTransaction */);
}
}
for (int i = closeApps.size() - 1; i >= 0; --i) {
@@ -773,10 +773,15 @@
for (int i = mTmpOpenApps.size() - 1; i >= 0; --i) {
final WindowContainer wc = mTmpOpenApps.get(i);
if (mAnimationHandler.isTarget(wc, true /* open */)) {
- mAnimationHandler.markStartingSurfaceMatch();
+ mAnimationHandler.markStartingSurfaceMatch(startTransaction);
break;
}
}
+ // release animation leash
+ if (mAnimationHandler.mOpenAnimAdaptor.mCloseTransaction != null) {
+ startTransaction.merge(mAnimationHandler.mOpenAnimAdaptor.mCloseTransaction);
+ mAnimationHandler.mOpenAnimAdaptor.mCloseTransaction = null;
+ }
// Because the target will reparent to transition root, so it cannot be controlled by
// animation leash. Hide the close target when transition starts.
startTransaction.hide(mAnimationHandler.mCloseAdaptor.mTarget.getSurfaceControl());
@@ -993,7 +998,7 @@
}
final RemoteAnimationTarget[] targets = new RemoteAnimationTarget[2];
targets[0] = mCloseAdaptor.mAnimationTarget;
- targets[1] = mOpenAnimAdaptor.getOrCreateAnimationTarget();
+ targets[1] = mOpenAnimAdaptor.mRemoteAnimationTarget;
return targets;
}
@@ -1067,11 +1072,12 @@
}
}
- void markStartingSurfaceMatch() {
- mStartingSurfaceTargetMatch = true;
- for (int i = mOpenAnimAdaptor.mAdaptors.length - 1; i >= 0; --i) {
- mOpenAnimAdaptor.mAdaptors[i].reparentWindowlessSurfaceToTarget();
+ void markStartingSurfaceMatch(SurfaceControl.Transaction reparentTransaction) {
+ if (mStartingSurfaceTargetMatch) {
+ return;
}
+ mStartingSurfaceTargetMatch = true;
+ mOpenAnimAdaptor.reparentWindowlessSurfaceToTarget(reparentTransaction);
}
void clearBackAnimateTarget() {
@@ -1140,14 +1146,23 @@
private static class BackWindowAnimationAdaptorWrapper {
final BackWindowAnimationAdaptor[] mAdaptors;
+ // The highest remote animation target, which can be a wrapper if multiple adaptors,
+ // or the single opening target.
+ final RemoteAnimationTarget mRemoteAnimationTarget;
SurfaceControl.Transaction mCloseTransaction;
+ // The starting surface task Id. Used to clear the starting surface if the animation has
+ // requested one during animating.
+ private int mRequestedStartingSurfaceId = INVALID_TASK_ID;
+ private SurfaceControl mStartingSurface;
BackWindowAnimationAdaptorWrapper(boolean isOpen, int switchType,
@NonNull WindowContainer... targets) {
mAdaptors = new BackWindowAnimationAdaptor[targets.length];
for (int i = targets.length - 1; i >= 0; --i) {
mAdaptors[i] = createAdaptor(targets[i], isOpen, switchType);
}
+ mRemoteAnimationTarget = targets.length > 1 ? createWrapTarget()
+ : mAdaptors[0].mAnimationTarget;
}
boolean isValid() {
@@ -1160,75 +1175,151 @@
}
void cleanUp(boolean startingSurfaceMatch) {
+ cleanUpWindowlessSurface(startingSurfaceMatch);
for (int i = mAdaptors.length - 1; i >= 0; --i) {
- mAdaptors[i].cleanUpWindowlessSurface(startingSurfaceMatch);
mAdaptors[i].mTarget.cancelAnimation();
}
+ mRequestedStartingSurfaceId = INVALID_TASK_ID;
+ mStartingSurface = null;
if (mCloseTransaction != null) {
mCloseTransaction.apply();
mCloseTransaction = null;
}
}
- void onAnimationFinish() {
- final SurfaceControl.Transaction pt = mAdaptors[0].mTarget.getPendingTransaction();
- if (mCloseTransaction != null) {
- pt.merge(mCloseTransaction);
- mCloseTransaction = null;
- }
- if (mAdaptors.length > 1) {
- for (int i = mAdaptors.length - 1; i >= 0; --i) {
- final WindowContainer wc = mAdaptors[i].mTarget;
- final WindowContainer parent = wc.getParent();
- if (parent != null) {
- pt.reparent(wc.getSurfaceControl(),
- parent.getSurfaceControl());
- }
- }
- }
- }
-
- @NonNull RemoteAnimationTarget getOrCreateAnimationTarget() {
+ private RemoteAnimationTarget createWrapTarget() {
// Special handle for opening two activities together.
// If we animate both activities separately, the animation area and rounded corner
// would also being handled separately. To make them seem like "open" together, wrap
// their leash with another animation leash.
- if (mAdaptors.length > 1 && mCloseTransaction == null) {
- final Rect unionBounds = new Rect();
- for (int i = mAdaptors.length - 1; i >= 0; --i) {
- unionBounds.union(mAdaptors[i].mAnimationTarget.localBounds);
- }
- final WindowContainer wc = mAdaptors[0].mTarget;
- final Task task = wc.asActivityRecord() != null
- ? wc.asActivityRecord().getTask() : wc.asTask();
- final RemoteAnimationTarget represent = mAdaptors[0].mAnimationTarget;
- final SurfaceControl leashSurface = new SurfaceControl.Builder()
- .setName("cross-animation-leash")
- .setContainerLayer()
- .setHidden(false)
- .setParent(task.getSurfaceControl())
- .build();
- final SurfaceControl.Transaction pt = wc.getPendingTransaction();
- pt.setLayer(leashSurface, wc.getParent().getLastLayer());
- mCloseTransaction = new SurfaceControl.Transaction();
- mCloseTransaction.reparent(leashSurface, null);
- for (int i = mAdaptors.length - 1; i >= 0; --i) {
- BackWindowAnimationAdaptor adaptor = mAdaptors[i];
- pt.reparent(adaptor.mAnimationTarget.leash, leashSurface);
- pt.setPosition(adaptor.mAnimationTarget.leash,
- adaptor.mAnimationTarget.localBounds.left,
- adaptor.mAnimationTarget.localBounds.top);
- }
- return new RemoteAnimationTarget(represent.taskId, represent.mode, leashSurface,
- represent.isTranslucent, represent.clipRect, represent.contentInsets,
- represent.prefixOrderIndex,
- new Point(unionBounds.left, unionBounds.top),
- unionBounds, unionBounds, represent.windowConfiguration,
- true /* isNotInRecents */, null, null, represent.taskInfo,
- represent.allowEnterPip);
- } else {
- return mAdaptors[0].mAnimationTarget;
+ final Rect unionBounds = new Rect();
+ for (int i = mAdaptors.length - 1; i >= 0; --i) {
+ unionBounds.union(mAdaptors[i].mAnimationTarget.localBounds);
}
+ final WindowContainer wc = mAdaptors[0].mTarget;
+ final Task task = wc.asActivityRecord() != null
+ ? wc.asActivityRecord().getTask() : wc.asTask();
+ final RemoteAnimationTarget represent = mAdaptors[0].mAnimationTarget;
+ final SurfaceControl leashSurface = new SurfaceControl.Builder()
+ .setName("cross-animation-leash")
+ .setContainerLayer()
+ .setHidden(false)
+ .setParent(task.getSurfaceControl())
+ .build();
+ mCloseTransaction = new SurfaceControl.Transaction();
+ mCloseTransaction.reparent(leashSurface, null);
+ final SurfaceControl.Transaction pt = wc.getPendingTransaction();
+ pt.setLayer(leashSurface, wc.getParent().getLastLayer());
+ for (int i = mAdaptors.length - 1; i >= 0; --i) {
+ BackWindowAnimationAdaptor adaptor = mAdaptors[i];
+ pt.reparent(adaptor.mAnimationTarget.leash, leashSurface);
+ pt.setPosition(adaptor.mAnimationTarget.leash,
+ adaptor.mAnimationTarget.localBounds.left,
+ adaptor.mAnimationTarget.localBounds.top);
+ // For adjacent activity embedded, reparent Activity to TaskFragment when
+ // animation finish
+ final WindowContainer parent = adaptor.mTarget.getParent();
+ if (parent != null) {
+ mCloseTransaction.reparent(adaptor.mTarget.getSurfaceControl(),
+ parent.getSurfaceControl());
+ }
+ }
+ return new RemoteAnimationTarget(represent.taskId, represent.mode, leashSurface,
+ represent.isTranslucent, represent.clipRect, represent.contentInsets,
+ represent.prefixOrderIndex,
+ new Point(unionBounds.left, unionBounds.top),
+ unionBounds, unionBounds, represent.windowConfiguration,
+ true /* isNotInRecents */, null, null, represent.taskInfo,
+ represent.allowEnterPip);
+ }
+
+ void createStartingSurface(@NonNull WindowContainer closeWindow,
+ ActivityRecord[] visibleOpenActivities) {
+ if (mAdaptors[0].mSwitchType == DIALOG_CLOSE) {
+ return;
+ }
+ final WindowContainer mainOpen = mAdaptors[0].mTarget;
+ final int switchType = mAdaptors[0].mSwitchType;
+ final Task openTask = switchType == TASK_SWITCH
+ ? mainOpen.asTask() : switchType == ACTIVITY_SWITCH
+ ? mainOpen.asActivityRecord().getTask() : null;
+ if (openTask == null) {
+ return;
+ }
+ final ActivityRecord mainActivity = switchType == ACTIVITY_SWITCH
+ ? mainOpen.asActivityRecord()
+ : openTask.getTopNonFinishingActivity();
+ if (mainActivity == null) {
+ return;
+ }
+ final TaskSnapshot snapshot = getSnapshot(mainOpen, visibleOpenActivities);
+ mRequestedStartingSurfaceId = openTask.mAtmService.mTaskOrganizerController
+ .addWindowlessStartingSurface(openTask, mainActivity,
+ // Choose configuration from closeWindow, because the configuration
+ // of opening target may not update before resume, so the starting
+ // surface should occlude it entirely.
+ mRemoteAnimationTarget.leash, snapshot, closeWindow.getConfiguration(),
+ new IWindowlessStartingSurfaceCallback.Stub() {
+ // Once the starting surface has been created in shell, it will call
+ // onSurfaceAdded to pass the created surface to core, so if a
+ // transition is triggered by the back gesture, there doesn't need to
+ // create another starting surface for the opening target, just reparent
+ // the starting surface to the opening target.
+ // Note if cleanUpWindowlessSurface happen prior than onSurfaceAdded
+ // called, there won't be able to reparent the starting surface on
+ // opening target. But if that happens and transition target is matched,
+ // the app window should already draw.
+ @Override
+ public void onSurfaceAdded(SurfaceControl sc) {
+ synchronized (openTask.mWmService.mGlobalLock) {
+ if (mRequestedStartingSurfaceId != INVALID_TASK_ID) {
+ mStartingSurface = sc;
+ }
+ }
+ }
+ });
+ }
+
+ // When back gesture has triggered and transition target matches navigation target,
+ // reparent the starting surface to the opening target as it's starting window.
+ void reparentWindowlessSurfaceToTarget(SurfaceControl.Transaction reparentTransaction) {
+ if (mRequestedStartingSurfaceId == INVALID_TASK_ID) {
+ return;
+ }
+ // If open target matches, reparent to open activity or task
+ if (mStartingSurface != null && mStartingSurface.isValid()) {
+ SurfaceControl.Transaction transaction = reparentTransaction != null
+ ? reparentTransaction : mAdaptors[0].mTarget.getPendingTransaction();
+ if (mAdaptors.length == 1) {
+ transaction.reparent(mStartingSurface,
+ mAdaptors[0].mTarget.getSurfaceControl());
+ } else {
+ // More than one opening window, reparent starting surface to leaf task.
+ final WindowContainer wc = mAdaptors[0].mTarget;
+ final Task task = wc.asActivityRecord() != null
+ ? wc.asActivityRecord().getTask() : wc.asTask();
+ transaction.reparent(mStartingSurface, task != null
+ ? task.getSurfaceControl()
+ : mAdaptors[0].mTarget.getSurfaceControl());
+ }
+ // remove starting surface.
+ mStartingSurface = null;
+ }
+ }
+
+ /**
+ * Ask shell to clear the starting surface.
+ * @param openTransitionMatch if true, shell will play the remove starting window
+ * animation, otherwise remove it directly.
+ */
+ void cleanUpWindowlessSurface(boolean openTransitionMatch) {
+ if (mRequestedStartingSurfaceId == INVALID_TASK_ID) {
+ return;
+ }
+ mAdaptors[0].mTarget.mWmService.mAtmService.mTaskOrganizerController
+ .removeWindowlessStartingSurface(mRequestedStartingSurfaceId,
+ !openTransitionMatch);
+ mRequestedStartingSurfaceId = INVALID_TASK_ID;
}
}
@@ -1240,11 +1331,6 @@
private RemoteAnimationTarget mAnimationTarget;
private final int mSwitchType;
- // The starting surface task Id. Used to clear the starting surface if the animation has
- // requested one during animating.
- private int mRequestedStartingSurfaceId = INVALID_TASK_ID;
- private SurfaceControl mStartingSurface;
-
BackWindowAnimationAdaptor(@NonNull WindowContainer target, boolean isOpen,
int switchType) {
mBounds.set(target.getBounds());
@@ -1276,8 +1362,6 @@
public void onAnimationCancelled(SurfaceControl animationLeash) {
if (mCapturedLeash == animationLeash) {
mCapturedLeash = null;
- mRequestedStartingSurfaceId = INVALID_TASK_ID;
- mStartingSurface = null;
}
}
@@ -1345,84 +1429,6 @@
r.checkEnterPictureInPictureAppOpsState());
return mAnimationTarget;
}
-
- void createStartingSurface(@NonNull WindowContainer closeWindow,
- @NonNull ActivityRecord[] visibleOpenActivities) {
- if (!mIsOpen) {
- return;
- }
- if (mSwitchType == DIALOG_CLOSE) {
- return;
- }
- final Task openTask = mSwitchType == TASK_SWITCH
- ? mTarget.asTask() : mSwitchType == ACTIVITY_SWITCH
- ? mTarget.asActivityRecord().getTask() : null;
- if (openTask == null) {
- return;
- }
- final ActivityRecord mainActivity = mSwitchType == ACTIVITY_SWITCH
- ? mTarget.asActivityRecord()
- : openTask.getTopNonFinishingActivity();
- if (mainActivity == null) {
- return;
- }
- final TaskSnapshot snapshot = getSnapshot(mTarget, visibleOpenActivities);
- mRequestedStartingSurfaceId = openTask.mAtmService.mTaskOrganizerController
- .addWindowlessStartingSurface(openTask, mainActivity,
- // Choose configuration from closeWindow, because the configuration
- // of opening target may not update before resume, so the starting
- // surface should occlude it entirely.
- mAnimationTarget.leash, snapshot, closeWindow.getConfiguration(),
- new IWindowlessStartingSurfaceCallback.Stub() {
- // Once the starting surface has been created in shell, it will call
- // onSurfaceAdded to pass the created surface to core, so if a
- // transition is triggered by the back gesture, there doesn't need to
- // create another starting surface for the opening target, just reparent
- // the starting surface to the opening target.
- // Note if cleanUpWindowlessSurface happen prior than onSurfaceAdded
- // called, there won't be able to reparent the starting surface on
- // opening target. But if that happens and transition target is matched,
- // the app window should already draw.
- @Override
- public void onSurfaceAdded(SurfaceControl sc) {
- synchronized (mTarget.mWmService.mGlobalLock) {
- if (mRequestedStartingSurfaceId != INVALID_TASK_ID) {
- mStartingSurface = sc;
- }
- }
- }
- });
- }
-
- // When back gesture has triggered and transition target matches navigation target,
- // reparent the starting surface to the opening target as it's starting window.
- void reparentWindowlessSurfaceToTarget() {
- if (mRequestedStartingSurfaceId == INVALID_TASK_ID) {
- return;
- }
- // If open target matches, reparent to open activity or task
- if (mStartingSurface != null && mStartingSurface.isValid()) {
- mTarget.getPendingTransaction()
- .reparent(mStartingSurface, mTarget.getSurfaceControl());
- // remove starting surface.
- mStartingSurface = null;
- }
- }
-
- /**
- * Ask shell to clear the starting surface.
- * @param openTransitionMatch if true, shell will play the remove starting window
- * animation, otherwise remove it directly.
- */
- void cleanUpWindowlessSurface(boolean openTransitionMatch) {
- if (mRequestedStartingSurfaceId == INVALID_TASK_ID) {
- return;
- }
- mTarget.mWmService.mAtmService.mTaskOrganizerController
- .removeWindowlessStartingSurface(mRequestedStartingSurfaceId,
- !openTransitionMatch);
- mRequestedStartingSurfaceId = INVALID_TASK_ID;
- }
}
ScheduleAnimationBuilder prepareAnimation(
@@ -1499,18 +1505,10 @@
* @param visibleOpenActivities The visible activities in opening targets.
*/
private void applyPreviewStrategy(@NonNull WindowContainer closeWindow,
- @NonNull BackWindowAnimationAdaptor[] openAnimationAdaptor,
+ @NonNull BackWindowAnimationAdaptorWrapper openAnimationAdaptor,
@NonNull ActivityRecord[] visibleOpenActivities) {
- if (isSupportWindowlessSurface() && mShowWindowlessSurface && !mIsLaunchBehind
- // TODO (b/274997067) Draw two snapshot in a single starting surface.
- // We are using TaskId as the key of
- // StartingSurfaceDrawer#StartingWindowRecordManager, so we cannot create
- // two activity snapshot with WindowlessStartingWindow.
- // Try to draw two snapshot within a WindowlessStartingWindow, or find
- // another key for StartingWindowRecordManager.
- && openAnimationAdaptor.length == 1) {
- openAnimationAdaptor[0].createStartingSurface(closeWindow,
- visibleOpenActivities);
+ if (isSupportWindowlessSurface() && mShowWindowlessSurface && !mIsLaunchBehind) {
+ openAnimationAdaptor.createStartingSurface(closeWindow, visibleOpenActivities);
} else {
for (int i = visibleOpenActivities.length - 1; i >= 0; --i) {
setLaunchBehind(visibleOpenActivities[i]);
@@ -1541,7 +1539,7 @@
}
mCloseTarget.mTransitionController.mSnapshotController
.mActivitySnapshotController.clearOnBackPressedActivities();
- applyPreviewStrategy(mCloseTarget, mOpenAnimAdaptor.mAdaptors, openingActivities);
+ applyPreviewStrategy(mCloseTarget, mOpenAnimAdaptor, openingActivities);
final IBackAnimationFinishedCallback callback = makeAnimationFinishedCallback();
final RemoteAnimationTarget[] targets = getAnimationTargets();
@@ -1565,7 +1563,6 @@
// animation was canceled
return;
}
- mOpenAnimAdaptor.onAnimationFinish();
if (!triggerBack) {
clearBackAnimateTarget();
} else {
diff --git a/services/core/java/com/android/server/wm/ClientLifecycleManager.java b/services/core/java/com/android/server/wm/ClientLifecycleManager.java
index 5b4fb3e..e48e4e8 100644
--- a/services/core/java/com/android/server/wm/ClientLifecycleManager.java
+++ b/services/core/java/com/android/server/wm/ClientLifecycleManager.java
@@ -87,11 +87,7 @@
void scheduleTransactionItemNow(@NonNull IApplicationThread client,
@NonNull ClientTransactionItem transactionItem) throws RemoteException {
final ClientTransaction clientTransaction = ClientTransaction.obtain(client);
- if (transactionItem.isActivityLifecycleItem()) {
- clientTransaction.setLifecycleStateRequest((ActivityLifecycleItem) transactionItem);
- } else {
- clientTransaction.addCallback(transactionItem);
- }
+ clientTransaction.addTransactionItem(transactionItem);
scheduleTransaction(clientTransaction);
}
@@ -115,11 +111,8 @@
} else {
// TODO(b/260873529): cleanup after launch.
final ClientTransaction clientTransaction = ClientTransaction.obtain(client);
- if (transactionItem.isActivityLifecycleItem()) {
- clientTransaction.setLifecycleStateRequest((ActivityLifecycleItem) transactionItem);
- } else {
- clientTransaction.addCallback(transactionItem);
- }
+ clientTransaction.addTransactionItem(transactionItem);
+
scheduleTransaction(clientTransaction);
}
}
@@ -160,8 +153,8 @@
} else {
// TODO(b/260873529): cleanup after launch.
final ClientTransaction clientTransaction = ClientTransaction.obtain(client);
- clientTransaction.addCallback(transactionItem);
- clientTransaction.setLifecycleStateRequest(lifecycleItem);
+ clientTransaction.addTransactionItem(transactionItem);
+ clientTransaction.addTransactionItem(lifecycleItem);
scheduleTransaction(clientTransaction);
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index e743172..d3acd71 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1127,7 +1127,7 @@
final ActivityRecord activity = w.mActivityRecord;
if (activity != null && activity.isVisibleRequested()) {
- activity.updateLetterboxSurface(w);
+ activity.updateLetterboxSurfaceIfNeeded(w);
final boolean updateAllDrawn = activity.updateDrawnWindowStates(w);
if (updateAllDrawn && !mTmpUpdateAllDrawn.contains(activity)) {
mTmpUpdateAllDrawn.add(activity);
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index a7bbc25..5cf9acd 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -104,7 +104,6 @@
import android.os.Trace;
import android.os.UserHandle;
import android.util.ArraySet;
-import android.util.PrintWriterPrinter;
import android.util.Slog;
import android.util.SparseArray;
import android.view.DisplayInfo;
@@ -2070,8 +2069,7 @@
}
return false;
}
- if (mCachedDecorInsets != null && !mCachedDecorInsets.canPreserve()
- && !mDisplayContent.isSleeping()) {
+ if (mCachedDecorInsets != null && !mCachedDecorInsets.canPreserve() && mScreenOnFully) {
mCachedDecorInsets = null;
}
mDecorInsets.invalidate();
@@ -2136,16 +2134,6 @@
}
mCachedDecorInsets.mPreserveId =
mDisplayContent.mTransitionController.getCollectingTransitionId();
- // The validator will run after the transition is finished. So if the insets are changed
- // during the transition, it can update to the latest state.
- mDisplayContent.mTransitionController.mStateValidators.add(() -> {
- // The insets provider client may defer to change its window until screen is on. So
- // only validate when awake to avoid the cache being always dropped.
- if (!mDisplayContent.isSleeping() && updateDecorInsetsInfo()) {
- Slog.d(TAG, "Insets changed after display switch transition");
- mDisplayContent.sendNewConfiguration();
- }
- });
}
@NavigationBarPosition
@@ -2891,9 +2879,6 @@
if (!CLIENT_TRANSIENT) {
mSystemGestures.dump(pw, prefix);
}
-
- pw.print(prefix); pw.println("Looper state:");
- mHandler.getLooper().dump(new PrintWriterPrinter(pw), prefix + " ");
}
private boolean supportsPointerLocation() {
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java
index edf9da1..5d613cf 100644
--- a/services/core/java/com/android/server/wm/LetterboxUiController.java
+++ b/services/core/java/com/android/server/wm/LetterboxUiController.java
@@ -52,7 +52,6 @@
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_SPLIT_SCREEN;
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_UNSET;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
-import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
import static android.content.res.Configuration.SCREEN_HEIGHT_DP_UNDEFINED;
import static android.content.res.Configuration.SCREEN_WIDTH_DP_UNDEFINED;
@@ -924,21 +923,21 @@
return mLetterbox == null || mLetterbox.notIntersectsOrFullyContains(rect);
}
- void updateLetterboxSurface(WindowState winHint) {
- updateLetterboxSurface(winHint, mActivityRecord.getSyncTransaction());
+ void updateLetterboxSurfaceIfNeeded(WindowState winHint) {
+ updateLetterboxSurfaceIfNeeded(winHint, mActivityRecord.getSyncTransaction());
}
- void updateLetterboxSurface(WindowState winHint, Transaction t) {
+ void updateLetterboxSurfaceIfNeeded(WindowState winHint, Transaction t) {
if (shouldNotLayoutLetterbox(winHint)) {
return;
}
- layoutLetterbox(winHint);
+ layoutLetterboxIfNeeded(winHint);
if (mLetterbox != null && mLetterbox.needsApplySurfaceChanges()) {
mLetterbox.applySurfaceChanges(t);
}
}
- void layoutLetterbox(WindowState w) {
+ void layoutLetterboxIfNeeded(WindowState w) {
if (shouldNotLayoutLetterbox(w)) {
return;
}
@@ -1369,23 +1368,25 @@
*
* <p>Conditions that needs to be met:
* <ul>
- * <li>Activity is portrait-only.
- * <li>Fullscreen window in landscape device orientation.
+ * <li>Windowing mode is fullscreen.
* <li>Horizontal Reachability is enabled.
- * <li>Activity fills parent vertically.
+ * <li>First top opaque activity fills parent vertically, but not horizontally.
* </ul>
*/
private boolean isHorizontalReachabilityEnabled(Configuration parentConfiguration) {
// Use screen resolved bounds which uses resolved bounds or size compat bounds
// as activity bounds can sometimes be empty
+ final Rect opaqueActivityBounds = hasInheritedLetterboxBehavior()
+ ? mFirstOpaqueActivityBeneath.getScreenResolvedBounds()
+ : mActivityRecord.getScreenResolvedBounds();
return mLetterboxConfiguration.getIsHorizontalReachabilityEnabled()
&& parentConfiguration.windowConfiguration.getWindowingMode()
== WINDOWING_MODE_FULLSCREEN
- && (parentConfiguration.orientation == ORIENTATION_LANDSCAPE
- && mActivityRecord.getOrientationForReachability() == ORIENTATION_PORTRAIT)
// Check whether the activity fills the parent vertically.
&& parentConfiguration.windowConfiguration.getAppBounds().height()
- <= mActivityRecord.getScreenResolvedBounds().height();
+ <= opaqueActivityBounds.height()
+ && parentConfiguration.windowConfiguration.getAppBounds().width()
+ > opaqueActivityBounds.width();
}
@VisibleForTesting
@@ -1402,23 +1403,25 @@
*
* <p>Conditions that needs to be met:
* <ul>
- * <li>Activity is landscape-only.
- * <li>Fullscreen window in portrait device orientation.
+ * <li>Windowing mode is fullscreen.
* <li>Vertical Reachability is enabled.
- * <li>Activity fills parent horizontally.
+ * <li>First top opaque activity fills parent horizontally but not vertically.
* </ul>
*/
private boolean isVerticalReachabilityEnabled(Configuration parentConfiguration) {
// Use screen resolved bounds which uses resolved bounds or size compat bounds
// as activity bounds can sometimes be empty
+ final Rect opaqueActivityBounds = hasInheritedLetterboxBehavior()
+ ? mFirstOpaqueActivityBeneath.getScreenResolvedBounds()
+ : mActivityRecord.getScreenResolvedBounds();
return mLetterboxConfiguration.getIsVerticalReachabilityEnabled()
&& parentConfiguration.windowConfiguration.getWindowingMode()
== WINDOWING_MODE_FULLSCREEN
- && (parentConfiguration.orientation == ORIENTATION_PORTRAIT
- && mActivityRecord.getOrientationForReachability() == ORIENTATION_LANDSCAPE)
// Check whether the activity fills the parent horizontally.
- && parentConfiguration.windowConfiguration.getBounds().width()
- == mActivityRecord.getScreenResolvedBounds().width();
+ && parentConfiguration.windowConfiguration.getAppBounds().width()
+ <= opaqueActivityBounds.width()
+ && parentConfiguration.windowConfiguration.getAppBounds().height()
+ > opaqueActivityBounds.height();
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index bf45804..19a9b3f 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -1452,7 +1452,7 @@
return false;
}
- if (enableHomeDelay() && !mService.mAmInternal.getThemeOverlayReadiness()) {
+ if (enableHomeDelay() && !mService.mAmInternal.isThemeOverlayReady(userId)) {
Slog.d(TAG, "ThemeHomeDelay: Home launch was deferred.");
return false;
}
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 975208f..908cbd3 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -110,9 +110,7 @@
private final String mStringName;
SurfaceSession mSurfaceSession;
private final ArrayList<WindowState> mAddedWindows = new ArrayList<>();
- // Set of visible application overlay window surfaces connected to this session.
- private final ArraySet<WindowSurfaceController> mAppOverlaySurfaces = new ArraySet<>();
- // Set of visible alert window surfaces connected to this session.
+ /** Set of visible alert/app-overlay window surfaces connected to this session. */
private final ArraySet<WindowSurfaceController> mAlertWindowSurfaces = new ArraySet<>();
private final DragDropController mDragDropController;
final boolean mCanAddInternalSystemWindow;
@@ -796,46 +794,45 @@
}
boolean changed;
-
- if (!mCanAddInternalSystemWindow && !mCanCreateSystemApplicationOverlay) {
- // We want to track non-system apps adding alert windows so we can post an
- // on-going notification for the user to control their visibility.
- if (visible) {
- changed = mAlertWindowSurfaces.add(surfaceController);
- MetricsLoggerWrapper.logAppOverlayEnter(mUid, mPackageName, changed, type, true);
- } else {
- changed = mAlertWindowSurfaces.remove(surfaceController);
- MetricsLoggerWrapper.logAppOverlayExit(mUid, mPackageName, changed, type, true);
+ // Track non-system apps adding overlay/alert windows, so a notification can post for the
+ // user to control their visibility.
+ final boolean noSystemOverlayPermission =
+ !mCanAddInternalSystemWindow && !mCanCreateSystemApplicationOverlay;
+ if (visible) {
+ changed = mAlertWindowSurfaces.add(surfaceController);
+ if (type == TYPE_APPLICATION_OVERLAY) {
+ MetricsLoggerWrapper.logAppOverlayEnter(mUid, mPackageName, changed, type,
+ false /* set false to only log for TYPE_APPLICATION_OVERLAY */);
+ } else if (noSystemOverlayPermission) {
+ MetricsLoggerWrapper.logAppOverlayEnter(mUid, mPackageName, changed, type,
+ true /* only log for non-TYPE_APPLICATION_OVERLAY */);
}
+ } else {
+ changed = mAlertWindowSurfaces.remove(surfaceController);
+ if (type == TYPE_APPLICATION_OVERLAY) {
+ MetricsLoggerWrapper.logAppOverlayExit(mUid, mPackageName, changed, type,
+ false /* set false to only log for TYPE_APPLICATION_OVERLAY */);
+ } else if (noSystemOverlayPermission) {
+ MetricsLoggerWrapper.logAppOverlayExit(mUid, mPackageName, changed, type,
+ true /* only log for non-TYPE_APPLICATION_OVERLAY */);
+ }
+ }
- if (changed) {
- if (mAlertWindowSurfaces.isEmpty()) {
- cancelAlertWindowNotification();
- } else if (mAlertWindowNotification == null){
- mAlertWindowNotification = new AlertWindowNotification(mService, mPackageName);
- if (mShowingAlertWindowNotificationAllowed) {
- mAlertWindowNotification.post();
- }
+ if (changed && noSystemOverlayPermission) {
+ if (mAlertWindowSurfaces.isEmpty()) {
+ cancelAlertWindowNotification();
+ } else if (mAlertWindowNotification == null) {
+ mAlertWindowNotification = new AlertWindowNotification(mService, mPackageName);
+ if (mShowingAlertWindowNotificationAllowed) {
+ mAlertWindowNotification.post();
}
}
}
- if (type != TYPE_APPLICATION_OVERLAY) {
- return;
- }
-
- if (visible) {
- changed = mAppOverlaySurfaces.add(surfaceController);
- MetricsLoggerWrapper.logAppOverlayEnter(mUid, mPackageName, changed, type, false);
- } else {
- changed = mAppOverlaySurfaces.remove(surfaceController);
- MetricsLoggerWrapper.logAppOverlayExit(mUid, mPackageName, changed, type, false);
- }
-
- if (changed) {
- // Notify activity manager of changes to app overlay windows so it can adjust the
- // importance score for the process.
- setHasOverlayUi(!mAppOverlaySurfaces.isEmpty());
+ if (changed && mPid != WindowManagerService.MY_PID) {
+ // Notify activity manager that the process contains overlay/alert windows, so it can
+ // adjust the importance score for the process.
+ setHasOverlayUi(!mAlertWindowSurfaces.isEmpty());
}
}
@@ -870,12 +867,12 @@
mSurfaceSession = null;
mAddedWindows.clear();
mAlertWindowSurfaces.clear();
- mAppOverlaySurfaces.clear();
setHasOverlayUi(false);
cancelAlertWindowNotification();
}
- private void setHasOverlayUi(boolean hasOverlayUi) {
+ @VisibleForTesting
+ void setHasOverlayUi(boolean hasOverlayUi) {
mService.mH.obtainMessage(H.SET_HAS_OVERLAY_UI, mPid, hasOverlayUi ? 1 : 0).sendToTarget();
}
@@ -890,7 +887,6 @@
void dump(PrintWriter pw, String prefix) {
pw.print(prefix); pw.print("numWindow="); pw.print(mAddedWindows.size());
pw.print(" mCanAddInternalSystemWindow="); pw.print(mCanAddInternalSystemWindow);
- pw.print(" mAppOverlaySurfaces="); pw.print(mAppOverlaySurfaces);
pw.print(" mAlertWindowSurfaces="); pw.print(mAlertWindowSurfaces);
pw.print(" mClientDead="); pw.print(mClientDead);
pw.print(" mSurfaceSession="); pw.println(mSurfaceSession);
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 838ce86..10cbc66 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -1590,7 +1590,7 @@
mAtmService.getLifecycleManager().scheduleTransactionItem(
appThread, activityResultItem);
} else {
- transaction.addCallback(activityResultItem);
+ transaction.addTransactionItem(activityResultItem);
}
}
}
@@ -1602,7 +1602,7 @@
mAtmService.getLifecycleManager().scheduleTransactionItem(
appThread, newIntentItem);
} else {
- transaction.addCallback(newIntentItem);
+ transaction.addTransactionItem(newIntentItem);
}
}
@@ -1624,7 +1624,7 @@
mAtmService.getLifecycleManager().scheduleTransactionItem(
appThread, resumeActivityItem);
} else {
- transaction.setLifecycleStateRequest(resumeActivityItem);
+ transaction.addTransactionItem(resumeActivityItem);
mAtmService.getLifecycleManager().scheduleTransaction(transaction);
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index e538f5d5..61480d2 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -6750,11 +6750,6 @@
private void dumpWindowsLocked(PrintWriter pw, boolean dumpAll,
ArrayList<WindowState> windows) {
pw.println("WINDOW MANAGER WINDOWS (dumpsys window windows)");
- dumpWindowsNoHeaderLocked(pw, dumpAll, windows);
- }
-
- private void dumpWindowsNoHeaderLocked(PrintWriter pw, boolean dumpAll,
- ArrayList<WindowState> windows) {
mRoot.dumpWindowsNoHeader(pw, dumpAll, windows);
if (!mHidingNonSystemOverlayWindows.isEmpty()) {
@@ -6989,9 +6984,15 @@
if (reason != null) {
pw.println(" Reason: " + reason);
}
+ pw.println();
+ final ArrayList<WindowState> relatedWindows = new ArrayList<>();
for (int i = mRoot.getChildCount() - 1; i >= 0; i--) {
final DisplayContent dc = mRoot.getChildAt(i);
final int displayId = dc.getDisplayId();
+ final WindowState currentFocus = dc.mCurrentFocus;
+ final ActivityRecord focusedApp = dc.mFocusedApp;
+ pw.println(" Display #" + displayId + " currentFocus=" + currentFocus
+ + " focusedApp=" + focusedApp);
if (!dc.mWinAddedSinceNullFocus.isEmpty()) {
pw.println(" Windows added in display #" + displayId + " since null focus: "
+ dc.mWinAddedSinceNullFocus);
@@ -7000,12 +7001,25 @@
pw.println(" Windows removed in display #" + displayId + " since null focus: "
+ dc.mWinRemovedSinceNullFocus);
}
+ pw.println(" Tasks in top down Z order:");
+ dc.forAllTaskDisplayAreas(tda -> {
+ tda.dump(pw, " ", false /* dumpAll */);
+ });
+ dc.getInputMonitor().dump(pw, " ");
+ pw.println();
+ dc.forAllWindows(w -> {
+ if ((currentFocus != null && Objects.equals(w.mAttrs.packageName,
+ currentFocus.mAttrs.packageName)) || (focusedApp != null
+ && Objects.equals(w.mAttrs.packageName, focusedApp.packageName))) {
+ relatedWindows.add(w);
+ }
+ }, true /* traverseTopToBottom */);
}
+ if (windowState != null && !relatedWindows.contains(windowState)) {
+ relatedWindows.add(windowState);
+ }
+ mRoot.dumpWindowsNoHeader(pw, true /* dumpAll */, relatedWindows);
pw.println();
- dumpWindowsNoHeaderLocked(pw, true, null);
- pw.println();
- pw.println("Last ANR continued");
- mRoot.dumpDisplayContents(pw);
pw.close();
mLastANRState = sw.toString();
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 6acf1f3..ee16a37 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -28,7 +28,6 @@
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_CONFIGURATION;
import static com.android.internal.util.Preconditions.checkArgument;
import static com.android.server.am.ProcessList.INVALID_ADJ;
-import static com.android.server.grammaticalinflection.GrammaticalInflectionUtils.checkSystemGrammaticalGenderPermission;
import static com.android.server.wm.ActivityRecord.State.DESTROYED;
import static com.android.server.wm.ActivityRecord.State.DESTROYING;
import static com.android.server.wm.ActivityRecord.State.PAUSED;
@@ -299,7 +298,7 @@
*/
private volatile int mActivityStateFlags = ACTIVITY_STATE_FLAG_MASK_MIN_TASK_LAYER;
- private boolean mCanUseSystemGrammaticalGender;
+ private final boolean mCanUseSystemGrammaticalGender;
public WindowProcessController(@NonNull ActivityTaskManagerService atm,
@NonNull ApplicationInfo info, String name, int uid, int userId, Object owner,
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 6e993b3..b8f1d15 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1332,7 +1332,7 @@
updateSourceFrame(windowFrames.mFrame);
if (mActivityRecord != null && !mIsChildWindow) {
- mActivityRecord.layoutLetterbox(this);
+ mActivityRecord.layoutLetterboxIfNeeded(this);
}
mSurfacePlacementNeeded = true;
mHaveFrame = true;
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
index 17638fc..dc8cec9 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/ActiveAdmin.java
@@ -21,8 +21,6 @@
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
import static android.app.admin.WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_ALLOWLIST;
import static android.app.admin.WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_DENYLIST;
-import static android.app.admin.flags.Flags.dumpsysPolicyEngineMigrationEnabled;
-import static android.app.admin.flags.Flags.policyEngineMigrationV2Enabled;
import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_1;
import static com.android.server.devicepolicy.DevicePolicyManagerService.LOG_TAG;
@@ -41,6 +39,7 @@
import android.app.admin.PasswordPolicy;
import android.app.admin.PreferentialNetworkServiceConfig;
import android.app.admin.WifiSsidPolicy;
+import android.app.admin.flags.Flags;
import android.graphics.Color;
import android.net.wifi.WifiSsid;
import android.os.Bundle;
@@ -1297,7 +1296,7 @@
pw.print("encryptionRequested=");
pw.println(encryptionRequested);
- if (!dumpsysPolicyEngineMigrationEnabled()) {
+ if (!Flags.dumpsysPolicyEngineMigrationEnabled()) {
pw.print("disableCamera=");
pw.println(disableCamera);
@@ -1316,7 +1315,8 @@
UserRestrictionsUtils.dumpRestrictions(pw, " ", userRestrictions);
}
- if (!policyEngineMigrationV2Enabled() || !dumpsysPolicyEngineMigrationEnabled()) {
+ if (!Flags.policyEngineMigrationV2Enabled()
+ || !Flags.dumpsysPolicyEngineMigrationEnabled()) {
pw.print("mUsbDataSignaling=");
pw.println(mUsbDataSignalingEnabled);
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
index 0ccf810c..12f4407 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
@@ -24,7 +24,6 @@
import static android.app.admin.PolicyUpdateResult.RESULT_FAILURE_STORAGE_LIMIT_REACHED;
import static android.app.admin.PolicyUpdateResult.RESULT_POLICY_CLEARED;
import static android.app.admin.PolicyUpdateResult.RESULT_POLICY_SET;
-import static android.app.admin.flags.Flags.devicePolicySizeTrackingEnabled;
import static android.content.pm.UserProperties.INHERIT_DEVICE_POLICY_FROM_PARENT;
import android.Manifest;
@@ -42,6 +41,7 @@
import android.app.admin.PolicyValue;
import android.app.admin.TargetUser;
import android.app.admin.UserRestrictionPolicyKey;
+import android.app.admin.flags.Flags;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -225,7 +225,7 @@
synchronized (mLock) {
PolicyState<V> localPolicyState = getLocalPolicyStateLocked(policyDefinition, userId);
- if (devicePolicySizeTrackingEnabled()) {
+ if (Flags.devicePolicySizeTrackingEnabled() && false) {
if (!handleAdminPolicySizeLimit(localPolicyState, enforcingAdmin, value,
policyDefinition, userId)) {
return;
@@ -350,7 +350,7 @@
}
PolicyState<V> localPolicyState = getLocalPolicyStateLocked(policyDefinition, userId);
- if (devicePolicySizeTrackingEnabled()) {
+ if (Flags.devicePolicySizeTrackingEnabled() && false) {
decreasePolicySizeForAdmin(localPolicyState, enforcingAdmin);
}
@@ -496,7 +496,7 @@
synchronized (mLock) {
PolicyState<V> globalPolicyState = getGlobalPolicyStateLocked(policyDefinition);
- if (devicePolicySizeTrackingEnabled()) {
+ if (Flags.devicePolicySizeTrackingEnabled() && false) {
if (!handleAdminPolicySizeLimit(globalPolicyState, enforcingAdmin, value,
policyDefinition, UserHandle.USER_ALL)) {
return;
@@ -568,7 +568,7 @@
synchronized (mLock) {
PolicyState<V> policyState = getGlobalPolicyStateLocked(policyDefinition);
- if (devicePolicySizeTrackingEnabled()) {
+ if (Flags.devicePolicySizeTrackingEnabled() && false) {
decreasePolicySizeForAdmin(policyState, enforcingAdmin);
}
@@ -1892,7 +1892,7 @@
private void writeEnforcingAdminSizeInner(TypedXmlSerializer serializer)
throws IOException {
- if (devicePolicySizeTrackingEnabled()) {
+ if (Flags.devicePolicySizeTrackingEnabled() && false) {
if (mAdminPolicySize != null) {
for (int i = 0; i < mAdminPolicySize.size(); i++) {
int userId = mAdminPolicySize.keyAt(i);
@@ -1916,7 +1916,7 @@
private void writeMaxPolicySizeInner(TypedXmlSerializer serializer)
throws IOException {
- if (!devicePolicySizeTrackingEnabled()) {
+ if (!Flags.devicePolicySizeTrackingEnabled() || true) {
return;
}
serializer.startTag(/* namespace= */ null, TAG_MAX_POLICY_SIZE_LIMIT);
@@ -2081,7 +2081,7 @@
private void readMaxPolicySizeInner(TypedXmlPullParser parser)
throws XmlPullParserException, IOException {
- if (!devicePolicySizeTrackingEnabled()) {
+ if (!Flags.devicePolicySizeTrackingEnabled() || true) {
return;
}
mPolicySizeLimit = parser.getAttributeInt(/* namespace= */ null, ATTR_POLICY_SUM_SIZE);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 58e198e..d5d26be 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -239,14 +239,6 @@
import static android.app.admin.ProvisioningException.ERROR_SETTING_PROFILE_OWNER_FAILED;
import static android.app.admin.ProvisioningException.ERROR_SET_DEVICE_OWNER_FAILED;
import static android.app.admin.ProvisioningException.ERROR_STARTING_PROFILE_FAILED;
-import static android.app.admin.flags.Flags.backupServiceSecurityLogEventEnabled;
-import static android.app.admin.flags.Flags.devicePolicySizeTrackingEnabled;
-import static android.app.admin.flags.Flags.dumpsysPolicyEngineMigrationEnabled;
-import static android.app.admin.flags.Flags.headlessDeviceOwnerSingleUserEnabled;
-import static android.app.admin.flags.Flags.permissionMigrationForZeroTrustImplEnabled;
-import static android.app.admin.flags.Flags.policyEngineMigrationV2Enabled;
-import static android.app.admin.flags.Flags.assistContentUserRestrictionEnabled;
-import static android.app.admin.flags.Flags.securityLogV2Enabled;
import static android.content.Intent.ACTION_MANAGED_PROFILE_AVAILABLE;
import static android.content.Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
@@ -272,6 +264,7 @@
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.PROVISIONING_ENTRY_POINT_ADB;
import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
+import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST;
import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_DPM_LOCK_NOW;
import static com.android.server.SystemTimeZone.TIME_ZONE_CONFIDENCE_HIGH;
import static com.android.server.am.ActivityManagerService.STOCK_PM_FLAGS;
@@ -360,6 +353,7 @@
import android.app.admin.UnsafeStateException;
import android.app.admin.UserRestrictionPolicyKey;
import android.app.admin.WifiSsidPolicy;
+import android.app.admin.flags.Flags;
import android.app.backup.IBackupManager;
import android.app.compat.CompatChanges;
import android.app.role.OnRoleHoldersChangedListener;
@@ -513,7 +507,6 @@
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
import com.android.net.module.util.ProxyUtils;
-import com.android.net.thread.flags.Flags;
import com.android.server.AlarmManagerInternal;
import com.android.server.LocalManagerRegistry;
import com.android.server.LocalServices;
@@ -2728,7 +2721,7 @@
return;
}
- if (securityLogV2Enabled()) {
+ if (Flags.securityLogV2Enabled()) {
boolean auditLoggingEnabled = Boolean.TRUE.equals(
mDevicePolicyEngine.getResolvedPolicy(
PolicyDefinition.AUDIT_LOGGING, UserHandle.USER_ALL));
@@ -3418,7 +3411,7 @@
@GuardedBy("getLockObject()")
private void maybeMigrateSecurityLoggingPolicyLocked() {
- if (!securityLogV2Enabled() || mOwners.isSecurityLoggingMigrated()) {
+ if (!Flags.securityLogV2Enabled() || mOwners.isSecurityLoggingMigrated()) {
return;
}
@@ -3522,7 +3515,7 @@
}
revertTransferOwnershipIfNecessaryLocked();
- if (!policyEngineMigrationV2Enabled()) {
+ if (!Flags.policyEngineMigrationV2Enabled()) {
updateUsbDataSignal(mContext, isUsbDataSignalingEnabledInternalLocked());
}
}
@@ -11151,7 +11144,7 @@
pw.println();
mStatLogger.dump(pw);
pw.println();
- if (dumpsysPolicyEngineMigrationEnabled()) {
+ if (Flags.dumpsysPolicyEngineMigrationEnabled()) {
mDevicePolicyEngine.dump(pw);
pw.println();
}
@@ -12068,7 +12061,7 @@
}
if (packageList != null) {
- if (!devicePolicySizeTrackingEnabled()) {
+ if (!Flags.devicePolicySizeTrackingEnabled()) {
for (String pkg : packageList) {
PolicySizeVerifier.enforceMaxPackageNameLength(pkg);
}
@@ -12313,7 +12306,7 @@
Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller));
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_CREATE_AND_MANAGE_USER);
- if (headlessDeviceOwnerSingleUserEnabled()) {
+ if (Flags.headlessDeviceOwnerSingleUserEnabled()) {
// Block this method if the device is in headless main user mode
Preconditions.checkCallAuthorization(
getHeadlessDeviceOwnerMode() != HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER,
@@ -13438,12 +13431,12 @@
UserManager.DISALLOW_SMS, new String[]{MANAGE_DEVICE_POLICY_SMS});
USER_RESTRICTION_PERMISSIONS.put(
UserManager.DISALLOW_SYSTEM_ERROR_DIALOGS, new String[]{MANAGE_DEVICE_POLICY_SYSTEM_DIALOGS});
- if (Flags.threadUserRestrictionEnabled()) {
+ if (com.android.net.thread.flags.Flags.threadUserRestrictionEnabled()) {
USER_RESTRICTION_PERMISSIONS.put(
UserManager.DISALLOW_THREAD_NETWORK,
new String[]{MANAGE_DEVICE_POLICY_THREAD_NETWORK});
}
- if (assistContentUserRestrictionEnabled()) {
+ if (Flags.assistContentUserRestrictionEnabled()) {
USER_RESTRICTION_PERMISSIONS.put(
UserManager.DISALLOW_ASSIST_CONTENT,
new String[]{MANAGE_DEVICE_POLICY_ASSIST_CONTENT});
@@ -13777,7 +13770,7 @@
return;
}
- if (!devicePolicySizeTrackingEnabled()) {
+ if (!Flags.devicePolicySizeTrackingEnabled()) {
PolicySizeVerifier.enforceMaxStringLength(accountType, "account type");
}
@@ -14391,7 +14384,7 @@
public void setLockTaskPackages(ComponentName who, String callerPackageName, String[] packages)
throws SecurityException {
Objects.requireNonNull(packages, "packages is null");
- if (!devicePolicySizeTrackingEnabled()) {
+ if (!Flags.devicePolicySizeTrackingEnabled()) {
for (String pkg : packages) {
PolicySizeVerifier.enforceMaxPackageNameLength(pkg);
}
@@ -15798,7 +15791,7 @@
@Override
public void enforceSecurityLoggingPolicy(boolean enabled) {
- if (!securityLogV2Enabled()) {
+ if (!Flags.securityLogV2Enabled()) {
return;
}
Boolean auditLoggingEnabled = mDevicePolicyEngine.getResolvedPolicy(
@@ -15808,7 +15801,7 @@
@Override
public void enforceAuditLoggingPolicy(boolean enabled) {
- if (!securityLogV2Enabled()) {
+ if (!Flags.securityLogV2Enabled()) {
return;
}
Boolean securityLoggingEnabled = mDevicePolicyEngine.getResolvedPolicy(
@@ -15834,11 +15827,11 @@
}
private void enforceLoggingPolicy(
- boolean securityLoggingEnabled, boolean auditLoggingEnabled) {
+ boolean securityLogEnabled, boolean auditLogEnabled) {
Slogf.i(LOG_TAG, "Enforcing logging policy, security: %b audit: %b",
- securityLoggingEnabled, auditLoggingEnabled);
- SecurityLog.setLoggingEnabledProperty(securityLoggingEnabled || auditLoggingEnabled);
- setLoggingConfiguration(securityLoggingEnabled, auditLoggingEnabled);
+ securityLogEnabled, auditLogEnabled);
+ mInjector.securityLogSetLoggingEnabledProperty(securityLogEnabled || auditLogEnabled);
+ setLoggingConfiguration(securityLogEnabled, auditLogEnabled);
}
private void setLoggingConfiguration(
@@ -16345,7 +16338,7 @@
mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
}
- if (permissionMigrationForZeroTrustImplEnabled()) {
+ if (Flags.permissionMigrationForZeroTrustImplEnabled()) {
final UserHandle user = UserHandle.of(userId);
final String roleHolderPackage = getRoleHolderPackageNameOnUser(
RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT, userId);
@@ -16359,7 +16352,7 @@
@Override
public SystemUpdateInfo getPendingSystemUpdate(ComponentName admin, String callerPackage) {
- if (permissionMigrationForZeroTrustImplEnabled()) {
+ if (Flags.permissionMigrationForZeroTrustImplEnabled()) {
CallerIdentity caller = getCallerIdentity(admin, callerPackage);
enforcePermissions(new String[] {NOTIFY_PENDING_SYSTEM_UPDATE,
MANAGE_DEVICE_POLICY_QUERY_SYSTEM_UPDATES}, caller.getPackageName(),
@@ -16816,7 +16809,7 @@
return STATUS_HEADLESS_SYSTEM_USER_MODE_NOT_SUPPORTED;
}
- if (headlessDeviceOwnerSingleUserEnabled() && isHeadlessModeSingleUser) {
+ if (Flags.headlessDeviceOwnerSingleUserEnabled() && isHeadlessModeSingleUser) {
ensureSetUpUser = mUserManagerInternal.getMainUserId();
if (ensureSetUpUser == UserHandle.USER_NULL) {
return STATUS_HEADLESS_ONLY_SYSTEM_USER;
@@ -17723,7 +17716,7 @@
}
final CallerIdentity caller = getCallerIdentity(who, packageName);
- if (securityLogV2Enabled()) {
+ if (Flags.securityLogV2Enabled()) {
EnforcingAdmin admin = enforcePermissionAndGetEnforcingAdmin(
who,
MANAGE_DEVICE_POLICY_SECURITY_LOGGING,
@@ -17783,7 +17776,7 @@
return mInjector.securityLogGetLoggingEnabledProperty();
}
- if (securityLogV2Enabled()) {
+ if (Flags.securityLogV2Enabled()) {
final EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
admin,
MANAGE_DEVICE_POLICY_SECURITY_LOGGING,
@@ -17881,7 +17874,7 @@
final CallerIdentity caller = getCallerIdentity(admin, packageName);
- if (securityLogV2Enabled()) {
+ if (Flags.securityLogV2Enabled()) {
EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
admin,
MANAGE_DEVICE_POLICY_SECURITY_LOGGING,
@@ -17936,7 +17929,7 @@
}
final CallerIdentity caller = getCallerIdentity(callingPackage);
- if (!securityLogV2Enabled()) {
+ if (!Flags.securityLogV2Enabled()) {
throw new UnsupportedOperationException("Audit log not enabled");
}
@@ -17964,7 +17957,7 @@
return false;
}
- if (!securityLogV2Enabled()) {
+ if (!Flags.securityLogV2Enabled()) {
throw new UnsupportedOperationException("Audit log not enabled");
}
@@ -18230,7 +18223,7 @@
toggleBackupServiceActive(caller.getUserId(), enabled);
- if (backupServiceSecurityLogEventEnabled()) {
+ if (Flags.backupServiceSecurityLogEventEnabled()) {
if (SecurityLog.isLoggingEnabled()) {
SecurityLog.writeEvent(SecurityLog.TAG_BACKUP_SERVICE_TOGGLED,
caller.getPackageName(), caller.getUserId(), enabled ? 1 : 0);
@@ -20951,7 +20944,7 @@
final CallerIdentity caller = getCallerIdentity(callerPackage);
- if (permissionMigrationForZeroTrustImplEnabled()) {
+ if (Flags.permissionMigrationForZeroTrustImplEnabled()) {
enforcePermission(MANAGE_DEVICE_POLICY_CERTIFICATES, caller.getPackageName());
} else {
Preconditions.checkCallAuthorization(
@@ -21555,7 +21548,7 @@
setTimeAndTimezone(provisioningParams.getTimeZone(), provisioningParams.getLocalTime());
setLocale(provisioningParams.getLocale());
- int deviceOwnerUserId = headlessDeviceOwnerSingleUserEnabled()
+ int deviceOwnerUserId = Flags.headlessDeviceOwnerSingleUserEnabled()
&& getHeadlessDeviceOwnerMode() == HEADLESS_DEVICE_OWNER_MODE_SINGLE_USER
? mUserManagerInternal.getMainUserId()
: UserHandle.USER_SYSTEM;
@@ -21932,7 +21925,7 @@
Objects.requireNonNull(packageName, "Admin package name must be provided");
final CallerIdentity caller = getCallerIdentity(packageName);
- if (!policyEngineMigrationV2Enabled()) {
+ if (!Flags.policyEngineMigrationV2Enabled()) {
Preconditions.checkCallAuthorization(
isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller),
"USB data signaling can only be controlled by a device owner or "
@@ -21942,7 +21935,7 @@
}
synchronized (getLockObject()) {
- if (policyEngineMigrationV2Enabled()) {
+ if (Flags.policyEngineMigrationV2Enabled()) {
EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
/* admin= */ null, MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING,
caller.getPackageName(),
@@ -21982,7 +21975,7 @@
@Override
public boolean isUsbDataSignalingEnabled(String packageName) {
final CallerIdentity caller = getCallerIdentity(packageName);
- if (policyEngineMigrationV2Enabled()) {
+ if (Flags.policyEngineMigrationV2Enabled()) {
Boolean enabled = mDevicePolicyEngine.getResolvedPolicy(
PolicyDefinition.USB_DATA_SIGNALING,
caller.getUserId());
@@ -22107,9 +22100,9 @@
enforcePermission(MANAGE_DEVICE_POLICY_THEFT_DETECTION, caller.getPackageName(),
caller.getUserId());
- //STOPSHIP: replace 1<<9 with
- // LockPatternUtils.SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST once ag/26042068 lands
- return 0 != (mLockPatternUtils.getStrongAuthForUser(caller.getUserId()) & (1 << 9));
+ return mInjector.binderWithCleanCallingIdentity(() ->
+ 0 != (mLockPatternUtils.getStrongAuthForUser(caller.getUserId())
+ & SOME_AUTH_REQUIRED_AFTER_ADAPTIVE_AUTH_REQUEST));
}
@Override
@@ -24235,7 +24228,7 @@
@Override
public void setMaxPolicyStorageLimit(String callerPackageName, int storageLimit) {
- if (!devicePolicySizeTrackingEnabled()) {
+ if (!Flags.devicePolicySizeTrackingEnabled() || true) {
return;
}
CallerIdentity caller = getCallerIdentity(callerPackageName);
@@ -24247,7 +24240,7 @@
@Override
public int getMaxPolicyStorageLimit(String callerPackageName) {
- if (!devicePolicySizeTrackingEnabled()) {
+ if (!Flags.devicePolicySizeTrackingEnabled() || true) {
return -1;
}
CallerIdentity caller = getCallerIdentity(callerPackageName);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java b/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java
index d9fef10..9d73ed0 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/OwnersData.java
@@ -16,11 +16,11 @@
package com.android.server.devicepolicy;
import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_DEFAULT;
-import static android.app.admin.flags.Flags.securityLogV2Enabled;
import android.annotation.Nullable;
import android.app.admin.SystemUpdateInfo;
import android.app.admin.SystemUpdatePolicy;
+import android.app.admin.flags.Flags;
import android.content.ComponentName;
import android.os.UserHandle;
import android.util.ArrayMap;
@@ -400,7 +400,7 @@
out.startTag(null, TAG_POLICY_ENGINE_MIGRATION);
out.attributeBoolean(null, ATTR_MIGRATED_TO_POLICY_ENGINE, mMigratedToPolicyEngine);
- if (securityLogV2Enabled()) {
+ if (Flags.securityLogV2Enabled()) {
out.attributeBoolean(null, ATTR_SECURITY_LOG_MIGRATED, mSecurityLoggingMigrated);
}
out.endTag(null, TAG_POLICY_ENGINE_MIGRATION);
@@ -463,7 +463,7 @@
case TAG_POLICY_ENGINE_MIGRATION:
mMigratedToPolicyEngine = parser.getAttributeBoolean(
null, ATTR_MIGRATED_TO_POLICY_ENGINE, false);
- mSecurityLoggingMigrated = securityLogV2Enabled()
+ mSecurityLoggingMigrated = Flags.securityLogV2Enabled()
&& parser.getAttributeBoolean(null, ATTR_SECURITY_LOG_MIGRATED, false);
break;
default:
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java b/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java
index e8c5658..8cb511e 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PersonalAppsSuspensionHelper.java
@@ -17,11 +17,11 @@
package com.android.server.devicepolicy;
import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_ALL_MASK;
-import static android.app.admin.flags.Flags.defaultSmsPersonalAppSuspensionFixEnabled;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.app.admin.flags.Flags;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -206,7 +206,7 @@
private String getDefaultSmsPackage() {
//TODO(b/319449037): Unflag the following change.
- if (defaultSmsPersonalAppSuspensionFixEnabled()) {
+ if (Flags.defaultSmsPersonalAppSuspensionFixEnabled()) {
return SmsApplication.getDefaultSmsApplicationAsUser(
mContext, /*updateIfNeeded=*/ false, mContext.getUser())
.getPackageName();
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java b/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java
index 02f3918..c582a46 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java
@@ -16,8 +16,6 @@
package com.android.server.devicepolicy;
-import static android.app.admin.flags.Flags.securityLogV2Enabled;
-
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.NANOSECONDS;
@@ -25,6 +23,7 @@
import android.app.admin.IAuditLogEventsCallback;
import android.app.admin.SecurityLog;
import android.app.admin.SecurityLog.SecurityEvent;
+import android.app.admin.flags.Flags;
import android.os.Handler;
import android.os.IBinder;
import android.os.Process;
@@ -468,16 +467,17 @@
assignLogId(event);
}
- if (!securityLogV2Enabled() || mLegacyLogEnabled) {
- addToLegacyBuffer(dedupedLogs);
+ if (!Flags.securityLogV2Enabled() || mLegacyLogEnabled) {
+ addToLegacyBufferLocked(dedupedLogs);
}
- if (securityLogV2Enabled() && mAuditLogEnabled) {
- addAuditLogEvents(dedupedLogs);
+ if (Flags.securityLogV2Enabled() && mAuditLogEnabled) {
+ addAuditLogEventsLocked(dedupedLogs);
}
}
- private void addToLegacyBuffer(List<SecurityEvent> dedupedLogs) {
+ @GuardedBy("mLock")
+ private void addToLegacyBufferLocked(List<SecurityEvent> dedupedLogs) {
// Save the rest of the new batch.
mPendingLogs.addAll(dedupedLogs);
@@ -548,7 +548,7 @@
saveLastEvents(newLogs);
newLogs.clear();
- if (!securityLogV2Enabled() || mLegacyLogEnabled) {
+ if (!Flags.securityLogV2Enabled() || mLegacyLogEnabled) {
notifyDeviceOwnerOrProfileOwnerIfNeeded(force);
}
} catch (IOException e) {
@@ -649,32 +649,28 @@
Slogf.i(TAG, "Set audit log callback for UID %d", uid);
}
- private void addAuditLogEvents(List<SecurityEvent> events) {
- mLock.lock();
- try {
- if (mPaused) {
- // TODO: maybe we need to stash the logs in some temp buffer wile paused so that
- // they can be accessed after affiliation is fixed.
- return;
+ @GuardedBy("mLock")
+ private void addAuditLogEventsLocked(List<SecurityEvent> events) {
+ if (mPaused) {
+ // TODO: maybe we need to stash the logs in some temp buffer wile paused so that
+ // they can be accessed after affiliation is fixed.
+ return;
+ }
+ if (!events.isEmpty()) {
+ for (int i = 0; i < mAuditLogCallbacks.size(); i++) {
+ final int uid = mAuditLogCallbacks.keyAt(i);
+ scheduleSendAuditLogs(uid, mAuditLogCallbacks.valueAt(i), events);
}
- if (!events.isEmpty()) {
- for (int i = 0; i < mAuditLogCallbacks.size(); i++) {
- final int uid = mAuditLogCallbacks.keyAt(i);
- scheduleSendAuditLogs(uid, mAuditLogCallbacks.valueAt(i), events);
- }
- }
- if (DEBUG) {
- Slogf.d(TAG, "Adding audit %d events to % already present in the buffer",
- events.size(), mAuditLogEventBuffer.size());
- }
- mAuditLogEventBuffer.addAll(events);
- trimAuditLogBufferLocked();
- if (DEBUG) {
- Slogf.d(TAG, "Audit event buffer size after trimming: %d",
- mAuditLogEventBuffer.size());
- }
- } finally {
- mLock.unlock();
+ }
+ if (DEBUG) {
+ Slogf.d(TAG, "Adding audit %d events to % already present in the buffer",
+ events.size(), mAuditLogEventBuffer.size());
+ }
+ mAuditLogEventBuffer.addAll(events);
+ trimAuditLogBufferLocked();
+ if (DEBUG) {
+ Slogf.d(TAG, "Audit event buffer size after trimming: %d",
+ mAuditLogEventBuffer.size());
}
}
diff --git a/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java b/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java
index bf2619b..42e41d5 100644
--- a/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java
+++ b/services/foldables/devicestateprovider/src/com/android/server/policy/FoldableDeviceStateProvider.java
@@ -18,8 +18,8 @@
import static android.hardware.SensorManager.SENSOR_DELAY_FASTEST;
import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
-import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE;
-import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE;
+import static android.hardware.devicestate.DeviceStateManager.MAXIMUM_DEVICE_STATE_IDENTIFIER;
+import static android.hardware.devicestate.DeviceStateManager.MINIMUM_DEVICE_STATE_IDENTIFIER;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.TYPE_EXTERNAL;
@@ -478,7 +478,8 @@
}
public static DeviceStateConfiguration createConfig(
- @IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE) int identifier,
+ @IntRange(from = MINIMUM_DEVICE_STATE_IDENTIFIER, to =
+ MAXIMUM_DEVICE_STATE_IDENTIFIER) int identifier,
@NonNull String name,
@DeviceState.DeviceStateFlags int flags,
@NonNull Predicate<FoldableDeviceStateProvider> activeStatePredicate
@@ -488,7 +489,8 @@
}
public static DeviceStateConfiguration createConfig(
- @IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE) int identifier,
+ @IntRange(from = MINIMUM_DEVICE_STATE_IDENTIFIER, to =
+ MAXIMUM_DEVICE_STATE_IDENTIFIER) int identifier,
@NonNull String name,
@NonNull Predicate<FoldableDeviceStateProvider> activeStatePredicate
) {
@@ -498,7 +500,8 @@
/** Create a configuration with availability predicate **/
public static DeviceStateConfiguration createConfig(
- @IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE) int identifier,
+ @IntRange(from = MINIMUM_DEVICE_STATE_IDENTIFIER, to =
+ MAXIMUM_DEVICE_STATE_IDENTIFIER) int identifier,
@NonNull String name,
@DeviceState.DeviceStateFlags int flags,
@NonNull Predicate<FoldableDeviceStateProvider> activeStatePredicate,
@@ -543,7 +546,8 @@
* @return device state configuration
*/
public static DeviceStateConfiguration createTentModeClosedState(
- @IntRange(from = MINIMUM_DEVICE_STATE, to = MAXIMUM_DEVICE_STATE) int identifier,
+ @IntRange(from = MINIMUM_DEVICE_STATE_IDENTIFIER, to =
+ MAXIMUM_DEVICE_STATE_IDENTIFIER) int identifier,
@NonNull String name,
@DeviceState.DeviceStateFlags int flags,
int minClosedAngleDegrees,
diff --git a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
index cb3ee73..4b086b3 100644
--- a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
+++ b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt
@@ -590,8 +590,13 @@
var areGidsPerUser = false
if (!parsedPermission.isTree && packageState.isSystem) {
newState.externalState.configPermissions[permissionName]?.let {
- gids = it.gids
- areGidsPerUser = it.perUser
+ // PermissionEntry.gids may return null when parsing legacy config trying
+ // to work around an issue about upgrading from L platfrm. We can just
+ // ignore such entries now.
+ if (it.gids != null) {
+ gids = it.gids
+ areGidsPerUser = it.perUser
+ }
}
}
newPermission = Permission(
diff --git a/services/tests/PackageManagerServiceTests/apks/Android.bp b/services/tests/PackageManagerServiceTests/apks/Android.bp
index 6c91806..55f2bc9 100644
--- a/services/tests/PackageManagerServiceTests/apks/Android.bp
+++ b/services/tests/PackageManagerServiceTests/apks/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_framework_android_packages",
// 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"
diff --git a/services/tests/PackageManagerServiceTests/apks/install-split-base/Android.bp b/services/tests/PackageManagerServiceTests/apks/install-split-base/Android.bp
index 39992f6..2f5a57f 100644
--- a/services/tests/PackageManagerServiceTests/apks/install-split-base/Android.bp
+++ b/services/tests/PackageManagerServiceTests/apks/install-split-base/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_framework_android_packages",
// 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"
diff --git a/services/tests/PackageManagerServiceTests/apks/install-split-feature-a/Android.bp b/services/tests/PackageManagerServiceTests/apks/install-split-feature-a/Android.bp
index ca7295e..fee9b02 100644
--- a/services/tests/PackageManagerServiceTests/apks/install-split-feature-a/Android.bp
+++ b/services/tests/PackageManagerServiceTests/apks/install-split-feature-a/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_framework_android_packages",
// 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"
diff --git a/services/tests/PackageManagerServiceTests/apks/install/Android.bp b/services/tests/PackageManagerServiceTests/apks/install/Android.bp
index 12175fd..b0a2adb 100644
--- a/services/tests/PackageManagerServiceTests/apks/install/Android.bp
+++ b/services/tests/PackageManagerServiceTests/apks/install/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_framework_android_packages",
// 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"
diff --git a/services/tests/PackageManagerServiceTests/apks/install_bad_dex/Android.bp b/services/tests/PackageManagerServiceTests/apks/install_bad_dex/Android.bp
index ad75668..891ed29 100644
--- a/services/tests/PackageManagerServiceTests/apks/install_bad_dex/Android.bp
+++ b/services/tests/PackageManagerServiceTests/apks/install_bad_dex/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_framework_android_packages",
// 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"
diff --git a/services/tests/PackageManagerServiceTests/apks/install_complete_package_info/Android.bp b/services/tests/PackageManagerServiceTests/apks/install_complete_package_info/Android.bp
index 98aa750..4f03bb9 100644
--- a/services/tests/PackageManagerServiceTests/apks/install_complete_package_info/Android.bp
+++ b/services/tests/PackageManagerServiceTests/apks/install_complete_package_info/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_framework_android_packages",
// 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"
diff --git a/services/tests/PackageManagerServiceTests/apks/install_decl_perm/Android.bp b/services/tests/PackageManagerServiceTests/apks/install_decl_perm/Android.bp
index ef65f5d..452a6ad 100644
--- a/services/tests/PackageManagerServiceTests/apks/install_decl_perm/Android.bp
+++ b/services/tests/PackageManagerServiceTests/apks/install_decl_perm/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_framework_android_packages",
// 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"
diff --git a/services/tests/PackageManagerServiceTests/apks/install_intent_filters/Android.bp b/services/tests/PackageManagerServiceTests/apks/install_intent_filters/Android.bp
index 643824d..b05f0ec 100644
--- a/services/tests/PackageManagerServiceTests/apks/install_intent_filters/Android.bp
+++ b/services/tests/PackageManagerServiceTests/apks/install_intent_filters/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_framework_android_packages",
// 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"
diff --git a/services/tests/PackageManagerServiceTests/apks/install_loc_auto/Android.bp b/services/tests/PackageManagerServiceTests/apks/install_loc_auto/Android.bp
index 4e4ae52..1f1d901e 100644
--- a/services/tests/PackageManagerServiceTests/apks/install_loc_auto/Android.bp
+++ b/services/tests/PackageManagerServiceTests/apks/install_loc_auto/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_framework_android_packages",
// 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"
diff --git a/services/tests/PackageManagerServiceTests/apks/install_loc_internal/Android.bp b/services/tests/PackageManagerServiceTests/apks/install_loc_internal/Android.bp
index 39cdd51..b04bb50 100644
--- a/services/tests/PackageManagerServiceTests/apks/install_loc_internal/Android.bp
+++ b/services/tests/PackageManagerServiceTests/apks/install_loc_internal/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_framework_android_packages",
// 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"
diff --git a/services/tests/PackageManagerServiceTests/apks/install_loc_sdcard/Android.bp b/services/tests/PackageManagerServiceTests/apks/install_loc_sdcard/Android.bp
index ed82793..d0a5192 100644
--- a/services/tests/PackageManagerServiceTests/apks/install_loc_sdcard/Android.bp
+++ b/services/tests/PackageManagerServiceTests/apks/install_loc_sdcard/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_framework_android_packages",
// 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"
diff --git a/services/tests/PackageManagerServiceTests/apks/install_loc_unspecified/Android.bp b/services/tests/PackageManagerServiceTests/apks/install_loc_unspecified/Android.bp
index fd15cb8..7f6b29e 100644
--- a/services/tests/PackageManagerServiceTests/apks/install_loc_unspecified/Android.bp
+++ b/services/tests/PackageManagerServiceTests/apks/install_loc_unspecified/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_framework_android_packages",
// 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"
diff --git a/services/tests/PackageManagerServiceTests/apks/install_target_sdk_22/Android.bp b/services/tests/PackageManagerServiceTests/apks/install_target_sdk_22/Android.bp
index 69b26cc..8159bd1 100644
--- a/services/tests/PackageManagerServiceTests/apks/install_target_sdk_22/Android.bp
+++ b/services/tests/PackageManagerServiceTests/apks/install_target_sdk_22/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_framework_android_packages",
// 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"
diff --git a/services/tests/PackageManagerServiceTests/apks/install_target_sdk_23/Android.bp b/services/tests/PackageManagerServiceTests/apks/install_target_sdk_23/Android.bp
index e3154db..9ce980f 100644
--- a/services/tests/PackageManagerServiceTests/apks/install_target_sdk_23/Android.bp
+++ b/services/tests/PackageManagerServiceTests/apks/install_target_sdk_23/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_framework_android_packages",
// 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"
diff --git a/services/tests/PackageManagerServiceTests/apks/install_use_perm_good/Android.bp b/services/tests/PackageManagerServiceTests/apks/install_use_perm_good/Android.bp
index 959ffbc..e4a5728 100644
--- a/services/tests/PackageManagerServiceTests/apks/install_use_perm_good/Android.bp
+++ b/services/tests/PackageManagerServiceTests/apks/install_use_perm_good/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_framework_android_packages",
// 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"
diff --git a/services/tests/PackageManagerServiceTests/apks/install_uses_feature/Android.bp b/services/tests/PackageManagerServiceTests/apks/install_uses_feature/Android.bp
index fa25af4..eb2f52a 100644
--- a/services/tests/PackageManagerServiceTests/apks/install_uses_feature/Android.bp
+++ b/services/tests/PackageManagerServiceTests/apks/install_uses_feature/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_framework_android_packages",
// 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"
diff --git a/services/tests/PackageManagerServiceTests/apks/install_uses_sdk/Android.bp b/services/tests/PackageManagerServiceTests/apks/install_uses_sdk/Android.bp
index 24e380c..46b4fc2 100644
--- a/services/tests/PackageManagerServiceTests/apks/install_uses_sdk/Android.bp
+++ b/services/tests/PackageManagerServiceTests/apks/install_uses_sdk/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_framework_android_packages",
// 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"
diff --git a/services/tests/PackageManagerServiceTests/apks/keyset/Android.bp b/services/tests/PackageManagerServiceTests/apks/keyset/Android.bp
index ce7919c..d86b51d 100644
--- a/services/tests/PackageManagerServiceTests/apks/keyset/Android.bp
+++ b/services/tests/PackageManagerServiceTests/apks/keyset/Android.bp
@@ -1,5 +1,6 @@
//apks signed by keyset_A
package {
+ default_team: "trendy_team_framework_android_packages",
// 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"
diff --git a/services/tests/PackageManagerServiceTests/host/Android.bp b/services/tests/PackageManagerServiceTests/host/Android.bp
index c617ec4..6fd21f7 100644
--- a/services/tests/PackageManagerServiceTests/host/Android.bp
+++ b/services/tests/PackageManagerServiceTests/host/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_framework_android_packages",
// 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"
diff --git a/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/Android.bp b/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/Android.bp
index e70a734..69d79c9 100644
--- a/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/Android.bp
+++ b/services/tests/PackageManagerServiceTests/host/libs/IntentVerifyUtils/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_framework_android_packages",
// 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"
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/Apex/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/Apex/Android.bp
index aef365e..fe78d7d 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/Apex/Android.bp
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/Apex/Android.bp
@@ -15,6 +15,7 @@
//
package {
+ default_team: "trendy_team_framework_android_packages",
default_applicable_licenses: ["Android-Apache-2.0"],
}
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp
index cea9c59..73cec6c 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/DeviceSide/Android.bp
@@ -15,6 +15,7 @@
//
package {
+ default_team: "trendy_team_framework_android_packages",
// 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"
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/Generic/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/Generic/Android.bp
index b826590..08e41e7 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/Generic/Android.bp
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/Generic/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_framework_android_packages",
// 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"
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/Overlay/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/Overlay/Android.bp
index 92dcd34..2fe7f47 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/Overlay/Android.bp
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/Overlay/Android.bp
@@ -15,6 +15,7 @@
//
package {
+ default_team: "trendy_team_framework_android_packages",
default_applicable_licenses: ["Android-Apache-2.0"],
}
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/OverlayActor/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/OverlayActor/Android.bp
index ed5f2b5..e80b5b5 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/OverlayActor/Android.bp
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/OverlayActor/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_framework_android_packages",
// 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"
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/OverlayTarget/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/OverlayTarget/Android.bp
index 2bb6b82..a8b4dd1 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/OverlayTarget/Android.bp
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/OverlayTarget/Android.bp
@@ -15,6 +15,7 @@
//
package {
+ default_team: "trendy_team_framework_android_packages",
default_applicable_licenses: ["Android-Apache-2.0"],
}
diff --git a/services/tests/PackageManagerServiceTests/host/test-apps/UsesStaticLibrary/Android.bp b/services/tests/PackageManagerServiceTests/host/test-apps/UsesStaticLibrary/Android.bp
index 93d70bb..c65a4ec 100644
--- a/services/tests/PackageManagerServiceTests/host/test-apps/UsesStaticLibrary/Android.bp
+++ b/services/tests/PackageManagerServiceTests/host/test-apps/UsesStaticLibrary/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_framework_android_packages",
// 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"
diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamperTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamperTest.java
index 3458b08..306b4f8 100644
--- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamperTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessWearBedtimeModeClamperTest.java
@@ -85,7 +85,7 @@
@Test
public void testType() {
- assertEquals(BrightnessClamper.Type.BEDTIME_MODE, mClamper.getType());
+ assertEquals(BrightnessClamper.Type.WEAR_BEDTIME_MODE, mClamper.getType());
}
@Test
diff --git a/services/tests/dreamservicetests/Android.bp b/services/tests/dreamservicetests/Android.bp
index 8ef443e..5aa5d61 100644
--- a/services/tests/dreamservicetests/Android.bp
+++ b/services/tests/dreamservicetests/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_system_ui_please_use_a_more_specific_subteam_if_possible_",
// See: http://go/android-license-faq
default_applicable_licenses: ["frameworks_base_license"],
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/OWNERS b/services/tests/mockingservicestests/src/com/android/server/OWNERS
index b363f54..f801560 100644
--- a/services/tests/mockingservicestests/src/com/android/server/OWNERS
+++ b/services/tests/mockingservicestests/src/com/android/server/OWNERS
@@ -2,3 +2,4 @@
per-file *AppStateTracker* = file:/apex/jobscheduler/OWNERS
per-file *DeviceIdleController* = file:/apex/jobscheduler/OWNERS
per-file SensitiveContentProtectionManagerServiceTest.java = file:/core/java/android/permission/OWNERS
+per-file RescuePartyTest.java = file:/packages/CrashRecovery/OWNERS
diff --git a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
index 2d065e2..211a83d 100644
--- a/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/RescuePartyTest.java
@@ -33,6 +33,7 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import android.content.ContentResolver;
@@ -45,7 +46,6 @@
import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.provider.Settings;
-import android.sysprop.CrashRecoveryProperties;
import android.util.ArraySet;
import com.android.dx.mockito.inline.extended.ExtendedMockito;
@@ -64,6 +64,7 @@
import org.mockito.quality.Strictness;
import org.mockito.stubbing.Answer;
+import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
@@ -101,6 +102,7 @@
private MockitoSession mSession;
private HashMap<String, String> mSystemSettingsMap;
+ private HashMap<String, String> mCrashRecoveryPropertiesMap;
//Records the namespaces wiped by setProperties().
private HashSet<String> mNamespacesWiped;
@@ -113,6 +115,9 @@
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private PackageManager mPackageManager;
+ // Mock only sysprop apis
+ private PackageWatchdog.BootThreshold mSpyBootThreshold;
+
@Captor
private ArgumentCaptor<DeviceConfig.MonitorCallback> mMonitorCallbackCaptor;
@Captor
@@ -208,11 +213,12 @@
// Mock PackageWatchdog
doAnswer((Answer<PackageWatchdog>) invocationOnMock -> mMockPackageWatchdog)
.when(() -> PackageWatchdog.getInstance(mMockContext));
+ mockCrashRecoveryProperties(mMockPackageWatchdog);
doReturn(CURRENT_NETWORK_TIME_MILLIS).when(() -> RescueParty.getElapsedRealtime());
- CrashRecoveryProperties.rescueBootCount(0);
- CrashRecoveryProperties.enableRescueParty(true);
+ setCrashRecoveryPropRescueBootCount(0);
+ SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(true));
SystemProperties.set(PROP_DEVICE_CONFIG_DISABLE_FLAG, Boolean.toString(false));
}
@@ -255,7 +261,7 @@
noteBoot(4);
assertTrue(RescueParty.isRebootPropertySet());
- CrashRecoveryProperties.attemptingReboot(false);
+ setCrashRecoveryPropAttemptingReboot(false);
noteBoot(5);
assertTrue(RescueParty.isFactoryResetPropertySet());
}
@@ -280,7 +286,7 @@
noteAppCrash(4, true);
assertTrue(RescueParty.isRebootPropertySet());
- CrashRecoveryProperties.attemptingReboot(false);
+ setCrashRecoveryPropAttemptingReboot(false);
noteAppCrash(5, true);
assertTrue(RescueParty.isFactoryResetPropertySet());
}
@@ -438,7 +444,7 @@
noteBoot(i + 1);
}
assertFalse(RescueParty.isFactoryResetPropertySet());
- CrashRecoveryProperties.attemptingReboot(false);
+ setCrashRecoveryPropAttemptingReboot(false);
noteBoot(LEVEL_FACTORY_RESET + 1);
assertTrue(RescueParty.isAttemptingFactoryReset());
assertTrue(RescueParty.isFactoryResetPropertySet());
@@ -456,7 +462,7 @@
noteBoot(mitigationCount++);
assertFalse(RescueParty.isFactoryResetPropertySet());
noteBoot(mitigationCount++);
- CrashRecoveryProperties.attemptingReboot(false);
+ setCrashRecoveryPropAttemptingReboot(false);
noteBoot(mitigationCount + 1);
assertTrue(RescueParty.isAttemptingFactoryReset());
assertTrue(RescueParty.isFactoryResetPropertySet());
@@ -464,10 +470,10 @@
@Test
public void testThrottlingOnBootFailures() {
- CrashRecoveryProperties.attemptingReboot(false);
+ setCrashRecoveryPropAttemptingReboot(false);
long now = System.currentTimeMillis();
long beforeTimeout = now - TimeUnit.MINUTES.toMillis(THROTTLING_DURATION_MIN - 1);
- CrashRecoveryProperties.lastFactoryResetTimeMs(beforeTimeout);
+ setCrashRecoveryPropLastFactoryReset(beforeTimeout);
for (int i = 1; i <= LEVEL_FACTORY_RESET; i++) {
noteBoot(i);
}
@@ -476,10 +482,10 @@
@Test
public void testThrottlingOnAppCrash() {
- CrashRecoveryProperties.attemptingReboot(false);
+ setCrashRecoveryPropAttemptingReboot(false);
long now = System.currentTimeMillis();
long beforeTimeout = now - TimeUnit.MINUTES.toMillis(THROTTLING_DURATION_MIN - 1);
- CrashRecoveryProperties.lastFactoryResetTimeMs(beforeTimeout);
+ setCrashRecoveryPropLastFactoryReset(beforeTimeout);
for (int i = 0; i <= LEVEL_FACTORY_RESET; i++) {
noteAppCrash(i + 1, true);
}
@@ -488,10 +494,10 @@
@Test
public void testNotThrottlingAfterTimeoutOnBootFailures() {
- CrashRecoveryProperties.attemptingReboot(false);
+ setCrashRecoveryPropAttemptingReboot(false);
long now = System.currentTimeMillis();
long afterTimeout = now - TimeUnit.MINUTES.toMillis(THROTTLING_DURATION_MIN + 1);
- CrashRecoveryProperties.lastFactoryResetTimeMs(afterTimeout);
+ setCrashRecoveryPropLastFactoryReset(afterTimeout);
for (int i = 1; i <= LEVEL_FACTORY_RESET; i++) {
noteBoot(i);
}
@@ -499,10 +505,10 @@
}
@Test
public void testNotThrottlingAfterTimeoutOnAppCrash() {
- CrashRecoveryProperties.attemptingReboot(false);
+ setCrashRecoveryPropAttemptingReboot(false);
long now = System.currentTimeMillis();
long afterTimeout = now - TimeUnit.MINUTES.toMillis(THROTTLING_DURATION_MIN + 1);
- CrashRecoveryProperties.lastFactoryResetTimeMs(afterTimeout);
+ setCrashRecoveryPropLastFactoryReset(afterTimeout);
for (int i = 0; i <= LEVEL_FACTORY_RESET; i++) {
noteAppCrash(i + 1, true);
}
@@ -525,26 +531,26 @@
@Test
public void testExplicitlyEnablingAndDisablingRescue() {
- CrashRecoveryProperties.enableRescueParty(false);
+ SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(false));
SystemProperties.set(PROP_DISABLE_RESCUE, Boolean.toString(true));
assertEquals(RescuePartyObserver.getInstance(mMockContext).execute(sFailingPackage,
PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 1), false);
- CrashRecoveryProperties.enableRescueParty(true);
+ SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(true));
assertTrue(RescuePartyObserver.getInstance(mMockContext).execute(sFailingPackage,
PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 1));
}
@Test
public void testDisablingRescueByDeviceConfigFlag() {
- CrashRecoveryProperties.enableRescueParty(false);
+ SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(false));
SystemProperties.set(PROP_DEVICE_CONFIG_DISABLE_FLAG, Boolean.toString(true));
assertEquals(RescuePartyObserver.getInstance(mMockContext).execute(sFailingPackage,
PackageWatchdog.FAILURE_REASON_APP_NOT_RESPONDING, 1), false);
// Restore the property value initialized in SetUp()
- CrashRecoveryProperties.enableRescueParty(true);
+ SystemProperties.set(RescueParty.PROP_ENABLE_RESCUE, Boolean.toString(true));
SystemProperties.set(PROP_DEVICE_CONFIG_DISABLE_FLAG, Boolean.toString(false));
}
@@ -753,4 +759,138 @@
RescuePartyObserver.getInstance(mMockContext).execute(new VersionedPackage(
packageName, 1), PackageWatchdog.FAILURE_REASON_APP_CRASH, mitigationCount);
}
+
+ // Mock CrashRecoveryProperties as they cannot be accessed due to SEPolicy restrictions
+ private void mockCrashRecoveryProperties(PackageWatchdog watchdog) {
+ // mock properties in RescueParty
+ try {
+
+ doAnswer((Answer<Boolean>) invocationOnMock -> {
+ String storedValue = mCrashRecoveryPropertiesMap
+ .getOrDefault("crashrecovery.attempting_factory_reset", "false");
+ return Boolean.parseBoolean(storedValue);
+ }).when(() -> RescueParty.isFactoryResetPropertySet());
+ doAnswer((Answer<Void>) invocationOnMock -> {
+ boolean value = invocationOnMock.getArgument(0);
+ mCrashRecoveryPropertiesMap.put("crashrecovery.attempting_factory_reset",
+ Boolean.toString(value));
+ return null;
+ }).when(() -> RescueParty.setFactoryResetProperty(anyBoolean()));
+
+ doAnswer((Answer<Boolean>) invocationOnMock -> {
+ String storedValue = mCrashRecoveryPropertiesMap
+ .getOrDefault("crashrecovery.attempting_reboot", "false");
+ return Boolean.parseBoolean(storedValue);
+ }).when(() -> RescueParty.isRebootPropertySet());
+ doAnswer((Answer<Void>) invocationOnMock -> {
+ boolean value = invocationOnMock.getArgument(0);
+ setCrashRecoveryPropAttemptingReboot(value);
+ return null;
+ }).when(() -> RescueParty.setRebootProperty(anyBoolean()));
+
+ doAnswer((Answer<Long>) invocationOnMock -> {
+ String storedValue = mCrashRecoveryPropertiesMap
+ .getOrDefault("persist.crashrecovery.last_factory_reset", "0");
+ return Long.parseLong(storedValue);
+ }).when(() -> RescueParty.getLastFactoryResetTimeMs());
+ doAnswer((Answer<Void>) invocationOnMock -> {
+ long value = invocationOnMock.getArgument(0);
+ setCrashRecoveryPropLastFactoryReset(value);
+ return null;
+ }).when(() -> RescueParty.setLastFactoryResetTimeMs(anyLong()));
+
+ doAnswer((Answer<Integer>) invocationOnMock -> {
+ String storedValue = mCrashRecoveryPropertiesMap
+ .getOrDefault("crashrecovery.max_rescue_level_attempted", "0");
+ return Integer.parseInt(storedValue);
+ }).when(() -> RescueParty.getMaxRescueLevelAttempted());
+ doAnswer((Answer<Void>) invocationOnMock -> {
+ int value = invocationOnMock.getArgument(0);
+ mCrashRecoveryPropertiesMap.put("crashrecovery.max_rescue_level_attempted",
+ Integer.toString(value));
+ return null;
+ }).when(() -> RescueParty.setMaxRescueLevelAttempted(anyInt()));
+
+ } catch (Exception e) {
+ // tests will fail, just printing the error
+ System.out.println("Error while mocking crashrecovery properties " + e.getMessage());
+ }
+
+ // mock properties in BootThreshold
+ try {
+ mSpyBootThreshold = spy(watchdog.new BootThreshold(
+ PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_COUNT,
+ PackageWatchdog.DEFAULT_BOOT_LOOP_TRIGGER_WINDOW_MS));
+ mCrashRecoveryPropertiesMap = new HashMap<>();
+
+ doAnswer((Answer<Integer>) invocationOnMock -> {
+ String storedValue = mCrashRecoveryPropertiesMap
+ .getOrDefault("crashrecovery.rescue_boot_count", "0");
+ return Integer.parseInt(storedValue);
+ }).when(mSpyBootThreshold).getCount();
+ doAnswer((Answer<Void>) invocationOnMock -> {
+ int count = invocationOnMock.getArgument(0);
+ setCrashRecoveryPropRescueBootCount(count);
+ return null;
+ }).when(mSpyBootThreshold).setCount(anyInt());
+
+ doAnswer((Answer<Integer>) invocationOnMock -> {
+ String storedValue = mCrashRecoveryPropertiesMap
+ .getOrDefault("crashrecovery.boot_mitigation_count", "0");
+ return Integer.parseInt(storedValue);
+ }).when(mSpyBootThreshold).getMitigationCount();
+ doAnswer((Answer<Void>) invocationOnMock -> {
+ int count = invocationOnMock.getArgument(0);
+ mCrashRecoveryPropertiesMap.put("crashrecovery.boot_mitigation_count",
+ Integer.toString(count));
+ return null;
+ }).when(mSpyBootThreshold).setMitigationCount(anyInt());
+
+ doAnswer((Answer<Long>) invocationOnMock -> {
+ String storedValue = mCrashRecoveryPropertiesMap
+ .getOrDefault("crashrecovery.rescue_boot_start", "0");
+ return Long.parseLong(storedValue);
+ }).when(mSpyBootThreshold).getStart();
+ doAnswer((Answer<Void>) invocationOnMock -> {
+ long count = invocationOnMock.getArgument(0);
+ mCrashRecoveryPropertiesMap.put("crashrecovery.rescue_boot_start",
+ Long.toString(count));
+ return null;
+ }).when(mSpyBootThreshold).setStart(anyLong());
+
+ doAnswer((Answer<Long>) invocationOnMock -> {
+ String storedValue = mCrashRecoveryPropertiesMap
+ .getOrDefault("crashrecovery.boot_mitigation_start", "0");
+ return Long.parseLong(storedValue);
+ }).when(mSpyBootThreshold).getMitigationStart();
+ doAnswer((Answer<Void>) invocationOnMock -> {
+ long count = invocationOnMock.getArgument(0);
+ mCrashRecoveryPropertiesMap.put("crashrecovery.boot_mitigation_start",
+ Long.toString(count));
+ return null;
+ }).when(mSpyBootThreshold).setMitigationStart(anyLong());
+
+ Field mBootThresholdField = watchdog.getClass().getDeclaredField("mBootThreshold");
+ mBootThresholdField.setAccessible(true);
+ mBootThresholdField.set(watchdog, mSpyBootThreshold);
+ } catch (Exception e) {
+ // tests will fail, just printing the error
+ System.out.println("Error while spying BootThreshold " + e.getMessage());
+ }
+ }
+
+ private void setCrashRecoveryPropRescueBootCount(int count) {
+ mCrashRecoveryPropertiesMap.put("crashrecovery.rescue_boot_count",
+ Integer.toString(count));
+ }
+
+ private void setCrashRecoveryPropAttemptingReboot(boolean value) {
+ mCrashRecoveryPropertiesMap.put("crashrecovery.attempting_reboot",
+ Boolean.toString(value));
+ }
+
+ private void setCrashRecoveryPropLastFactoryReset(long value) {
+ mCrashRecoveryPropertiesMap.put("persist.crashrecovery.last_factory_reset",
+ Long.toString(value));
+ }
}
diff --git a/services/tests/powerservicetests/Android.bp b/services/tests/powerservicetests/Android.bp
index 8d455fe..729dcbd 100644
--- a/services/tests/powerservicetests/Android.bp
+++ b/services/tests/powerservicetests/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_powermanager_framework",
// See: http://go/android-license-faq
default_applicable_licenses: ["frameworks_base_license"],
}
diff --git a/services/tests/powerstatstests/Android.bp b/services/tests/powerstatstests/Android.bp
index 1de049e..51c9d0a 100644
--- a/services/tests/powerstatstests/Android.bp
+++ b/services/tests/powerstatstests/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_framework_backstage_power",
// See: http://go/android-license-faq
default_applicable_licenses: ["frameworks_base_license"],
}
diff --git a/services/tests/powerstatstests/BstatsTestApp/Android.bp b/services/tests/powerstatstests/BstatsTestApp/Android.bp
index c82da9e..7408d13 100644
--- a/services/tests/powerstatstests/BstatsTestApp/Android.bp
+++ b/services/tests/powerstatstests/BstatsTestApp/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_framework_backstage_power",
// 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"
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
index b12d6da..89d146d 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityWindowManagerTest.java
@@ -41,12 +41,12 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.annotation.Nullable;
import android.graphics.Region;
import android.os.IBinder;
import android.os.LocaleList;
import android.os.RemoteException;
import android.os.UserHandle;
-import android.text.TextUtils;
import android.util.SparseArray;
import android.view.Display;
import android.view.IWindow;
@@ -452,7 +452,9 @@
false, mockHostToken, USER_SYSTEM_ID);
final int embeddedWindowId = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY,
false, mockEmbeddedToken, USER_SYSTEM_ID);
+
mA11yWindowManager.associateEmbeddedHierarchyLocked(mockHostToken, mockEmbeddedToken);
+
final int resolvedWindowId = mA11yWindowManager.resolveParentWindowIdLocked(
embeddedWindowId);
assertEquals(hostWindowId, resolvedWindowId);
@@ -467,10 +469,13 @@
false, mockHostToken, USER_SYSTEM_ID);
final int embeddedWindowId = addAccessibilityInteractionConnection(Display.DEFAULT_DISPLAY,
false, mockEmbeddedToken, USER_SYSTEM_ID);
+
mA11yWindowManager.associateEmbeddedHierarchyLocked(mockHostToken, mockEmbeddedToken);
mA11yWindowManager.disassociateEmbeddedHierarchyLocked(mockEmbeddedToken);
+
final int resolvedWindowId = mA11yWindowManager.resolveParentWindowIdLocked(
embeddedWindowId);
+ assertNotEquals(hostWindowId, resolvedWindowId);
assertEquals(embeddedWindowId, resolvedWindowId);
}
@@ -917,7 +922,7 @@
final AccessibilityWindowInfo a11yWindow = mA11yWindowManager.findA11yWindowInfoByIdLocked(
windowId);
- assertTrue(TextUtils.equals(layoutParams.accessibilityTitle, a11yWindow.getTitle()));
+ assertEquals(toString(layoutParams.accessibilityTitle), toString(a11yWindow.getTitle()));
}
@Test
@@ -1057,7 +1062,7 @@
when(mockWindowToken.asBinder()).thenReturn(mockWindowBinder);
when(mMockA11ySecurityPolicy.isCallerInteractingAcrossUsers(userId))
.thenReturn(bGlobal);
- when(mMockWindowManagerInternal.getDisplayIdForWindow(mockWindowToken.asBinder()))
+ when(mMockWindowManagerInternal.getDisplayIdForWindow(mockWindowBinder))
.thenReturn(displayId);
int windowId = mA11yWindowManager.addAccessibilityInteractionConnection(
@@ -1077,7 +1082,7 @@
when(mockWindowToken.asBinder()).thenReturn(mockWindowBinder);
when(mMockA11ySecurityPolicy.isCallerInteractingAcrossUsers(userId))
.thenReturn(bGlobal);
- when(mMockWindowManagerInternal.getDisplayIdForWindow(mockWindowToken.asBinder()))
+ when(mMockWindowManagerInternal.getDisplayIdForWindow(mockWindowBinder))
.thenReturn(displayId);
int windowId = mA11yWindowManager.addAccessibilityInteractionConnection(
@@ -1148,6 +1153,11 @@
}
}
+ @Nullable
+ private static String toString(@Nullable CharSequence cs) {
+ return cs == null ? null : cs.toString();
+ }
+
static class DisplayIdMatcher extends TypeSafeMatcher<AccessibilityEvent> {
private final int mDisplayId;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/BrailleDisplayConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/BrailleDisplayConnectionTest.java
index 7c278ce..b322dd7 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/BrailleDisplayConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/BrailleDisplayConnectionTest.java
@@ -18,22 +18,31 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.accessibilityservice.BrailleDisplayController;
+import android.content.Context;
import android.os.Bundle;
+import android.os.IBinder;
import android.testing.DexmakerShareClassLoaderRule;
+import androidx.test.platform.app.InstrumentationRegistry;
+
import com.google.common.truth.Expect;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
+import java.io.File;
import java.nio.file.Path;
import java.util.List;
@@ -54,6 +63,8 @@
@Rule
public final Expect expect = Expect.create();
+ private Context mContext;
+
// To mock package-private class
@Rule
public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
@@ -62,7 +73,34 @@
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
- mBrailleDisplayConnection = new BrailleDisplayConnection(new Object(), mServiceConnection);
+ mContext = InstrumentationRegistry.getInstrumentation().getContext();
+ when(mServiceConnection.isConnectedLocked()).thenReturn(true);
+ mBrailleDisplayConnection =
+ spy(new BrailleDisplayConnection(new Object(), mServiceConnection));
+ }
+
+ @Test
+ public void defaultNativeScanner_getHidrawNodePaths_returnsHidrawPaths() throws Exception {
+ File testDir = mContext.getFilesDir();
+ Path hidrawNode0 = Path.of(testDir.getPath(), "hidraw0");
+ Path hidrawNode1 = Path.of(testDir.getPath(), "hidraw1");
+ Path otherDevice = Path.of(testDir.getPath(), "otherDevice");
+ Path[] nodePaths = {hidrawNode0, hidrawNode1, otherDevice};
+ try {
+ for (Path node : nodePaths) {
+ assertThat(node.toFile().createNewFile()).isTrue();
+ }
+
+ BrailleDisplayConnection.BrailleDisplayScanner scanner =
+ mBrailleDisplayConnection.getDefaultNativeScanner(mNativeInterface);
+
+ assertThat(scanner.getHidrawNodePaths(testDir.toPath()))
+ .containsExactly(hidrawNode0, hidrawNode1);
+ } finally {
+ for (Path node : nodePaths) {
+ node.toFile().delete();
+ }
+ }
}
@Test
@@ -123,9 +161,38 @@
.isEqualTo(BrailleDisplayConnection.BUS_BLUETOOTH);
}
+ @Test
+ public void write_bypassesServiceSideCheckWithLargeBuffer_disconnects() {
+ Mockito.doNothing().when(mBrailleDisplayConnection).disconnect();
+ mBrailleDisplayConnection.write(
+ new byte[IBinder.getSuggestedMaxIpcSizeBytes() * 2]);
+
+ verify(mBrailleDisplayConnection).disconnect();
+ }
+
+ @Test
+ public void write_notConnected_throwsIllegalStateException() {
+ when(mServiceConnection.isConnectedLocked()).thenReturn(false);
+
+ assertThrows(IllegalStateException.class,
+ () -> mBrailleDisplayConnection.write(new byte[1]));
+ }
+
+ @Test
+ public void write_unableToCreateWriteStream_disconnects() {
+ Mockito.doNothing().when(mBrailleDisplayConnection).disconnect();
+ // mBrailleDisplayConnection#connectLocked was never called so the
+ // connection's mHidrawNode is still null. This will throw an exception
+ // when attempting to create FileOutputStream on the node.
+ mBrailleDisplayConnection.write(new byte[1]);
+
+ verify(mBrailleDisplayConnection).disconnect();
+ }
+
// BrailleDisplayConnection#setTestData() is used to enable CTS testing with
// test Braille display data, but its own implementation should also be tested
// so that issues in this helper don't cause confusing failures in CTS.
+
@Test
public void setTestData_scannerReturnsTestData() {
Bundle bd1 = new Bundle(), bd2 = new Bundle();
@@ -148,7 +215,7 @@
BrailleDisplayConnection.BrailleDisplayScanner scanner =
mBrailleDisplayConnection.setTestData(List.of(bd1, bd2));
- expect.that(scanner.getHidrawNodePaths()).containsExactly(path1, path2);
+ expect.that(scanner.getHidrawNodePaths(Path.of("/dev"))).containsExactly(path1, path2);
expect.that(scanner.getDeviceReportDescriptor(path1)).isEqualTo(desc1);
expect.that(scanner.getDeviceReportDescriptor(path2)).isEqualTo(desc2);
expect.that(scanner.getUniqueId(path1)).isEqualTo(uniq1);
@@ -156,4 +223,12 @@
expect.that(scanner.getDeviceBusType(path1)).isEqualTo(bus1);
expect.that(scanner.getDeviceBusType(path2)).isEqualTo(bus2);
}
+
+ @Test
+ public void setTestData_emptyTestData_returnsNullNodePaths() {
+ BrailleDisplayConnection.BrailleDisplayScanner scanner =
+ mBrailleDisplayConnection.setTestData(List.of());
+
+ expect.that(scanner.getHidrawNodePaths(Path.of("/dev"))).isNull();
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
index cd904eb..a0c4b5e 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationControllerTest.java
@@ -150,6 +150,9 @@
@Mock
private DisplayManagerInternal mDisplayManagerInternal;
+ @Mock
+ private Scroller mMockScroller;
+
// To mock package-private class
@Rule
public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
@@ -208,7 +211,7 @@
mScaleProvider,
() -> null,
ConcurrentUtils.DIRECT_EXECUTOR,
- () -> new Scroller(mContext),
+ () -> mMockScroller,
() -> mTimeAnimator));
mScreenMagnificationController.register(TEST_DISPLAY);
diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
index 4307ec5..cea10ea 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -87,6 +87,7 @@
import android.os.IRemoteCallback;
import android.os.Looper;
import android.os.Message;
+import android.os.PowerManagerInternal;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
@@ -1223,6 +1224,7 @@
private final UserManagerInternal mUserManagerInternalMock;
private final WindowManagerService mWindowManagerMock;
private final ActivityTaskManagerInternal mActivityTaskManagerInternal;
+ private final PowerManagerInternal mPowerManagerInternal;
private final KeyguardManager mKeyguardManagerMock;
private final LockPatternUtils mLockPatternUtilsMock;
@@ -1244,6 +1246,7 @@
mWindowManagerMock = mock(WindowManagerService.class);
mActivityTaskManagerInternal = mock(ActivityTaskManagerInternal.class);
mStorageManagerMock = mock(IStorageManager.class);
+ mPowerManagerInternal = mock(PowerManagerInternal.class);
mKeyguardManagerMock = mock(KeyguardManager.class);
when(mKeyguardManagerMock.isDeviceSecure(anyInt())).thenReturn(true);
mLockPatternUtilsMock = mock(LockPatternUtils.class);
@@ -1309,6 +1312,11 @@
}
@Override
+ PowerManagerInternal getPowerManagerInternal() {
+ return mPowerManagerInternal;
+ }
+
+ @Override
KeyguardManager getKeyguardManager() {
return mKeyguardManagerMock;
}
diff --git a/services/tests/servicestests/src/com/android/server/audio/AbsoluteVolumeBehaviorTest.java b/services/tests/servicestests/src/com/android/server/audio/AbsoluteVolumeBehaviorTest.java
index fc5819d..e756082 100644
--- a/services/tests/servicestests/src/com/android/server/audio/AbsoluteVolumeBehaviorTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/AbsoluteVolumeBehaviorTest.java
@@ -69,6 +69,7 @@
private AudioSystemAdapter mSpyAudioSystem;
private SystemServerAdapter mSystemServer;
private SettingsAdapter mSettingsAdapter;
+ private AudioVolumeGroupHelperBase mAudioVolumeGroupHelper;
private TestLooper mTestLooper;
private AudioService mAudioService;
@@ -93,9 +94,11 @@
mSpyAudioSystem = spy(new NoOpAudioSystemAdapter());
mSystemServer = new NoOpSystemServerAdapter();
mSettingsAdapter = new NoOpSettingsAdapter();
+ mAudioVolumeGroupHelper = new AudioVolumeGroupHelperBase();
mAudioService = new AudioService(mContext, mSpyAudioSystem, mSystemServer,
- mSettingsAdapter, mMockAudioPolicy, mTestLooper.getLooper()) {
+ mSettingsAdapter, mAudioVolumeGroupHelper, mMockAudioPolicy,
+ mTestLooper.getLooper()) {
@Override
public int getDeviceForStream(int stream) {
return AudioSystem.DEVICE_OUT_SPEAKER;
diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceVolumeManagerTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceVolumeManagerTest.java
index d4d3128..3623012 100644
--- a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceVolumeManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceVolumeManagerTest.java
@@ -56,6 +56,7 @@
private AudioSystemAdapter mSpyAudioSystem;
private SystemServerAdapter mSystemServer;
private SettingsAdapter mSettingsAdapter;
+ private AudioVolumeGroupHelperBase mAudioVolumeGroupHelper;
private TestLooper mTestLooper;
private AudioPolicyFacade mAudioPolicyMock = mock(AudioPolicyFacade.class);
@@ -71,8 +72,10 @@
mSystemServer = new NoOpSystemServerAdapter();
mSettingsAdapter = new NoOpSettingsAdapter();
+ mAudioVolumeGroupHelper = new AudioVolumeGroupHelperBase();
mAudioService = new AudioService(mContext, mSpyAudioSystem, mSystemServer,
- mSettingsAdapter, mAudioPolicyMock, mTestLooper.getLooper()) {
+ mSettingsAdapter, mAudioVolumeGroupHelper, mAudioPolicyMock,
+ mTestLooper.getLooper()) {
@Override
public int getDeviceForStream(int stream) {
return AudioSystem.DEVICE_OUT_SPEAKER;
@@ -82,8 +85,9 @@
mTestLooper.dispatchAll();
}
+ // ------------ AudioDeviceVolumeManager related tests ------------
@Test
- public void testSetDeviceVolume() {
+ public void setDeviceVolume_checkIndex() {
AudioManager am = mContext.getSystemService(AudioManager.class);
final int minIndex = am.getStreamMinVolume(AudioManager.STREAM_MUSIC);
final int maxIndex = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
@@ -110,7 +114,7 @@
@Test
@RequiresFlagsDisabled(FLAG_DISABLE_PRESCALE_ABSOLUTE_VOLUME)
- public void testConfigurablePreScaleAbsoluteVolume() throws Exception {
+ public void configurablePreScaleAbsoluteVolume_checkIndex() throws Exception {
AudioManager am = mContext.getSystemService(AudioManager.class);
final int minIndex = am.getStreamMinVolume(AudioManager.STREAM_MUSIC);
final int maxIndex = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
@@ -159,7 +163,7 @@
@Test
@RequiresFlagsEnabled(FLAG_DISABLE_PRESCALE_ABSOLUTE_VOLUME)
- public void testDisablePreScaleAbsoluteVolume() throws Exception {
+ public void disablePreScaleAbsoluteVolume_checkIndex() throws Exception {
AudioManager am = mContext.getSystemService(AudioManager.class);
final int minIndex = am.getStreamMinVolume(AudioManager.STREAM_MUSIC);
final int maxIndex = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java
index e565faa..634877e 100644
--- a/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/AudioServiceTest.java
@@ -60,6 +60,7 @@
private Context mContext;
private AudioSystemAdapter mSpyAudioSystem;
private SettingsAdapter mSettingsAdapter;
+ private AudioVolumeGroupHelperBase mAudioVolumeGroupHelper;
@Spy private NoOpSystemServerAdapter mSpySystemServer;
@Mock private AppOpsManager mMockAppOpsManager;
@@ -80,11 +81,12 @@
mContext = InstrumentationRegistry.getTargetContext();
mSpyAudioSystem = spy(new NoOpAudioSystemAdapter());
mSettingsAdapter = new NoOpSettingsAdapter();
+ mAudioVolumeGroupHelper = new AudioVolumeGroupHelperBase();
when(mMockAppOpsManager.noteOp(anyInt(), anyInt(), anyString(), anyString(), anyString()))
.thenReturn(AppOpsManager.MODE_ALLOWED);
mAudioService = new AudioService(mContext, mSpyAudioSystem, mSpySystemServer,
- mSettingsAdapter, mMockAudioPolicy, null, mMockAppOpsManager,
- mMockPermissionEnforcer);
+ mSettingsAdapter, mAudioVolumeGroupHelper, mMockAudioPolicy, null,
+ mMockAppOpsManager, mMockPermissionEnforcer);
}
/**
diff --git a/services/tests/servicestests/src/com/android/server/audio/DeviceVolumeBehaviorTest.java b/services/tests/servicestests/src/com/android/server/audio/DeviceVolumeBehaviorTest.java
index f5862ac..8dfcc18 100644
--- a/services/tests/servicestests/src/com/android/server/audio/DeviceVolumeBehaviorTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/DeviceVolumeBehaviorTest.java
@@ -50,6 +50,7 @@
private AudioSystemAdapter mAudioSystem;
private SystemServerAdapter mSystemServer;
private SettingsAdapter mSettingsAdapter;
+ private AudioVolumeGroupHelperBase mAudioVolumeGroupHelper;
private TestLooper mTestLooper;
private AudioPolicyFacade mAudioPolicyMock = mock(AudioPolicyFacade.class);
@@ -71,8 +72,10 @@
mAudioSystem = new NoOpAudioSystemAdapter();
mSystemServer = new NoOpSystemServerAdapter();
mSettingsAdapter = new NoOpSettingsAdapter();
+ mAudioVolumeGroupHelper = new AudioVolumeGroupHelperBase();
mAudioService = new AudioService(mContext, mAudioSystem, mSystemServer,
- mSettingsAdapter, mAudioPolicyMock, mTestLooper.getLooper());
+ mSettingsAdapter, mAudioVolumeGroupHelper, mAudioPolicyMock,
+ mTestLooper.getLooper());
mTestLooper.dispatchAll();
}
diff --git a/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java b/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java
index 0eac718..96ac5d2 100644
--- a/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java
+++ b/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java
@@ -137,6 +137,11 @@
}
@Override
+ public int setVolumeIndexForAttributes(AudioAttributes attributes, int index, int device) {
+ return AudioSystem.AUDIO_STATUS_OK;
+ }
+
+ @Override
@NonNull
public ArrayList<AudioDeviceAttributes> getDevicesForAttributes(
@NonNull AudioAttributes attributes, boolean forVolume) {
diff --git a/services/tests/servicestests/src/com/android/server/audio/VolumeHelperTest.java b/services/tests/servicestests/src/com/android/server/audio/VolumeHelperTest.java
new file mode 100644
index 0000000..83bbd0e
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/audio/VolumeHelperTest.java
@@ -0,0 +1,726 @@
+/*
+ * Copyright 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.server.audio;
+
+import static android.media.AudioManager.ADJUST_LOWER;
+import static android.media.AudioManager.ADJUST_MUTE;
+import static android.media.AudioManager.ADJUST_RAISE;
+import static android.media.AudioManager.DEVICE_OUT_BLE_SPEAKER;
+import static android.media.AudioManager.DEVICE_OUT_BLUETOOTH_SCO;
+import static android.media.AudioManager.DEVICE_OUT_SPEAKER;
+import static android.media.AudioManager.DEVICE_OUT_USB_DEVICE;
+import static android.media.AudioManager.DEVICE_VOLUME_BEHAVIOR_UNSET;
+import static android.media.AudioManager.FLAG_ALLOW_RINGER_MODES;
+import static android.media.AudioManager.FLAG_BLUETOOTH_ABS_VOLUME;
+import static android.media.AudioManager.RINGER_MODE_NORMAL;
+import static android.media.AudioManager.RINGER_MODE_VIBRATE;
+import static android.media.AudioManager.STREAM_ACCESSIBILITY;
+import static android.media.AudioManager.STREAM_ALARM;
+import static android.media.AudioManager.STREAM_BLUETOOTH_SCO;
+import static android.media.AudioManager.STREAM_MUSIC;
+import static android.media.AudioManager.STREAM_NOTIFICATION;
+import static android.media.AudioManager.STREAM_RING;
+import static android.media.AudioManager.STREAM_SYSTEM;
+import static android.media.AudioManager.STREAM_VOICE_CALL;
+import static android.view.KeyEvent.ACTION_DOWN;
+import static android.view.KeyEvent.KEYCODE_VOLUME_UP;
+
+import static com.android.media.audio.Flags.FLAG_DISABLE_PRESCALE_ABSOLUTE_VOLUME;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeNotNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.atLeast;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.media.AudioDeviceAttributes;
+import android.media.AudioDeviceInfo;
+import android.media.AudioManager;
+import android.media.AudioSystem;
+import android.media.IDeviceVolumeBehaviorDispatcher;
+import android.media.VolumeInfo;
+import android.media.audiopolicy.AudioVolumeGroup;
+import android.os.Looper;
+import android.os.PermissionEnforcer;
+import android.os.test.TestLooper;
+import android.platform.test.annotations.Presubmit;
+import android.platform.test.annotations.RequiresFlagsDisabled;
+import android.platform.test.annotations.RequiresFlagsEnabled;
+import android.platform.test.flag.junit.CheckFlagsRule;
+import android.platform.test.flag.junit.DeviceFlagsValueProvider;
+import android.util.SparseIntArray;
+import android.view.KeyEvent;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+@Presubmit
+public class VolumeHelperTest {
+ private static final AudioDeviceAttributes DEVICE_SPEAKER_OUT = new AudioDeviceAttributes(
+ AudioDeviceAttributes.ROLE_OUTPUT, AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, "");
+
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ @Rule
+ public final CheckFlagsRule mCheckFlagsRule =
+ DeviceFlagsValueProvider.createCheckFlagsRule();
+
+ private MyAudioService mAudioService;
+
+ private AudioManager mAm;
+
+ private Context mContext;
+
+ private AudioSystemAdapter mSpyAudioSystem;
+ private SettingsAdapter mSettingsAdapter;
+ @Spy
+ private NoOpSystemServerAdapter mSpySystemServer;
+ @Mock
+ private AppOpsManager mMockAppOpsManager;
+ @Mock
+ private PermissionEnforcer mMockPermissionEnforcer;
+ @Mock
+ private AudioVolumeGroupHelperBase mAudioVolumeGroupHelper;
+
+ private final AudioPolicyFacade mFakeAudioPolicy = lookbackAudio -> false;
+
+ private AudioVolumeGroup mAudioMusicVolumeGroup;
+
+ private TestLooper mTestLooper;
+
+ public static final int[] BASIC_VOLUME_BEHAVIORS = {
+ AudioManager.DEVICE_VOLUME_BEHAVIOR_VARIABLE,
+ AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL,
+ AudioManager.DEVICE_VOLUME_BEHAVIOR_FIXED
+ };
+
+ private static class MyAudioService extends AudioService {
+ private final SparseIntArray mStreamDevice = new SparseIntArray();
+
+ MyAudioService(Context context, AudioSystemAdapter audioSystem,
+ SystemServerAdapter systemServer, SettingsAdapter settings,
+ AudioVolumeGroupHelperBase audioVolumeGroupHelper, AudioPolicyFacade audioPolicy,
+ @Nullable Looper looper, AppOpsManager appOps,
+ @NonNull PermissionEnforcer enforcer) {
+ super(context, audioSystem, systemServer, settings, audioVolumeGroupHelper,
+ audioPolicy, looper, appOps, enforcer);
+ }
+
+ public void setDeviceForStream(int stream, int device) {
+ mStreamDevice.put(stream, device);
+ }
+
+ @Override
+ public int getDeviceForStream(int stream) {
+ if (mStreamDevice.indexOfKey(stream) < 0) {
+ return DEVICE_OUT_SPEAKER;
+ }
+ return mStreamDevice.get(stream);
+ }
+ }
+
+ private static class TestDeviceVolumeBehaviorDispatcherStub
+ extends IDeviceVolumeBehaviorDispatcher.Stub {
+
+ private AudioDeviceAttributes mDevice;
+ private int mVolumeBehavior;
+ private int mTimesCalled;
+
+ @Override
+ public void dispatchDeviceVolumeBehaviorChanged(@NonNull AudioDeviceAttributes device,
+ @AudioManager.DeviceVolumeBehavior int volumeBehavior) {
+ mDevice = device;
+ mVolumeBehavior = volumeBehavior;
+ mTimesCalled++;
+ }
+
+ public void reset() {
+ mTimesCalled = 0;
+ mVolumeBehavior = DEVICE_VOLUME_BEHAVIOR_UNSET;
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ mTestLooper = new TestLooper();
+
+ mSpyAudioSystem = spy(new NoOpAudioSystemAdapter());
+ mSettingsAdapter = new NoOpSettingsAdapter();
+
+ mAudioMusicVolumeGroup = getStreamTypeVolumeGroup(STREAM_MUSIC);
+ if (mAudioMusicVolumeGroup != null) {
+ when(mAudioVolumeGroupHelper.getAudioVolumeGroups()).thenReturn(
+ List.of(mAudioMusicVolumeGroup));
+ }
+
+ mAm = mContext.getSystemService(AudioManager.class);
+
+ mAudioService = new MyAudioService(mContext, mSpyAudioSystem, mSpySystemServer,
+ mSettingsAdapter, mAudioVolumeGroupHelper, mFakeAudioPolicy,
+ mTestLooper.getLooper(), mMockAppOpsManager, mMockPermissionEnforcer);
+
+ mTestLooper.dispatchAll();
+ prepareAudioServiceState();
+ mTestLooper.dispatchAll();
+
+ reset(mSpyAudioSystem);
+
+ InstrumentationRegistry.getInstrumentation().getUiAutomation()
+ .adoptShellPermissionIdentity(Manifest.permission.MODIFY_AUDIO_SETTINGS_PRIVILEGED,
+ Manifest.permission.MODIFY_AUDIO_ROUTING,
+ Manifest.permission.MODIFY_PHONE_STATE,
+ android.Manifest.permission.STATUS_BAR_SERVICE);
+ }
+
+ private void prepareAudioServiceState() throws Exception {
+ int[] usedStreamTypes =
+ {STREAM_MUSIC, STREAM_NOTIFICATION, STREAM_RING, STREAM_ALARM, STREAM_SYSTEM,
+ STREAM_VOICE_CALL, STREAM_ACCESSIBILITY};
+ for (int streamType : usedStreamTypes) {
+ final int streamVolume = (mAm.getStreamMinVolume(streamType) + mAm.getStreamMaxVolume(
+ streamType)) / 2;
+
+ mAudioService.setStreamVolume(streamType, streamVolume, /*flags=*/0,
+ mContext.getOpPackageName());
+ }
+
+ mAudioService.setRingerModeInternal(RINGER_MODE_NORMAL, mContext.getOpPackageName());
+ mAudioService.setRingerModeExternal(RINGER_MODE_NORMAL, mContext.getOpPackageName());
+ }
+
+ private AudioVolumeGroup getStreamTypeVolumeGroup(int streamType) {
+ // get the volume group from the AudioManager to pass permission checks
+ // when requesting from real service
+ final List<AudioVolumeGroup> audioVolumeGroups = AudioManager.getAudioVolumeGroups();
+ for (AudioVolumeGroup vg : audioVolumeGroups) {
+ for (int stream : vg.getLegacyStreamTypes()) {
+ if (stream == streamType) {
+ return vg;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ @After
+ public void tearDown() {
+ InstrumentationRegistry.getInstrumentation().getUiAutomation()
+ .dropShellPermissionIdentity();
+ }
+
+ // --------------- Volume Stream APIs ---------------
+ @Test
+ public void setStreamVolume_callsASSetStreamVolumeIndex() throws Exception {
+ int newIndex = circularNoMinMaxIncrementVolume(STREAM_MUSIC);
+
+ mAudioService.setDeviceForStream(STREAM_MUSIC, DEVICE_OUT_USB_DEVICE);
+ mAudioService.setStreamVolume(STREAM_MUSIC, newIndex, /*flags=*/0,
+ mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+
+ verify(mSpyAudioSystem).setStreamVolumeIndexAS(
+ eq(STREAM_MUSIC), eq(newIndex), eq(DEVICE_OUT_USB_DEVICE));
+ }
+
+ @Test
+ public void setStreamRingVolume0_setsRingerModeVibrate() throws Exception {
+ mAudioService.setStreamVolume(STREAM_RING, 0, /*flags=*/0,
+ mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+
+ assertEquals(RINGER_MODE_VIBRATE, mAudioService.getRingerModeExternal());
+ }
+
+ @Test
+ public void adjustStreamVolume_callsASSetStreamVolumeIndex() throws Exception {
+ mAudioService.setDeviceForStream(STREAM_MUSIC, DEVICE_OUT_USB_DEVICE);
+ mAudioService.adjustStreamVolume(STREAM_MUSIC, ADJUST_LOWER, /*flags=*/0,
+ mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+
+ verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
+ eq(STREAM_MUSIC), anyInt(), eq(DEVICE_OUT_USB_DEVICE));
+ }
+
+ @Test
+ public void handleVolumeKey_callsASSetStreamVolumeIndex() throws Exception {
+ final KeyEvent keyEvent = new KeyEvent(ACTION_DOWN, KEYCODE_VOLUME_UP);
+
+ mAudioService.setDeviceForStream(STREAM_MUSIC, DEVICE_OUT_USB_DEVICE);
+ mAudioService.handleVolumeKey(keyEvent, /*isOnTv=*/false, mContext.getOpPackageName(),
+ "adjustSuggestedStreamVolume_callsAudioSystemSetStreamVolumeIndex");
+ mTestLooper.dispatchAll();
+
+ verify(mSpyAudioSystem).setStreamVolumeIndexAS(
+ eq(STREAM_MUSIC), anyInt(), eq(DEVICE_OUT_USB_DEVICE));
+ }
+
+ // --------------- Volume Group APIs ---------------
+
+ @Test
+ public void setVolumeGroupVolumeIndex_callsASSetVolumeIndexForAttributes() throws Exception {
+ assumeNotNull(mAudioMusicVolumeGroup);
+
+ mAudioService.setDeviceForStream(STREAM_MUSIC, DEVICE_OUT_USB_DEVICE);
+ mAudioService.setVolumeGroupVolumeIndex(mAudioMusicVolumeGroup.getId(),
+ circularNoMinMaxIncrementVolume(STREAM_MUSIC), /*flags=*/0,
+ mContext.getOpPackageName(), /*attributionTag*/null);
+ mTestLooper.dispatchAll();
+
+ verify(mSpyAudioSystem).setVolumeIndexForAttributes(
+ any(), anyInt(), eq(DEVICE_OUT_USB_DEVICE));
+ }
+
+ @Test
+ public void adjustVolumeGroupVolume_callsASSetVolumeIndexForAttributes() throws Exception {
+ assumeNotNull(mAudioMusicVolumeGroup);
+
+ mAudioService.setDeviceForStream(STREAM_MUSIC, DEVICE_OUT_USB_DEVICE);
+ mAudioService.adjustVolumeGroupVolume(mAudioMusicVolumeGroup.getId(),
+ ADJUST_LOWER, /*flags=*/0, mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+
+ verify(mSpyAudioSystem).setVolumeIndexForAttributes(
+ any(), anyInt(), eq(DEVICE_OUT_USB_DEVICE));
+ }
+
+ @Test
+ public void check_getVolumeGroupVolumeIndex() throws Exception {
+ assumeNotNull(mAudioMusicVolumeGroup);
+
+ int newIndex = circularNoMinMaxIncrementVolume(STREAM_MUSIC);
+
+ mAudioService.setVolumeGroupVolumeIndex(mAudioMusicVolumeGroup.getId(),
+ newIndex, /*flags=*/0, mContext.getOpPackageName(), /*attributionTag*/null);
+ mTestLooper.dispatchAll();
+
+ assertEquals(mAudioService.getVolumeGroupVolumeIndex(mAudioMusicVolumeGroup.getId()),
+ newIndex);
+ assertEquals(mAudioService.getStreamVolume(STREAM_MUSIC),
+ newIndex);
+ }
+
+ @Test
+ public void check_getVolumeGroupMaxVolumeIndex() throws Exception {
+ assumeNotNull(mAudioMusicVolumeGroup);
+
+ assertEquals(mAudioService.getVolumeGroupMaxVolumeIndex(mAudioMusicVolumeGroup.getId()),
+ mAudioService.getStreamMaxVolume(STREAM_MUSIC));
+ }
+
+ @Test
+ public void check_getVolumeGroupMinVolumeIndex() throws Exception {
+ assumeNotNull(mAudioMusicVolumeGroup);
+
+ assertEquals(mAudioService.getVolumeGroupMinVolumeIndex(mAudioMusicVolumeGroup.getId()),
+ mAudioService.getStreamMinVolume(STREAM_MUSIC));
+ }
+
+ @Test
+ public void check_getLastAudibleVolumeForVolumeGroup() throws Exception {
+ assumeNotNull(mAudioMusicVolumeGroup);
+
+ assertEquals(
+ mAudioService.getLastAudibleVolumeForVolumeGroup(mAudioMusicVolumeGroup.getId()),
+ mAudioService.getLastAudibleStreamVolume(STREAM_MUSIC));
+ }
+
+ @Test
+ public void check_isVolumeGroupMuted() throws Exception {
+ assumeNotNull(mAudioMusicVolumeGroup);
+
+ assertEquals(mAudioService.isVolumeGroupMuted(mAudioMusicVolumeGroup.getId()),
+ mAudioService.isStreamMute(STREAM_MUSIC));
+ }
+
+ // ------------------------- Mute Tests ------------------------
+
+ @Test
+ public void check_setMasterMute() {
+ mAudioService.setMasterMute(true, /*flags=*/0, mContext.getOpPackageName(),
+ mContext.getUserId(), /*attributionTag*/"");
+
+ assertTrue(mAudioService.isMasterMute());
+ }
+
+ @Test
+ public void check_isStreamAffectedByMute() {
+ assertFalse(mAudioService.isStreamAffectedByMute(STREAM_VOICE_CALL));
+ }
+
+ // --------------------- Volume Flag Check --------------------
+
+ @Test
+ public void flagAbsVolume_onBtDevice_changesVolume() throws Exception {
+ mAudioService.setDeviceForStream(STREAM_NOTIFICATION, DEVICE_OUT_BLE_SPEAKER);
+
+ int newIndex = circularNoMinMaxIncrementVolume(STREAM_NOTIFICATION);
+ mAudioService.setStreamVolume(STREAM_NOTIFICATION, newIndex, FLAG_BLUETOOTH_ABS_VOLUME,
+ mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+ verify(mSpyAudioSystem).setStreamVolumeIndexAS(
+ eq(STREAM_NOTIFICATION), anyInt(), eq(DEVICE_OUT_BLE_SPEAKER));
+
+ reset(mSpyAudioSystem);
+ mAudioService.adjustStreamVolume(STREAM_NOTIFICATION, ADJUST_LOWER,
+ FLAG_BLUETOOTH_ABS_VOLUME, mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+ verify(mSpyAudioSystem).setStreamVolumeIndexAS(
+ eq(STREAM_NOTIFICATION), anyInt(), eq(DEVICE_OUT_BLE_SPEAKER));
+ }
+
+ @Test
+ public void flagAbsVolume_onNonBtDevice_noVolumeChange() throws Exception {
+ mAudioService.setDeviceForStream(STREAM_NOTIFICATION, DEVICE_OUT_SPEAKER);
+
+ int newIndex = circularNoMinMaxIncrementVolume(STREAM_NOTIFICATION);
+ mAudioService.setStreamVolume(STREAM_NOTIFICATION, newIndex, FLAG_BLUETOOTH_ABS_VOLUME,
+ mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+ verify(mSpyAudioSystem, times(0)).setStreamVolumeIndexAS(
+ eq(STREAM_NOTIFICATION), eq(newIndex), eq(DEVICE_OUT_BLE_SPEAKER));
+
+ mAudioService.adjustStreamVolume(STREAM_NOTIFICATION, ADJUST_LOWER,
+ FLAG_BLUETOOTH_ABS_VOLUME, mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+ verify(mSpyAudioSystem, times(0)).setStreamVolumeIndexAS(
+ eq(STREAM_NOTIFICATION), anyInt(), eq(DEVICE_OUT_BLE_SPEAKER));
+ }
+
+ @Test
+ public void flagAllowRingerModes_onSystemStreams_changesMode() throws Exception {
+ mAudioService.setStreamVolume(STREAM_SYSTEM,
+ mAudioService.getStreamMinVolume(STREAM_SYSTEM), /*flags=*/0,
+ mContext.getOpPackageName());
+ mAudioService.setRingerModeInternal(RINGER_MODE_VIBRATE, mContext.getOpPackageName());
+
+ mAudioService.adjustStreamVolume(STREAM_SYSTEM, ADJUST_RAISE, FLAG_ALLOW_RINGER_MODES,
+ mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+
+ assertNotEquals(mAudioService.getRingerModeInternal(), RINGER_MODE_VIBRATE);
+ }
+
+ @Test
+ public void flagAllowRingerModesAbsent_onNonSystemStreams_noModeChange() throws Exception {
+ mAudioService.setStreamVolume(STREAM_MUSIC,
+ mAudioService.getStreamMinVolume(STREAM_MUSIC), /*flags=*/0,
+ mContext.getOpPackageName());
+ mAudioService.setRingerModeInternal(RINGER_MODE_VIBRATE, mContext.getOpPackageName());
+
+ mAudioService.adjustStreamVolume(STREAM_MUSIC, ADJUST_RAISE, /*flags=*/0,
+ mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+
+ assertEquals(mAudioService.getRingerModeInternal(), RINGER_MODE_VIBRATE);
+ }
+
+ // --------------------- Permission tests ---------------------
+
+ @Test
+ public void appOpsIgnore_noVolumeChange() throws Exception {
+ reset(mMockAppOpsManager);
+ when(mMockAppOpsManager.noteOp(anyInt(), anyInt(), anyString(), isNull(), isNull()))
+ .thenReturn(AppOpsManager.MODE_IGNORED);
+
+ mAudioService.adjustStreamVolume(STREAM_MUSIC, ADJUST_LOWER, /*flags=*/0,
+ mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+
+ verify(mSpyAudioSystem, times(0)).setStreamVolumeIndexAS(
+ eq(STREAM_MUSIC), anyInt(), anyInt());
+ }
+
+ @Test
+ public void modifyPhoneStateAbsent_noMuteVoiceCallScoAllowed() throws Exception {
+ InstrumentationRegistry.getInstrumentation().getUiAutomation()
+ .dropShellPermissionIdentity();
+
+ mAudioService.setDeviceForStream(STREAM_VOICE_CALL, DEVICE_OUT_USB_DEVICE);
+ mAudioService.adjustStreamVolume(STREAM_VOICE_CALL, ADJUST_MUTE, /*flags=*/0,
+ mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+
+ verify(mSpyAudioSystem, times(0)).setStreamVolumeIndexAS(
+ eq(STREAM_VOICE_CALL), anyInt(), eq(DEVICE_OUT_USB_DEVICE));
+
+ mAudioService.setDeviceForStream(STREAM_BLUETOOTH_SCO, DEVICE_OUT_BLUETOOTH_SCO);
+ mAudioService.adjustStreamVolume(STREAM_BLUETOOTH_SCO, ADJUST_MUTE, /*flags=*/0,
+ mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+
+ verify(mSpyAudioSystem, times(0)).setStreamVolumeIndexAS(
+ eq(STREAM_BLUETOOTH_SCO), anyInt(), eq(DEVICE_OUT_USB_DEVICE));
+ }
+
+ // ----------------- AudioDeviceVolumeManager -----------------
+ @Test
+ public void setDeviceVolume_checkIndex() {
+ final int minIndex = mAm.getStreamMinVolume(STREAM_MUSIC);
+ final int maxIndex = mAm.getStreamMaxVolume(STREAM_MUSIC);
+ final int midIndex = (minIndex + maxIndex) / 2;
+ final VolumeInfo volMedia = new VolumeInfo.Builder(STREAM_MUSIC)
+ .setMinVolumeIndex(minIndex)
+ .setMaxVolumeIndex(maxIndex)
+ .build();
+ final VolumeInfo volMin = new VolumeInfo.Builder(volMedia).setVolumeIndex(minIndex).build();
+ final VolumeInfo volMid = new VolumeInfo.Builder(volMedia).setVolumeIndex(midIndex).build();
+ final AudioDeviceAttributes usbDevice = new AudioDeviceAttributes(
+ /*native type*/ AudioSystem.DEVICE_OUT_USB_DEVICE, /*address*/ "bla");
+
+ mAudioService.setDeviceVolume(volMin, usbDevice, mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+
+ assertEquals(mAudioService.getDeviceVolume(volMin, usbDevice,
+ mContext.getOpPackageName()), volMin);
+ verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
+ STREAM_MUSIC, minIndex, AudioSystem.DEVICE_OUT_USB_DEVICE);
+
+ mAudioService.setDeviceVolume(volMid, usbDevice, mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+ assertEquals(mAudioService.getDeviceVolume(volMid, usbDevice,
+ mContext.getOpPackageName()), volMid);
+ verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
+ STREAM_MUSIC, midIndex, AudioSystem.DEVICE_OUT_USB_DEVICE);
+ }
+
+ @Test
+ @RequiresFlagsDisabled(FLAG_DISABLE_PRESCALE_ABSOLUTE_VOLUME)
+ public void configurablePreScaleAbsoluteVolume_checkIndex() throws Exception {
+ final int minIndex = mAm.getStreamMinVolume(STREAM_MUSIC);
+ final int maxIndex = mAm.getStreamMaxVolume(STREAM_MUSIC);
+ final VolumeInfo volMedia = new VolumeInfo.Builder(STREAM_MUSIC)
+ .setMinVolumeIndex(minIndex)
+ .setMaxVolumeIndex(maxIndex)
+ .build();
+ final AudioDeviceAttributes bleDevice = new AudioDeviceAttributes(
+ /*native type*/ AudioSystem.DEVICE_OUT_BLE_HEADSET, /*address*/ "fake_ble");
+ final int maxPreScaleIndex = 3;
+ final float[] preScale = new float[maxPreScaleIndex];
+ preScale[0] = mContext.getResources().getFraction(
+ com.android.internal.R.fraction.config_prescaleAbsoluteVolume_index1,
+ 1, 1);
+ preScale[1] = mContext.getResources().getFraction(
+ com.android.internal.R.fraction.config_prescaleAbsoluteVolume_index2,
+ 1, 1);
+ preScale[2] = mContext.getResources().getFraction(
+ com.android.internal.R.fraction.config_prescaleAbsoluteVolume_index3,
+ 1, 1);
+
+ for (int i = 0; i < maxPreScaleIndex; i++) {
+ final int targetIndex = (int) (preScale[i] * maxIndex);
+ final VolumeInfo volCur = new VolumeInfo.Builder(volMedia)
+ .setVolumeIndex(i + 1).build();
+ // Adjust stream volume with FLAG_ABSOLUTE_VOLUME set (index:1~3)
+ mAudioService.setDeviceVolume(volCur, bleDevice, mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+
+ assertEquals(
+ mAudioService.getDeviceVolume(volCur, bleDevice, mContext.getOpPackageName()),
+ volCur);
+ // Stream volume changes
+ verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
+ STREAM_MUSIC, targetIndex,
+ AudioSystem.DEVICE_OUT_BLE_HEADSET);
+ }
+
+ // Adjust stream volume with FLAG_ABSOLUTE_VOLUME set (index:4)
+ final VolumeInfo volIndex4 = new VolumeInfo.Builder(volMedia)
+ .setVolumeIndex(4).build();
+ mAudioService.setDeviceVolume(volIndex4, bleDevice, mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+
+ assertEquals(
+ mAudioService.getDeviceVolume(volIndex4, bleDevice, mContext.getOpPackageName()),
+ volIndex4);
+ verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
+ STREAM_MUSIC, maxIndex,
+ AudioSystem.DEVICE_OUT_BLE_HEADSET);
+ }
+
+ @Test
+ @RequiresFlagsEnabled(FLAG_DISABLE_PRESCALE_ABSOLUTE_VOLUME)
+ public void disablePreScaleAbsoluteVolume_checkIndex() throws Exception {
+ final int minIndex = mAm.getStreamMinVolume(STREAM_MUSIC);
+ final int maxIndex = mAm.getStreamMaxVolume(STREAM_MUSIC);
+ final VolumeInfo volMedia = new VolumeInfo.Builder(STREAM_MUSIC)
+ .setMinVolumeIndex(minIndex)
+ .setMaxVolumeIndex(maxIndex)
+ .build();
+ final AudioDeviceAttributes bleDevice = new AudioDeviceAttributes(
+ /*native type*/ AudioSystem.DEVICE_OUT_BLE_HEADSET, /*address*/ "bla");
+ final int maxPreScaleIndex = 3;
+
+ for (int i = 0; i < maxPreScaleIndex; i++) {
+ final VolumeInfo volCur = new VolumeInfo.Builder(volMedia)
+ .setVolumeIndex(i + 1).build();
+ // Adjust stream volume with FLAG_ABSOLUTE_VOLUME set (index:1~3)
+ mAudioService.setDeviceVolume(volCur, bleDevice, mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+
+ // Stream volume changes
+ verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
+ STREAM_MUSIC, maxIndex,
+ AudioSystem.DEVICE_OUT_BLE_HEADSET);
+ }
+
+ // Adjust stream volume with FLAG_ABSOLUTE_VOLUME set (index:4)
+ final VolumeInfo volIndex4 = new VolumeInfo.Builder(volMedia)
+ .setVolumeIndex(4).build();
+ mAudioService.setDeviceVolume(volIndex4, bleDevice, mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+
+ verify(mSpyAudioSystem, atLeast(1)).setStreamVolumeIndexAS(
+ STREAM_MUSIC, maxIndex,
+ AudioSystem.DEVICE_OUT_BLE_HEADSET);
+ }
+
+ // ---------------- DeviceVolumeBehaviorTest ----------------
+ @Test
+ public void setDeviceVolumeBehavior_changesDeviceVolumeBehavior() {
+ mAudioService.setDeviceVolumeBehavior(DEVICE_SPEAKER_OUT,
+ AudioManager.DEVICE_VOLUME_BEHAVIOR_FIXED, mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+
+ for (int behavior : BASIC_VOLUME_BEHAVIORS) {
+ mAudioService.setDeviceVolumeBehavior(DEVICE_SPEAKER_OUT, behavior,
+ mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+
+ int actualBehavior = mAudioService.getDeviceVolumeBehavior(DEVICE_SPEAKER_OUT);
+
+ assertWithMessage("Expected volume behavior to be " + behavior
+ + " but was instead " + actualBehavior)
+ .that(actualBehavior).isEqualTo(behavior);
+ }
+ }
+
+ @Test
+ public void setToNewBehavior_triggersDeviceVolumeBehaviorDispatcher() {
+ TestDeviceVolumeBehaviorDispatcherStub dispatcher =
+ new TestDeviceVolumeBehaviorDispatcherStub();
+ mAudioService.registerDeviceVolumeBehaviorDispatcher(true, dispatcher);
+
+ mAudioService.setDeviceVolumeBehavior(DEVICE_SPEAKER_OUT,
+ AudioManager.DEVICE_VOLUME_BEHAVIOR_FIXED, mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+
+ for (int behavior : BASIC_VOLUME_BEHAVIORS) {
+ dispatcher.reset();
+ mAudioService.setDeviceVolumeBehavior(DEVICE_SPEAKER_OUT, behavior,
+ mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+
+ assertThat(dispatcher.mTimesCalled).isEqualTo(1);
+ assertThat(dispatcher.mDevice).isEqualTo(DEVICE_SPEAKER_OUT);
+ assertWithMessage("Expected dispatched volume behavior to be " + behavior
+ + " but was instead " + dispatcher.mVolumeBehavior)
+ .that(dispatcher.mVolumeBehavior).isEqualTo(behavior);
+ }
+ }
+
+ @Test
+ public void setToSameBehavior_doesNotTriggerDeviceVolumeBehaviorDispatcher() {
+ mAudioService.setDeviceVolumeBehavior(DEVICE_SPEAKER_OUT,
+ AudioManager.DEVICE_VOLUME_BEHAVIOR_FIXED, mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+
+ TestDeviceVolumeBehaviorDispatcherStub dispatcher =
+ new TestDeviceVolumeBehaviorDispatcherStub();
+ mAudioService.registerDeviceVolumeBehaviorDispatcher(true, dispatcher);
+
+ mAudioService.setDeviceVolumeBehavior(DEVICE_SPEAKER_OUT,
+ AudioManager.DEVICE_VOLUME_BEHAVIOR_FIXED, mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+ assertThat(dispatcher.mTimesCalled).isEqualTo(0);
+ }
+
+ @Test
+ public void unregisterDeviceVolumeBehaviorDispatcher_noLongerTriggered() {
+ mAudioService.setDeviceVolumeBehavior(DEVICE_SPEAKER_OUT,
+ AudioManager.DEVICE_VOLUME_BEHAVIOR_FIXED, mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+
+ TestDeviceVolumeBehaviorDispatcherStub dispatcher =
+ new TestDeviceVolumeBehaviorDispatcherStub();
+ mAudioService.registerDeviceVolumeBehaviorDispatcher(true, dispatcher);
+ mAudioService.registerDeviceVolumeBehaviorDispatcher(false, dispatcher);
+
+ mAudioService.setDeviceVolumeBehavior(DEVICE_SPEAKER_OUT,
+ AudioManager.DEVICE_VOLUME_BEHAVIOR_FULL, mContext.getOpPackageName());
+ mTestLooper.dispatchAll();
+ assertThat(dispatcher.mTimesCalled).isEqualTo(0);
+ }
+
+ @Test
+ public void setDeviceVolumeBehavior_checkIsVolumeFixed() throws Exception {
+ when(mSpyAudioSystem.getDevicesForAttributes(any(), anyBoolean())).thenReturn(
+ new ArrayList<>(List.of(DEVICE_SPEAKER_OUT)));
+
+ mAudioService.setDeviceVolumeBehavior(DEVICE_SPEAKER_OUT,
+ AudioManager.DEVICE_VOLUME_BEHAVIOR_FIXED, mContext.getOpPackageName());
+
+ assertTrue(mAudioService.isVolumeFixed());
+ }
+
+ private int circularNoMinMaxIncrementVolume(int streamType) throws Exception {
+ final int streamMinVolume = mAm.getStreamMinVolume(streamType) + 1;
+ final int streamMaxVolume = mAm.getStreamMaxVolume(streamType) - 1;
+
+ int streamVolume = mAudioService.getStreamVolume(streamType);
+ if (streamVolume + 1 > streamMaxVolume) {
+ return streamMinVolume;
+ }
+ return streamVolume + 1;
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
index 071db68..f0dc5f0 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/AuthServiceTest.java
@@ -60,8 +60,10 @@
import android.hardware.fingerprint.IFingerprintService;
import android.hardware.iris.IIrisService;
import android.os.Binder;
+import android.os.Handler;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
import android.platform.test.annotations.RequiresFlagsDisabled;
import android.platform.test.annotations.RequiresFlagsEnabled;
@@ -127,6 +129,8 @@
AppOpsManager mAppOpsManager;
@Mock
private VirtualDeviceManagerInternal mVdmInternal;
+ @Mock
+ private BiometricHandlerProvider mBiometricHandlerProvider;
@Captor
private ArgumentCaptor<List<FingerprintSensorPropertiesInternal>> mFingerprintPropsCaptor;
@Captor
@@ -136,6 +140,9 @@
@Captor
private ArgumentCaptor<List<FaceSensorPropertiesInternal>> mFacePropsCaptor;
+ private final TestLooper mFingerprintLooper = new TestLooper();
+ private final TestLooper mFaceLooper = new TestLooper();
+
@Before
public void setUp() {
// Placeholder test config
@@ -167,6 +174,11 @@
when(mInjector.getIrisService()).thenReturn(mIrisService);
when(mInjector.getAppOps(any())).thenReturn(mAppOpsManager);
when(mInjector.isHidlDisabled(any())).thenReturn(false);
+ when(mInjector.getBiometricHandlerProvider()).thenReturn(mBiometricHandlerProvider);
+ when(mBiometricHandlerProvider.getFingerprintHandler()).thenReturn(
+ new Handler(mFingerprintLooper.getLooper()));
+ when(mBiometricHandlerProvider.getFaceHandler()).thenReturn(
+ new Handler(mFaceLooper.getLooper()));
setInternalAndTestBiometricPermissions(mContext, false /* hasPermission */);
}
@@ -250,6 +262,9 @@
mAuthService = new AuthService(mContext, mInjector);
mAuthService.onStart();
+ mFingerprintLooper.dispatchAll();
+ mFaceLooper.dispatchAll();
+
verify(mFingerprintService).registerAuthenticatorsLegacy(
mFingerprintSensorConfigurationsCaptor.capture());
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java
index 132b621..ec3e97b 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/GenericWindowPolicyControllerTest.java
@@ -38,6 +38,7 @@
import android.app.WindowConfiguration;
import android.companion.virtual.IVirtualDeviceIntentInterceptor;
import android.companion.virtual.VirtualDeviceManager;
+import android.content.AttributionSource;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -712,6 +713,7 @@
return new GenericWindowPolicyController(
0,
0,
+ AttributionSource.myAttributionSource(),
/* allowedUsers= */ new ArraySet<>(getCurrentUserId()),
/* activityLaunchAllowedByDefault= */ true,
/* activityPolicyExemptions= */ new ArraySet<>(),
@@ -732,6 +734,7 @@
return new GenericWindowPolicyController(
0,
0,
+ AttributionSource.myAttributionSource(),
/* allowedUsers= */ new ArraySet<>(),
/* activityLaunchAllowedByDefault= */ true,
/* activityPolicyExemptions= */ new ArraySet<>(),
@@ -753,6 +756,7 @@
return new GenericWindowPolicyController(
0,
0,
+ AttributionSource.myAttributionSource(),
/* allowedUsers= */ new ArraySet<>(getCurrentUserId()),
/* activityLaunchAllowedByDefault= */ true,
/* activityPolicyExemptions= */ new ArraySet<>(),
@@ -774,6 +778,7 @@
return new GenericWindowPolicyController(
0,
0,
+ AttributionSource.myAttributionSource(),
/* allowedUsers= */ new ArraySet<>(getCurrentUserId()),
/* activityLaunchAllowedByDefault= */ true,
/* activityPolicyExemptions= */ Collections.singleton(blockedComponent),
@@ -795,6 +800,7 @@
return new GenericWindowPolicyController(
0,
0,
+ AttributionSource.myAttributionSource(),
/* allowedUsers= */ new ArraySet<>(getCurrentUserId()),
/* activityLaunchAllowedByDefault= */ false,
/* activityPolicyExemptions= */ Collections.singleton(allowedComponent),
@@ -816,6 +822,7 @@
return new GenericWindowPolicyController(
0,
0,
+ AttributionSource.myAttributionSource(),
/* allowedUsers= */ new ArraySet<>(getCurrentUserId()),
/* activityLaunchAllowedByDefault= */ true,
/* activityPolicyExemptions= */ new ArraySet<>(),
@@ -837,6 +844,7 @@
return new GenericWindowPolicyController(
0,
0,
+ AttributionSource.myAttributionSource(),
/* allowedUsers= */ new ArraySet<>(getCurrentUserId()),
/* activityLaunchAllowedByDefault= */ true,
/* activityPolicyExemptions= */ new ArraySet<>(),
@@ -858,6 +866,7 @@
return new GenericWindowPolicyController(
0,
0,
+ AttributionSource.myAttributionSource(),
/* allowedUsers= */ new ArraySet<>(getCurrentUserId()),
/* activityLaunchAllowedByDefault= */ true,
/* activityPolicyExemptions= */ new ArraySet<>(),
@@ -880,6 +889,7 @@
return new GenericWindowPolicyController(
0,
0,
+ AttributionSource.myAttributionSource(),
/* allowedUsers= */ new ArraySet<>(getCurrentUserId()),
/* activityLaunchAllowedByDefault= */ true,
/* activityPolicyExemptions= */ new ArraySet<>(),
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java
index 07e6ab2..fd880dd 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java
@@ -25,6 +25,7 @@
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.verify;
+import android.content.AttributionSource;
import android.hardware.display.DisplayManagerInternal;
import android.hardware.input.IInputManager;
import android.hardware.input.InputManagerGlobal;
@@ -95,6 +96,7 @@
mInputController = new InputController(mNativeWrapperMock,
new Handler(TestableLooper.get(this).getLooper()),
InstrumentationRegistry.getTargetContext().getSystemService(WindowManager.class),
+ AttributionSource.myAttributionSource(),
threadVerifier);
}
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/SensorControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/SensorControllerTest.java
index faece4f..67fc564 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/SensorControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/SensorControllerTest.java
@@ -32,6 +32,7 @@
import android.companion.virtual.sensor.VirtualSensor;
import android.companion.virtual.sensor.VirtualSensorConfig;
import android.companion.virtual.sensor.VirtualSensorEvent;
+import android.content.AttributionSource;
import android.hardware.Sensor;
import android.os.Binder;
import android.os.IBinder;
@@ -96,6 +97,7 @@
Throwable thrown = assertThrows(
RuntimeException.class,
() -> new SensorController(mVirtualDevice, VIRTUAL_DEVICE_ID,
+ AttributionSource.myAttributionSource(),
mVirtualSensorCallback, List.of(mVirtualSensorConfig)));
assertThat(thrown.getCause().getMessage())
@@ -168,6 +170,7 @@
doReturn(VIRTUAL_DEVICE_ID).when(mVirtualDevice).getDeviceId();
SensorController sensorController = new SensorController(mVirtualDevice, VIRTUAL_DEVICE_ID,
+ AttributionSource.myAttributionSource(),
mVirtualSensorCallback, List.of(mVirtualSensorConfig));
List<VirtualSensor> sensors = sensorController.getSensorList();
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index 157e893..5e0806d 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -389,7 +389,8 @@
final InputController.DeviceCreationThreadVerifier threadVerifier = () -> true;
mInputController = new InputController(mNativeWrapperMock,
new Handler(TestableLooper.get(this).getLooper()),
- mContext.getSystemService(WindowManager.class), threadVerifier);
+ mContext.getSystemService(WindowManager.class),
+ AttributionSource.myAttributionSource(), threadVerifier);
mCameraAccessController =
new CameraAccessController(mContext, mLocalService, mCameraAccessBlockedCallback);
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java
index ef5270e..52f28b9 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/audio/VirtualAudioControllerTest.java
@@ -27,6 +27,7 @@
import android.companion.virtual.audio.IAudioConfigChangedCallback;
import android.companion.virtual.audio.IAudioRoutingCallback;
+import android.content.AttributionSource;
import android.content.Context;
import android.content.ContextWrapper;
import android.media.AudioPlaybackConfiguration;
@@ -72,11 +73,13 @@
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = Mockito.spy(new ContextWrapper(InstrumentationRegistry.getTargetContext()));
- mVirtualAudioController = new VirtualAudioController(mContext);
+ mVirtualAudioController = new VirtualAudioController(mContext,
+ AttributionSource.myAttributionSource());
mGenericWindowPolicyController =
new GenericWindowPolicyController(
FLAG_SECURE,
SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS,
+ AttributionSource.myAttributionSource(),
/* allowedUsers= */ new ArraySet<>(),
/* activityLaunchAllowedByDefault= */ true,
/* activityPolicyExemptions= */ new ArraySet<>(),
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java
index 81981e6..9ca1df0 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/camera/VirtualCameraControllerTest.java
@@ -37,6 +37,7 @@
import android.companion.virtual.camera.VirtualCameraConfig;
import android.companion.virtualcamera.IVirtualCameraService;
import android.companion.virtualcamera.VirtualCameraConfiguration;
+import android.content.AttributionSource;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.Looper;
@@ -104,7 +105,7 @@
public void registerCamera_registersCamera(int lensFacing) throws Exception {
mVirtualCameraController.registerCamera(createVirtualCameraConfig(
CAMERA_WIDTH_1, CAMERA_HEIGHT_1, CAMERA_FORMAT_1, CAMERA_MAX_FPS_1, CAMERA_NAME_1,
- CAMERA_SENSOR_ORIENTATION_1, lensFacing));
+ CAMERA_SENSOR_ORIENTATION_1, lensFacing), AttributionSource.myAttributionSource());
ArgumentCaptor<VirtualCameraConfiguration> configurationCaptor =
ArgumentCaptor.forClass(VirtualCameraConfiguration.class);
@@ -121,7 +122,7 @@
VirtualCameraConfig config = createVirtualCameraConfig(
CAMERA_WIDTH_1, CAMERA_HEIGHT_1, CAMERA_FORMAT_1, CAMERA_MAX_FPS_1, CAMERA_NAME_1,
CAMERA_SENSOR_ORIENTATION_1, CAMERA_LENS_FACING_1);
- mVirtualCameraController.registerCamera(config);
+ mVirtualCameraController.registerCamera(config, AttributionSource.myAttributionSource());
mVirtualCameraController.unregisterCamera(config);
@@ -131,11 +132,15 @@
@Test
public void close_unregistersAllCameras() throws Exception {
mVirtualCameraController.registerCamera(createVirtualCameraConfig(
- CAMERA_WIDTH_1, CAMERA_HEIGHT_1, CAMERA_FORMAT_1, CAMERA_MAX_FPS_1, CAMERA_NAME_1,
- CAMERA_SENSOR_ORIENTATION_1, CAMERA_LENS_FACING_1));
+ CAMERA_WIDTH_1, CAMERA_HEIGHT_1, CAMERA_FORMAT_1, CAMERA_MAX_FPS_1,
+ CAMERA_NAME_1,
+ CAMERA_SENSOR_ORIENTATION_1, CAMERA_LENS_FACING_1),
+ AttributionSource.myAttributionSource());
mVirtualCameraController.registerCamera(createVirtualCameraConfig(
- CAMERA_WIDTH_2, CAMERA_HEIGHT_2, CAMERA_FORMAT_2, CAMERA_MAX_FPS_2, CAMERA_NAME_2,
- CAMERA_SENSOR_ORIENTATION_2, CAMERA_LENS_FACING_2));
+ CAMERA_WIDTH_2, CAMERA_HEIGHT_2, CAMERA_FORMAT_2, CAMERA_MAX_FPS_2,
+ CAMERA_NAME_2,
+ CAMERA_SENSOR_ORIENTATION_2, CAMERA_LENS_FACING_2),
+ AttributionSource.myAttributionSource());
mVirtualCameraController.close();
@@ -160,11 +165,12 @@
int lensFacing) {
mVirtualCameraController.registerCamera(createVirtualCameraConfig(
CAMERA_WIDTH_1, CAMERA_HEIGHT_1, CAMERA_FORMAT_1, CAMERA_MAX_FPS_1, CAMERA_NAME_1,
- CAMERA_SENSOR_ORIENTATION_1, lensFacing));
+ CAMERA_SENSOR_ORIENTATION_1, lensFacing), AttributionSource.myAttributionSource());
assertThrows(IllegalArgumentException.class,
() -> mVirtualCameraController.registerCamera(createVirtualCameraConfig(
- CAMERA_WIDTH_2, CAMERA_HEIGHT_2, CAMERA_FORMAT_2, CAMERA_MAX_FPS_2,
- CAMERA_NAME_2, CAMERA_SENSOR_ORIENTATION_2, lensFacing)));
+ CAMERA_WIDTH_2, CAMERA_HEIGHT_2, CAMERA_FORMAT_2, CAMERA_MAX_FPS_2,
+ CAMERA_NAME_2, CAMERA_SENSOR_ORIENTATION_2, lensFacing),
+ AttributionSource.myAttributionSource()));
}
@Parameters(method = "getAllLensFacingDirections")
@@ -176,8 +182,9 @@
assertThrows(IllegalArgumentException.class,
() -> mVirtualCameraController.registerCamera(createVirtualCameraConfig(
- CAMERA_WIDTH_1, CAMERA_HEIGHT_1, CAMERA_FORMAT_1, CAMERA_MAX_FPS_1,
- CAMERA_NAME_1, CAMERA_SENSOR_ORIENTATION_1, lensFacing)));
+ CAMERA_WIDTH_1, CAMERA_HEIGHT_1, CAMERA_FORMAT_1, CAMERA_MAX_FPS_1,
+ CAMERA_NAME_1, CAMERA_SENSOR_ORIENTATION_1, lensFacing),
+ AttributionSource.myAttributionSource()));
}
private VirtualCameraConfig createVirtualCameraConfig(
@@ -203,7 +210,7 @@
}
private static Integer[] getAllLensFacingDirections() {
- return new Integer[] {
+ return new Integer[]{
LENS_FACING_BACK,
LENS_FACING_FRONT
};
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index 5f2abc3..a529382 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -205,6 +205,7 @@
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.rules.MethodRule;
@@ -2143,13 +2144,14 @@
assertFalse(mService.isUidNetworkingBlocked(UID_E, false));
}
+ @Ignore("Temporarily disabled until the feature is enabled")
@Test
@RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE)
public void testBackgroundChainEnabled() throws Exception {
verify(mNetworkManager).setFirewallChainEnabled(FIREWALL_CHAIN_BACKGROUND, true);
}
-
+ @Ignore("Temporarily disabled until the feature is enabled")
@Test
@RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE)
public void testBackgroundChainOnProcStateChange() throws Exception {
@@ -2179,6 +2181,7 @@
assertTrue(mService.isUidNetworkingBlocked(UID_A, false));
}
+ @Ignore("Temporarily disabled until the feature is enabled")
@Test
@RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE)
public void testBackgroundChainOnAllowlistChange() throws Exception {
@@ -2217,6 +2220,7 @@
assertFalse(mService.isUidNetworkingBlocked(UID_B, false));
}
+ @Ignore("Temporarily disabled until the feature is enabled")
@Test
@RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE)
public void testBackgroundChainOnTempAllowlistChange() throws Exception {
@@ -2246,6 +2250,7 @@
assertTrue(mService.isUidNetworkingBlocked(UID_A, false));
}
+ @Ignore("Temporarily disabled until the feature is enabled")
@Test
@RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE)
public void testUidObserverFiltersProcStateChanges() throws Exception {
@@ -2308,6 +2313,7 @@
waitForUidEventHandlerIdle();
}
+ @Ignore("Temporarily disabled until the feature is enabled")
@Test
@RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE)
public void testUidObserverFiltersStaleChanges() throws Exception {
@@ -2328,6 +2334,7 @@
waitForUidEventHandlerIdle();
}
+ @Ignore("Temporarily disabled until the feature is enabled")
@Test
@RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE)
public void testUidObserverFiltersCapabilityChanges() throws Exception {
diff --git a/services/tests/servicestests/src/com/android/server/pdb/PersistentDataBlockServiceTest.java b/services/tests/servicestests/src/com/android/server/pdb/PersistentDataBlockServiceTest.java
index da8ec2e..f91f77a 100644
--- a/services/tests/servicestests/src/com/android/server/pdb/PersistentDataBlockServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pdb/PersistentDataBlockServiceTest.java
@@ -17,12 +17,17 @@
package com.android.server.pdb;
import static com.android.server.pdb.PersistentDataBlockService.DIGEST_SIZE_BYTES;
+import static com.android.server.pdb.PersistentDataBlockService.FRP_CREDENTIAL_RESERVED_SIZE;
+import static com.android.server.pdb.PersistentDataBlockService.FRP_SECRET_MAGIC;
import static com.android.server.pdb.PersistentDataBlockService.FRP_SECRET_SIZE;
+import static com.android.server.pdb.PersistentDataBlockService.HEADER_SIZE;
import static com.android.server.pdb.PersistentDataBlockService.MAX_DATA_BLOCK_SIZE;
import static com.android.server.pdb.PersistentDataBlockService.MAX_FRP_CREDENTIAL_HANDLE_SIZE;
import static com.android.server.pdb.PersistentDataBlockService.MAX_TEST_MODE_DATA_SIZE;
+import static com.android.server.pdb.PersistentDataBlockService.TEST_MODE_RESERVED_SIZE;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -36,6 +41,7 @@
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
import android.Manifest;
+import android.annotation.NonNull;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Binder;
@@ -63,6 +69,7 @@
import java.nio.file.StandardOpenOption;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
+import java.util.Arrays;
@RunWith(JUnitParamsRunner.class)
public class PersistentDataBlockServiceTest {
@@ -397,6 +404,68 @@
@Test
@Parameters({"false", "true"})
+ public void testPartitionFormat(boolean frpEnabled) throws Exception {
+ setUp(frpEnabled);
+
+ /*
+ * 1. Fill the PDB with a specific value, so we can check regions that weren't touched
+ * by formatting
+ */
+ FileChannel channel = FileChannel.open(mDataBlockFile.toPath(), StandardOpenOption.WRITE,
+ StandardOpenOption.TRUNCATE_EXISTING);
+ byte[] bufArray = new byte[(int) mPdbService.getBlockDeviceSize()];
+ Arrays.fill(bufArray, (byte) 0x7f);
+ ByteBuffer buf = ByteBuffer.wrap(bufArray);
+ channel.write(buf);
+ channel.close();
+
+ /*
+ * 2. Format it.
+ */
+ mPdbService.formatPartitionLocked(true);
+
+ /*
+ * 3. Check it.
+ */
+ channel = FileChannel.open(mDataBlockFile.toPath(), StandardOpenOption.READ);
+
+ // 3a. Skip the digest and header
+ channel.position(channel.position() + DIGEST_SIZE_BYTES + HEADER_SIZE);
+
+ // 3b. Check the FRP data segment
+ assertContains("FRP data", readData(channel, mPdbService.getMaximumFrpDataSize()).array(),
+ (byte) 0);
+
+ if (frpEnabled) {
+ // 3c. The FRP secret magic & value
+ assertThat(mPdbService.getFrpSecretMagicOffset()).isEqualTo(channel.position());
+ assertThat(readData(channel, FRP_SECRET_MAGIC.length).array()).isEqualTo(
+ FRP_SECRET_MAGIC);
+
+ assertThat(mPdbService.getFrpSecretDataOffset()).isEqualTo(channel.position());
+ assertContains("FRP secret", readData(channel, FRP_SECRET_SIZE).array(), (byte) 0);
+ }
+
+ // 3d. The test mode data (unmodified by formatPartitionLocked()).
+ assertThat(mPdbService.getTestHarnessModeDataOffset()).isEqualTo(channel.position());
+ assertContains("Test data", readData(channel, TEST_MODE_RESERVED_SIZE).array(),
+ (byte) 0x7f);
+
+ // 3e. The FRP credential segment
+ assertThat(mPdbService.getFrpCredentialDataOffset()).isEqualTo(channel.position());
+ assertContains("FRP credential", readData(channel, FRP_CREDENTIAL_RESERVED_SIZE).array(),
+ (byte) 0);
+
+ // 3f. OEM unlock byte.
+ assertThat(mPdbService.getOemUnlockDataOffset()).isEqualTo(channel.position());
+ assertThat(new byte[]{1}).isEqualTo(readData(channel, 1).array());
+
+ // 3g. EOF
+ assertThat(channel.position()).isEqualTo(channel.size());
+ }
+
+ @Test
+ @Parameters({"false", "true"})
public void wipePermissionCheck(boolean frpEnabled) throws Exception {
setUp(frpEnabled);
denyOemUnlockPermission();
@@ -987,4 +1056,20 @@
return buffer;
}
}
+
+ @NonNull
+ private static ByteBuffer readData(FileChannel channel, int length) throws IOException {
+ ByteBuffer buf = ByteBuffer.allocate(length);
+ assertThat(channel.read(buf)).isEqualTo(length);
+ buf.flip();
+ assertThat(buf.limit()).isEqualTo(length);
+ return buf;
+ }
+
+ private static void assertContains(String sectionName, byte[] buf, byte expected) {
+ for (int i = 0; i < buf.length; i++) {
+ assertWithMessage(sectionName + " is incorrect at offset " + i)
+ .that(buf[i]).isEqualTo(expected);
+ }
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/search/SearchablesTest.java b/services/tests/servicestests/src/com/android/server/search/SearchablesTest.java
index 8bccce1..f5c6795 100644
--- a/services/tests/servicestests/src/com/android/server/search/SearchablesTest.java
+++ b/services/tests/servicestests/src/com/android/server/search/SearchablesTest.java
@@ -16,41 +16,60 @@
package com.android.server.search;
-import android.app.SearchManager;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
+
import android.app.SearchableInfo;
import android.app.SearchableInfo.ActionKeyInfo;
import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ProviderInfo;
-import android.content.pm.ResolveInfo;
-import android.content.res.Resources;
-import android.content.res.XmlResourceParser;
-import android.os.RemoteException;
-import com.android.server.search.Searchables;
-import android.test.AndroidTestCase;
+import android.content.pm.PackageManagerInternal;
import android.test.MoreAsserts;
-import android.test.mock.MockContext;
-import android.test.mock.MockPackageManager;
-import android.test.suitebuilder.annotation.SmallTest;
import android.view.KeyEvent;
-import java.util.ArrayList;
-import java.util.List;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
-/**
- * To launch this test from the command line:
- *
- * adb shell am instrument -w \
- * -e class com.android.unit_tests.SearchablesTest \
- * com.android.unit_tests/android.test.InstrumentationTestRunner
- */
+import com.android.server.LocalServices;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+
+@RunWith(AndroidJUnit4.class)
@SmallTest
-public class SearchablesTest extends AndroidTestCase {
-
+public class SearchablesTest {
+ @Mock protected PackageManagerInternal mPackageManagerInternal;
+
+ private Context mContext;
+
+ @Before
+ public final void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ LocalServices.removeServiceForTest(PackageManagerInternal.class);
+ LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternal);
+
+ mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ }
+
+ @After
+ public final void tearDown() {
+ Mockito.framework().clearInlineMocks();
+ }
+
/*
* SearchableInfo tests
* Mock the context so I can provide very specific input data
@@ -69,15 +88,15 @@
* Test that non-searchable activities return no searchable info (this would typically
* trigger the use of the default searchable e.g. contacts)
*/
+ @Test
public void testNonSearchable() {
// test basic array & hashmap
Searchables searchables = new Searchables(mContext, 0);
searchables.updateSearchableList();
// confirm that we return null for non-searchy activities
- ComponentName nonActivity = new ComponentName(
- "com.android.frameworks.coretests",
- "com.android.frameworks.coretests.activity.NO_SEARCH_ACTIVITY");
+ ComponentName nonActivity = new ComponentName("com.android.frameworks.servicestests",
+ "com.android.frameworks.servicestests.activity.NO_SEARCH_ACTIVITY");
SearchableInfo si = searchables.getSearchableInfo(nonActivity);
assertNull(si);
}
@@ -97,13 +116,11 @@
* getIcon works
*/
+ @Test
public void testSearchablesListReal() {
- MyMockPackageManager mockPM = new MyMockPackageManager(mContext.getPackageManager());
- MyMockContext mockContext = new MyMockContext(mContext, mockPM);
+ doReturn(true).when(mPackageManagerInternal).canAccessComponent(anyInt(), any(), anyInt());
- // build item list with real-world source data
- mockPM.setSearchablesMode(MyMockPackageManager.SEARCHABLES_PASSTHROUGH);
- Searchables searchables = new Searchables(mockContext, 0);
+ Searchables searchables = new Searchables(mContext, 0);
searchables.updateSearchableList();
// tests with "real" searchables (deprecate, this should be a unit test)
ArrayList<SearchableInfo> searchablesList = searchables.getSearchablesList();
@@ -117,12 +134,11 @@
/**
* This round of tests confirms good operations with "zero" searchables found
*/
+ @Test
public void testSearchablesListEmpty() {
- MyMockPackageManager mockPM = new MyMockPackageManager(mContext.getPackageManager());
- MyMockContext mockContext = new MyMockContext(mContext, mockPM);
+ doReturn(false).when(mPackageManagerInternal).canAccessComponent(anyInt(), any(), anyInt());
- mockPM.setSearchablesMode(MyMockPackageManager.SEARCHABLES_MOCK_ZERO);
- Searchables searchables = new Searchables(mockContext, 0);
+ Searchables searchables = new Searchables(mContext, 0);
searchables.updateSearchableList();
ArrayList<SearchableInfo> searchablesList = searchables.getSearchablesList();
assertNotNull(searchablesList);
@@ -219,231 +235,6 @@
if (s != null) {
MoreAsserts.assertNotEqual(s, "");
}
- }
-
- /**
- * This is a mock for context. Used to perform a true unit test on SearchableInfo.
- *
- */
- private class MyMockContext extends MockContext {
-
- protected Context mRealContext;
- protected PackageManager mPackageManager;
-
- /**
- * Constructor.
- *
- * @param realContext Please pass in a real context for some pass-throughs to function.
- */
- MyMockContext(Context realContext, PackageManager packageManager) {
- mRealContext = realContext;
- mPackageManager = packageManager;
- }
-
- /**
- * Resources. Pass through for now.
- */
- @Override
- public Resources getResources() {
- return mRealContext.getResources();
- }
-
- /**
- * Package manager. Pass through for now.
- */
- @Override
- public PackageManager getPackageManager() {
- return mPackageManager;
- }
-
- /**
- * Package manager. Pass through for now.
- */
- @Override
- public Context createPackageContext(String packageName, int flags)
- throws PackageManager.NameNotFoundException {
- return mRealContext.createPackageContext(packageName, flags);
- }
-
- /**
- * Message broadcast. Pass through for now.
- */
- @Override
- public void sendBroadcast(Intent intent) {
- mRealContext.sendBroadcast(intent);
- }
- }
-
-/**
- * This is a mock for package manager. Used to perform a true unit test on SearchableInfo.
- *
- */
- private class MyMockPackageManager extends MockPackageManager {
-
- public final static int SEARCHABLES_PASSTHROUGH = 0;
- public final static int SEARCHABLES_MOCK_ZERO = 1;
- public final static int SEARCHABLES_MOCK_ONEGOOD = 2;
- public final static int SEARCHABLES_MOCK_ONEGOOD_ONEBAD = 3;
-
- protected PackageManager mRealPackageManager;
- protected int mSearchablesMode;
-
- public MyMockPackageManager(PackageManager realPM) {
- mRealPackageManager = realPM;
- mSearchablesMode = SEARCHABLES_PASSTHROUGH;
- }
-
- /**
- * Set the mode for various tests.
- */
- public void setSearchablesMode(int newMode) {
- switch (newMode) {
- case SEARCHABLES_PASSTHROUGH:
- case SEARCHABLES_MOCK_ZERO:
- mSearchablesMode = newMode;
- break;
-
- default:
- throw new UnsupportedOperationException();
- }
- }
-
- /**
- * Find activities that support a given intent.
- *
- * Retrieve all activities that can be performed for the given intent.
- *
- * @param intent The desired intent as per resolveActivity().
- * @param flags Additional option flags. The most important is
- * MATCH_DEFAULT_ONLY, to limit the resolution to only
- * those activities that support the CATEGORY_DEFAULT.
- *
- * @return A List<ResolveInfo> containing one entry for each matching
- * Activity. These are ordered from best to worst match -- that
- * is, the first item in the list is what is returned by
- * resolveActivity(). If there are no matching activities, an empty
- * list is returned.
- */
- @Override
- public List<ResolveInfo> queryIntentActivities(Intent intent, int flags) {
- assertNotNull(intent);
- assertTrue(intent.getAction().equals(Intent.ACTION_SEARCH)
- || intent.getAction().equals(Intent.ACTION_WEB_SEARCH)
- || intent.getAction().equals(SearchManager.INTENT_ACTION_GLOBAL_SEARCH));
- switch (mSearchablesMode) {
- case SEARCHABLES_PASSTHROUGH:
- return mRealPackageManager.queryIntentActivities(intent, flags);
- case SEARCHABLES_MOCK_ZERO:
- return null;
- default:
- throw new UnsupportedOperationException();
- }
- }
-
- @Override
- public ResolveInfo resolveActivity(Intent intent, int flags) {
- assertNotNull(intent);
- assertTrue(intent.getAction().equals(Intent.ACTION_WEB_SEARCH)
- || intent.getAction().equals(SearchManager.INTENT_ACTION_GLOBAL_SEARCH));
- switch (mSearchablesMode) {
- case SEARCHABLES_PASSTHROUGH:
- return mRealPackageManager.resolveActivity(intent, flags);
- case SEARCHABLES_MOCK_ZERO:
- return null;
- default:
- throw new UnsupportedOperationException();
- }
- }
-
- /**
- * Retrieve an XML file from a package. This is a low-level API used to
- * retrieve XML meta data.
- *
- * @param packageName The name of the package that this xml is coming from.
- * Can not be null.
- * @param resid The resource identifier of the desired xml. Can not be 0.
- * @param appInfo Overall information about <var>packageName</var>. This
- * may be null, in which case the application information will be retrieved
- * for you if needed; if you already have this information around, it can
- * be much more efficient to supply it here.
- *
- * @return Returns an TypedXmlPullParser allowing you to parse out the XML
- * data. Returns null if the xml resource could not be found for any
- * reason.
- */
- @Override
- public XmlResourceParser getXml(String packageName, int resid, ApplicationInfo appInfo) {
- assertNotNull(packageName);
- MoreAsserts.assertNotEqual(packageName, "");
- MoreAsserts.assertNotEqual(resid, 0);
- switch (mSearchablesMode) {
- case SEARCHABLES_PASSTHROUGH:
- return mRealPackageManager.getXml(packageName, resid, appInfo);
- case SEARCHABLES_MOCK_ZERO:
- default:
- throw new UnsupportedOperationException();
- }
- }
-
- /**
- * Find a single content provider by its base path name.
- *
- * @param name The name of the provider to find.
- * @param flags Additional option flags. Currently should always be 0.
- *
- * @return ContentProviderInfo Information about the provider, if found,
- * else null.
- */
- @Override
- public ProviderInfo resolveContentProvider(String name, int flags) {
- assertNotNull(name);
- MoreAsserts.assertNotEqual(name, "");
- assertEquals(flags, 0);
- switch (mSearchablesMode) {
- case SEARCHABLES_PASSTHROUGH:
- return mRealPackageManager.resolveContentProvider(name, flags);
- case SEARCHABLES_MOCK_ZERO:
- default:
- throw new UnsupportedOperationException();
- }
- }
-
- /**
- * Get the activity information for a particular activity.
- *
- * @param name The name of the activity to find.
- * @param flags Additional option flags.
- *
- * @return ActivityInfo Information about the activity, if found, else null.
- */
- @Override
- public ActivityInfo getActivityInfo(ComponentName name, int flags)
- throws NameNotFoundException {
- assertNotNull(name);
- MoreAsserts.assertNotEqual(name, "");
- switch (mSearchablesMode) {
- case SEARCHABLES_PASSTHROUGH:
- return mRealPackageManager.getActivityInfo(name, flags);
- case SEARCHABLES_MOCK_ZERO:
- throw new NameNotFoundException();
- default:
- throw new UnsupportedOperationException();
- }
- }
-
- @Override
- public int checkPermission(String permName, String pkgName) {
- assertNotNull(permName);
- assertNotNull(pkgName);
- switch (mSearchablesMode) {
- case SEARCHABLES_PASSTHROUGH:
- return mRealPackageManager.checkPermission(permName, pkgName);
- case SEARCHABLES_MOCK_ZERO:
- return PackageManager.PERMISSION_DENIED;
- default:
- throw new UnsupportedOperationException();
- }
- }
}
}
diff --git a/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java b/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
index ae0a758..65662d6 100644
--- a/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
+++ b/services/tests/servicestests/src/com/android/server/webkit/TestSystemImpl.java
@@ -17,6 +17,7 @@
package com.android.server.webkit;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.UserInfo;
@@ -179,4 +180,7 @@
public boolean isMultiProcessDefaultEnabled() {
return mMultiProcessDefault;
}
+
+ @Override
+ public void pinWebviewIfRequired(ApplicationInfo appInfo) {}
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 94d24a9..92dad25 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -595,6 +595,7 @@
.thenReturn(INVALID_TASK_ID);
mContext.addMockSystemService(AppOpsManager.class, mock(AppOpsManager.class));
when(mUm.getProfileIds(eq(mUserId), eq(false))).thenReturn(new int[] { mUserId });
+ when(mAmi.getCurrentUserId()).thenReturn(mUserId);
when(mPackageManagerClient.hasSystemFeature(FEATURE_TELECOM)).thenReturn(true);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
index 12f9e26..abfb95c 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java
@@ -625,6 +625,21 @@
}
@Test
+ public void testRuleXml_customInterruptionFilter() throws Exception {
+ ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule();
+ rule.zenMode = Settings.Global.ZEN_MODE_ALARMS;
+ rule.conditionId = Uri.parse("condition://android/blah");
+ assertThat(Condition.isValidId(rule.conditionId, ZenModeConfig.SYSTEM_AUTHORITY)).isTrue();
+
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ writeRuleXml(rule, baos);
+ ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+ ZenModeConfig.ZenRule fromXml = readRuleXml(bais);
+
+ assertEquals(rule.zenMode, fromXml.zenMode);
+ }
+
+ @Test
public void testZenPolicyXml_allUnset() throws Exception {
ZenPolicy policy = new ZenPolicy.Builder().build();
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index 6e5c180..3f2ccaf 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -5384,6 +5384,7 @@
ZenRule rule = new ZenRule();
rule.pkg = pkg;
rule.creationTime = createdAt.toEpochMilli();
+ rule.enabled = true;
rule.deletionInstant = deletedAt;
// Plus stuff so that isValidAutomaticRule() passes
rule.name = "A rule from " + pkg + " created on " + createdAt;
@@ -5392,6 +5393,47 @@
}
@Test
+ @EnableFlags(Flags.FLAG_MODES_API)
+ public void getAutomaticZenRuleState_ownedRule_returnsRuleState() {
+ String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(),
+ new AutomaticZenRule.Builder("Rule", CONDITION_ID)
+ .setConfigurationActivity(
+ new ComponentName(mContext.getPackageName(), "Blah"))
+ .build(),
+ UPDATE_ORIGIN_APP, "reasons", CUSTOM_PKG_UID);
+
+ // Null condition -> STATE_FALSE
+ assertThat(mZenModeHelper.getAutomaticZenRuleState(id)).isEqualTo(Condition.STATE_FALSE);
+
+ mZenModeHelper.setAutomaticZenRuleState(id, CONDITION_TRUE, UPDATE_ORIGIN_APP,
+ CUSTOM_PKG_UID);
+ assertThat(mZenModeHelper.getAutomaticZenRuleState(id)).isEqualTo(Condition.STATE_TRUE);
+
+ mZenModeHelper.setAutomaticZenRuleState(id, CONDITION_FALSE, UPDATE_ORIGIN_APP,
+ CUSTOM_PKG_UID);
+ assertThat(mZenModeHelper.getAutomaticZenRuleState(id)).isEqualTo(Condition.STATE_FALSE);
+
+ mZenModeHelper.removeAutomaticZenRule(id, UPDATE_ORIGIN_APP, "", CUSTOM_PKG_UID);
+ assertThat(mZenModeHelper.getAutomaticZenRuleState(id)).isEqualTo(Condition.STATE_UNKNOWN);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_MODES_API)
+ public void getAutomaticZenRuleState_notOwnedRule_returnsStateUnknown() {
+ // Assume existence of a system-owned rule that is currently ACTIVE.
+ ZenRule systemRule = newZenRule("android", Instant.now(), null);
+ systemRule.zenMode = ZEN_MODE_ALARMS;
+ systemRule.condition = new Condition(systemRule.conditionId, "on", Condition.STATE_TRUE);
+ ZenModeConfig config = mZenModeHelper.mConfig.copy();
+ config.automaticRules.put("systemRule", systemRule);
+ mZenModeHelper.setConfig(config, null, UPDATE_ORIGIN_INIT, "", Process.SYSTEM_UID);
+ assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_ALARMS);
+
+ assertThat(mZenModeHelper.getAutomaticZenRuleState("systemRule")).isEqualTo(
+ Condition.STATE_UNKNOWN);
+ }
+
+ @Test
@EnableFlags(android.app.Flags.FLAG_MODES_API)
public void testCallbacks_policy() throws Exception {
setupZenConfig();
diff --git a/services/tests/vibrator/Android.bp b/services/tests/vibrator/Android.bp
index 66dcaff..da21cd3 100644
--- a/services/tests/vibrator/Android.bp
+++ b/services/tests/vibrator/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_haptics_framework",
// 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"
diff --git a/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java b/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java
index f080341..f54c7e5 100644
--- a/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java
+++ b/services/tests/vibrator/src/com/android/server/vibrator/VibrationSettingsTest.java
@@ -100,6 +100,8 @@
@Rule
public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
+ private static final int OLD_USER_ID = 123;
+ private static final int NEW_USER_ID = 456;
private static final int UID = 1;
private static final int VIRTUAL_DEVICE_ID = 1;
private static final String SYSUI_PACKAGE_NAME = "sysui";
@@ -211,10 +213,10 @@
mVibrationSettings.addListener(mListenerMock);
// Testing the broadcast flow manually.
- mVibrationSettings.mSettingChangeReceiver.onReceive(mContextSpy,
- new Intent(Intent.ACTION_USER_SWITCHED));
+ mVibrationSettings.mUserSwitchObserver.onUserSwitching(NEW_USER_ID);
+ mVibrationSettings.mUserSwitchObserver.onUserSwitchComplete(NEW_USER_ID);
- verify(mListenerMock).onChange();
+ verify(mListenerMock, times(2)).onChange();
}
@Test
@@ -265,8 +267,7 @@
// Trigger multiple observers manually.
mVibrationSettings.mSettingObserver.onChange(false);
mRegisteredPowerModeListener.onLowPowerModeChanged(LOW_POWER_STATE);
- mVibrationSettings.mSettingChangeReceiver.onReceive(mContextSpy,
- new Intent(Intent.ACTION_USER_SWITCHED));
+ mVibrationSettings.mUserSwitchObserver.onUserSwitchComplete(NEW_USER_ID);
mVibrationSettings.mSettingChangeReceiver.onReceive(mContextSpy,
new Intent(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION));
@@ -834,13 +835,17 @@
assertEquals(VIBRATION_INTENSITY_HIGH,
mVibrationSettings.getCurrentIntensity(USAGE_RINGTONE));
- // Switching user is not working with FakeSettingsProvider.
- // Testing the broadcast flow manually.
- Settings.System.putIntForUser(mContextSpy.getContentResolver(),
- Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_LOW,
+ // Test early update of settings based on new user id.
+ putUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_LOW,
+ NEW_USER_ID);
+ mVibrationSettings.mUserSwitchObserver.onUserSwitching(NEW_USER_ID);
+ assertEquals(VIBRATION_INTENSITY_LOW,
+ mVibrationSettings.getCurrentIntensity(USAGE_RINGTONE));
+
+ // Test later update of settings for UserHandle.USER_CURRENT.
+ putUserSetting(Settings.System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_LOW,
UserHandle.USER_CURRENT);
- mVibrationSettings.mSettingChangeReceiver.onReceive(mContextSpy,
- new Intent(Intent.ACTION_USER_SWITCHED));
+ mVibrationSettings.mUserSwitchObserver.onUserSwitchComplete(NEW_USER_ID);
assertEquals(VIBRATION_INTENSITY_LOW,
mVibrationSettings.getCurrentIntensity(USAGE_RINGTONE));
}
@@ -956,12 +961,16 @@
}
private void setUserSetting(String settingName, int value) {
- Settings.System.putIntForUser(
- mContextSpy.getContentResolver(), settingName, value, UserHandle.USER_CURRENT);
+ putUserSetting(settingName, value, UserHandle.USER_CURRENT);
// FakeSettingsProvider doesn't support testing triggering ContentObserver yet.
mVibrationSettings.mSettingObserver.onChange(false);
}
+ private void putUserSetting(String settingName, int value, int userHandle) {
+ Settings.System.putIntForUser(
+ mContextSpy.getContentResolver(), settingName, value, userHandle);
+ }
+
private void setRingerMode(int ringerMode) {
when(mAudioManagerMock.getRingerModeInternal()).thenReturn(ringerMode);
// Mock AudioManager broadcast of internal ringer mode change.
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
index 5d14334..5965fae 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
@@ -39,6 +39,7 @@
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.server.policy.WindowManagerPolicy.NAV_BAR_BOTTOM;
import static org.junit.Assert.assertEquals;
@@ -388,6 +389,24 @@
// The current insets are restored from cache directly.
assertEquals(prevConfigFrame, displayPolicy.getDecorInsetsInfo(info.rotation,
info.logicalWidth, info.logicalHeight).mConfigFrame);
+
+ // If screen is not fully turned on, then the cache should be preserved.
+ displayPolicy.screenTurnedOff();
+ final TransitionController transitionController = mDisplayContent.mTransitionController;
+ spyOn(transitionController);
+ doReturn(true).when(transitionController).isCollecting();
+ doReturn(Integer.MAX_VALUE).when(transitionController).getCollectingTransitionId();
+ // Make CachedDecorInsets.canPreserve return false.
+ displayPolicy.physicalDisplayUpdated();
+ assertFalse(displayPolicy.shouldKeepCurrentDecorInsets());
+ displayPolicy.getDecorInsetsInfo(info.rotation, info.logicalWidth, info.logicalHeight)
+ .mConfigFrame.offset(1, 1);
+ // Even if CachedDecorInsets.canPreserve returns false, the cache won't be cleared.
+ displayPolicy.updateDecorInsetsInfo();
+ // Successful to restore from cache.
+ displayPolicy.updateCachedDecorInsets();
+ assertEquals(prevConfigFrame, displayPolicy.getDecorInsetsInfo(info.rotation,
+ info.logicalWidth, info.logicalHeight).mConfigFrame);
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index f42cdb8..b96f39d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -22,6 +22,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.content.pm.ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
@@ -46,6 +47,7 @@
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
@@ -100,6 +102,7 @@
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
+import android.graphics.Insets;
import android.graphics.Rect;
import android.os.Binder;
import android.os.RemoteException;
@@ -153,6 +156,8 @@
private static final String CONFIG_NEVER_CONSTRAIN_DISPLAY_APIS_ALL_PACKAGES =
"never_constrain_display_apis_all_packages";
+ private static final float DELTA_ASPECT_RATIO_TOLERANCE = 0.005f;
+
@Rule
public TestRule compatChangeRule = new PlatformCompatChangeRule();
@@ -211,90 +216,46 @@
@Test
public void testHorizontalReachabilityEnabledForTranslucentActivities() {
- setUpDisplaySizeWithApp(2500, 1000);
- mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
- final LetterboxConfiguration config = mWm.mLetterboxConfiguration;
- config.setTranslucentLetterboxingOverrideEnabled(true);
- config.setLetterboxHorizontalPositionMultiplier(0.5f);
- config.setIsHorizontalReachabilityEnabled(true);
+ testReachabilityEnabledForTranslucentActivity(/* dw */ 2500, /* dh */1000,
+ SCREEN_ORIENTATION_PORTRAIT, /* minAspectRatio */ 0f,
+ /* horizontalReachability */ true);
+ }
- // Opaque activity
- prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
- addWindowToActivity(mActivity);
- mActivity.mRootWindowContainer.performSurfacePlacement();
-
- // Translucent Activity
- final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
- .setActivityTheme(android.R.style.Theme_Translucent)
- .setLaunchedFromUid(mActivity.getUid())
- .setScreenOrientation(SCREEN_ORIENTATION_PORTRAIT)
- .build();
- mTask.addChild(translucentActivity);
-
- spyOn(translucentActivity.mLetterboxUiController);
- doReturn(true).when(translucentActivity.mLetterboxUiController)
- .shouldShowLetterboxUi(any());
-
- addWindowToActivity(translucentActivity);
- translucentActivity.mRootWindowContainer.performSurfacePlacement();
-
- final Function<ActivityRecord, Rect> innerBoundsOf =
- (ActivityRecord a) -> {
- final Rect bounds = new Rect();
- a.mLetterboxUiController.getLetterboxInnerBounds(bounds);
- return bounds;
- };
- final Runnable checkLetterboxPositions = () -> assertEquals(innerBoundsOf.apply(mActivity),
- innerBoundsOf.apply(translucentActivity));
- final Runnable checkIsLeft = () -> assertThat(
- innerBoundsOf.apply(translucentActivity).left).isEqualTo(0);
- final Runnable checkIsRight = () -> assertThat(
- innerBoundsOf.apply(translucentActivity).right).isEqualTo(2500);
- final Runnable checkIsCentered = () -> assertThat(
- innerBoundsOf.apply(translucentActivity).left > 0
- && innerBoundsOf.apply(translucentActivity).right < 2500).isTrue();
-
- final Consumer<Integer> doubleClick =
- (Integer x) -> {
- mActivity.mLetterboxUiController.handleHorizontalDoubleTap(x);
- mActivity.mRootWindowContainer.performSurfacePlacement();
- };
-
- // Initial state
- checkIsCentered.run();
-
- // Double-click left
- doubleClick.accept(/* x */ 10);
- checkLetterboxPositions.run();
- checkIsLeft.run();
-
- // Double-click right
- doubleClick.accept(/* x */ 1990);
- checkLetterboxPositions.run();
- checkIsCentered.run();
-
- // Double-click right
- doubleClick.accept(/* x */ 1990);
- checkLetterboxPositions.run();
- checkIsRight.run();
-
- // Double-click left
- doubleClick.accept(/* x */ 10);
- checkLetterboxPositions.run();
- checkIsCentered.run();
+ @Test
+ public void testHorizontalReachabilityEnabled_TranslucentPortraitActivities_portraitDisplay() {
+ testReachabilityEnabledForTranslucentActivity(/* dw */ 1400, /* dh */1600,
+ SCREEN_ORIENTATION_PORTRAIT, OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE,
+ /* horizontalReachability */ true);
}
@Test
public void testVerticalReachabilityEnabledForTranslucentActivities() {
- setUpDisplaySizeWithApp(1000, 2500);
+ testReachabilityEnabledForTranslucentActivity(/* dw */ 1000, /* dh */2500,
+ SCREEN_ORIENTATION_LANDSCAPE, /* minAspectRatio */ 0f,
+ /* horizontalReachability */ false);
+ }
+
+ @Test
+ public void testVerticalReachabilityEnabled_TranslucentLandscapeActivities_landscapeDisplay() {
+ testReachabilityEnabledForTranslucentActivity(/* dw */ 1600, /* dh */1400,
+ SCREEN_ORIENTATION_LANDSCAPE, OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE,
+ /* horizontalReachability */ false);
+ }
+
+ private void testReachabilityEnabledForTranslucentActivity(int displayWidth, int displayHeight,
+ @ScreenOrientation int screenOrientation, float minAspectRatio,
+ boolean horizontalReachability) {
+ setUpDisplaySizeWithApp(displayWidth, displayHeight);
mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
final LetterboxConfiguration config = mWm.mLetterboxConfiguration;
config.setTranslucentLetterboxingOverrideEnabled(true);
config.setLetterboxVerticalPositionMultiplier(0.5f);
config.setIsVerticalReachabilityEnabled(true);
+ config.setLetterboxHorizontalPositionMultiplier(0.5f);
+ config.setIsHorizontalReachabilityEnabled(true);
// Opaque activity
- prepareUnresizable(mActivity, SCREEN_ORIENTATION_LANDSCAPE);
+ prepareMinAspectRatio(mActivity, minAspectRatio, screenOrientation);
addWindowToActivity(mActivity);
mActivity.mRootWindowContainer.performSurfacePlacement();
@@ -302,7 +263,7 @@
final ActivityRecord translucentActivity = new ActivityBuilder(mAtm)
.setActivityTheme(android.R.style.Theme_Translucent)
.setLaunchedFromUid(mActivity.getUid())
- .setScreenOrientation(SCREEN_ORIENTATION_LANDSCAPE)
+ .setScreenOrientation(screenOrientation)
.build();
mTask.addChild(translucentActivity);
@@ -324,39 +285,78 @@
final Runnable checkIsTop = () -> assertThat(
innerBoundsOf.apply(translucentActivity).top).isEqualTo(0);
final Runnable checkIsBottom = () -> assertThat(
- innerBoundsOf.apply(translucentActivity).bottom).isEqualTo(2500);
- final Runnable checkIsCentered = () -> assertThat(
+ innerBoundsOf.apply(translucentActivity).bottom).isEqualTo(displayHeight);
+ final Runnable checkIsLeft = () -> assertThat(
+ innerBoundsOf.apply(translucentActivity).left).isEqualTo(0);
+ final Runnable checkIsRight = () -> assertThat(
+ innerBoundsOf.apply(translucentActivity).right).isEqualTo(displayWidth);
+ final Runnable checkIsHorizontallyCentered = () -> assertThat(
+ innerBoundsOf.apply(translucentActivity).left > 0
+ && innerBoundsOf.apply(translucentActivity).right < displayWidth).isTrue();
+ final Runnable checkIsVerticallyCentered = () -> assertThat(
innerBoundsOf.apply(translucentActivity).top > 0
- && innerBoundsOf.apply(translucentActivity).bottom < 2500).isTrue();
+ && innerBoundsOf.apply(translucentActivity).bottom < displayHeight)
+ .isTrue();
- final Consumer<Integer> doubleClick =
- (Integer y) -> {
- mActivity.mLetterboxUiController.handleVerticalDoubleTap(y);
- mActivity.mRootWindowContainer.performSurfacePlacement();
- };
+ if (horizontalReachability) {
+ final Consumer<Integer> doubleClick =
+ (Integer x) -> {
+ mActivity.mLetterboxUiController.handleHorizontalDoubleTap(x);
+ mActivity.mRootWindowContainer.performSurfacePlacement();
+ };
- // Initial state
- checkIsCentered.run();
+ // Initial state
+ checkIsHorizontallyCentered.run();
- // Double-click top
- doubleClick.accept(/* y */ 10);
- checkLetterboxPositions.run();
- checkIsTop.run();
+ // Double-click left
+ doubleClick.accept(/* x */ 10);
+ checkLetterboxPositions.run();
+ checkIsLeft.run();
- // Double-click bottom
- doubleClick.accept(/* y */ 1990);
- checkLetterboxPositions.run();
- checkIsCentered.run();
+ // Double-click right
+ doubleClick.accept(/* x */ displayWidth - 100);
+ checkLetterboxPositions.run();
+ checkIsHorizontallyCentered.run();
- // Double-click bottom
- doubleClick.accept(/* y */ 1990);
- checkLetterboxPositions.run();
- checkIsBottom.run();
+ // Double-click right
+ doubleClick.accept(/* x */ displayWidth - 100);
+ checkLetterboxPositions.run();
+ checkIsRight.run();
- // Double-click top
- doubleClick.accept(/* y */ 10);
- checkLetterboxPositions.run();
- checkIsCentered.run();
+ // Double-click left
+ doubleClick.accept(/* x */ 10);
+ checkLetterboxPositions.run();
+ checkIsHorizontallyCentered.run();
+ } else {
+ final Consumer<Integer> doubleClick =
+ (Integer y) -> {
+ mActivity.mLetterboxUiController.handleVerticalDoubleTap(y);
+ mActivity.mRootWindowContainer.performSurfacePlacement();
+ };
+
+ // Initial state
+ checkIsVerticallyCentered.run();
+
+ // Double-click top
+ doubleClick.accept(/* y */ 10);
+ checkLetterboxPositions.run();
+ checkIsTop.run();
+
+ // Double-click bottom
+ doubleClick.accept(/* y */ displayHeight - 100);
+ checkLetterboxPositions.run();
+ checkIsVerticallyCentered.run();
+
+ // Double-click bottom
+ doubleClick.accept(/* y */ displayHeight - 100);
+ checkLetterboxPositions.run();
+ checkIsBottom.run();
+
+ // Double-click top
+ doubleClick.accept(/* y */ 10);
+ checkLetterboxPositions.run();
+ checkIsVerticallyCentered.run();
+ }
}
@Test
@@ -2143,7 +2143,7 @@
final Rect afterBounds = mActivity.getBounds();
final float actualAspectRatio = 1f * afterBounds.height() / afterBounds.width();
assertEquals(LetterboxConfiguration.DEFAULT_LETTERBOX_ASPECT_RATIO_FOR_MULTI_WINDOW,
- actualAspectRatio, 0.001f);
+ actualAspectRatio, DELTA_ASPECT_RATIO_TOLERANCE);
assertTrue(mActivity.areBoundsLetterboxed());
}
@@ -2179,7 +2179,7 @@
// default letterbox aspect ratio for multi-window.
final Rect afterBounds = mActivity.getBounds();
final float actualAspectRatio = 1f * afterBounds.height() / afterBounds.width();
- assertEquals(minAspectRatio, actualAspectRatio, 0.001f);
+ assertEquals(minAspectRatio, actualAspectRatio, DELTA_ASPECT_RATIO_TOLERANCE);
assertTrue(mActivity.areBoundsLetterboxed());
}
@@ -2487,7 +2487,7 @@
final float afterAspectRatio =
(float) Math.max(width, height) / (float) Math.min(width, height);
- assertEquals(expectedAspectRatio, afterAspectRatio, 0.001f);
+ assertEquals(expectedAspectRatio, afterAspectRatio, DELTA_ASPECT_RATIO_TOLERANCE);
}
@Test
@@ -2512,7 +2512,7 @@
float expectedAspectRatio = 1f * displayWidth / getExpectedSplitSize(displayHeight);
final Rect afterBounds = activity.getBounds();
final float afterAspectRatio = (float) (afterBounds.height()) / afterBounds.width();
- assertEquals(expectedAspectRatio, afterAspectRatio, 0.001f);
+ assertEquals(expectedAspectRatio, afterAspectRatio, DELTA_ASPECT_RATIO_TOLERANCE);
}
@Test
@@ -2537,7 +2537,7 @@
float expectedAspectRatio = 1f * displayHeight / getExpectedSplitSize(displayWidth);
final Rect afterBounds = activity.getBounds();
final float afterAspectRatio = (float) (afterBounds.height()) / afterBounds.width();
- assertEquals(expectedAspectRatio, afterAspectRatio, 0.001f);
+ assertEquals(expectedAspectRatio, afterAspectRatio, DELTA_ASPECT_RATIO_TOLERANCE);
}
@Test
@@ -2563,7 +2563,7 @@
float expectedAspectRatio = 1f * displayWidth / getExpectedSplitSize(displayHeight);
final Rect afterBounds = activity.getBounds();
final float afterAspectRatio = (float) (afterBounds.width()) / afterBounds.height();
- assertEquals(expectedAspectRatio, afterAspectRatio, 0.001f);
+ assertEquals(expectedAspectRatio, afterAspectRatio, DELTA_ASPECT_RATIO_TOLERANCE);
}
@Test
@@ -2589,7 +2589,7 @@
float expectedAspectRatio = 1f * displayHeight / getExpectedSplitSize(displayWidth);
final Rect afterBounds = activity.getBounds();
final float afterAspectRatio = (float) (afterBounds.width()) / afterBounds.height();
- assertEquals(expectedAspectRatio, afterAspectRatio, 0.001f);
+ assertEquals(expectedAspectRatio, afterAspectRatio, DELTA_ASPECT_RATIO_TOLERANCE);
}
@Test
@@ -2629,7 +2629,7 @@
float expectedAspectRatio = 1f * screenHeight / getExpectedSplitSize(screenWidth);
final Rect afterBounds = activity.getBounds();
final float afterAspectRatio = (float) (afterBounds.height()) / afterBounds.width();
- assertEquals(expectedAspectRatio, afterAspectRatio, 0.001f);
+ assertEquals(expectedAspectRatio, afterAspectRatio, DELTA_ASPECT_RATIO_TOLERANCE);
assertFalse(activity.areBoundsLetterboxed());
}
@@ -2670,7 +2670,7 @@
float expectedAspectRatio = 1f * screenWidth / getExpectedSplitSize(screenHeight);
final Rect afterBounds = activity.getBounds();
final float afterAspectRatio = (float) (afterBounds.width()) / afterBounds.height();
- assertEquals(expectedAspectRatio, afterAspectRatio, 0.001f);
+ assertEquals(expectedAspectRatio, afterAspectRatio, DELTA_ASPECT_RATIO_TOLERANCE);
assertFalse(activity.areBoundsLetterboxed());
}
@@ -2847,9 +2847,8 @@
assertFitted();
// Check that the display aspect ratio is used by the app.
final float targetMinAspectRatio = 1f * displayHeight / displayWidth;
- final float delta = 0.01f;
assertEquals(targetMinAspectRatio, ActivityRecord
- .computeAspectRatio(mActivity.getBounds()), delta);
+ .computeAspectRatio(mActivity.getBounds()), DELTA_ASPECT_RATIO_TOLERANCE);
}
@Test
@@ -2883,9 +2882,8 @@
assertFitted();
// Check that the display aspect ratio is used by the app.
final float targetMinAspectRatio = 1f * displayWidth / displayHeight;
- final float delta = 0.01f;
assertEquals(targetMinAspectRatio, ActivityRecord
- .computeAspectRatio(mActivity.getBounds()), delta);
+ .computeAspectRatio(mActivity.getBounds()), DELTA_ASPECT_RATIO_TOLERANCE);
}
@Test
@@ -2910,9 +2908,8 @@
assertFitted();
// Check that the display aspect ratio is used by the app.
final float targetMinAspectRatio = 1f * displayHeight / displayWidth;
- final float delta = 0.01f;
assertEquals(targetMinAspectRatio, ActivityRecord
- .computeAspectRatio(mActivity.getBounds()), delta);
+ .computeAspectRatio(mActivity.getBounds()), DELTA_ASPECT_RATIO_TOLERANCE);
}
@Test
@@ -2937,9 +2934,8 @@
assertFitted();
// Check that the display aspect ratio is used by the app.
final float targetMinAspectRatio = 1f * displayWidth / displayHeight;
- final float delta = 0.01f;
assertEquals(targetMinAspectRatio, ActivityRecord
- .computeAspectRatio(mActivity.getBounds()), delta);
+ .computeAspectRatio(mActivity.getBounds()), DELTA_ASPECT_RATIO_TOLERANCE);
}
@Test
@@ -3609,6 +3605,32 @@
}
@Test
+ public void testIsHorizontalReachabilityEnabled_portraitDisplayAndApp_true() {
+ // Portrait display
+ setUpDisplaySizeWithApp(1400, 1600);
+ mActivity.mWmService.mLetterboxConfiguration.setIsHorizontalReachabilityEnabled(true);
+
+ // 16:9f unresizable portrait app
+ prepareMinAspectRatio(mActivity, OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE,
+ SCREEN_ORIENTATION_PORTRAIT);
+
+ assertTrue(mActivity.mLetterboxUiController.isHorizontalReachabilityEnabled());
+ }
+
+ @Test
+ public void testIsVerticalReachabilityEnabled_landscapeDisplayAndApp_true() {
+ // Landscape display
+ setUpDisplaySizeWithApp(1600, 1500);
+ mActivity.mWmService.mLetterboxConfiguration.setIsVerticalReachabilityEnabled(true);
+
+ // 16:9f unresizable landscape app
+ prepareMinAspectRatio(mActivity, OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE,
+ SCREEN_ORIENTATION_LANDSCAPE);
+
+ assertTrue(mActivity.mLetterboxUiController.isVerticalReachabilityEnabled());
+ }
+
+ @Test
public void testIsHorizontalReachabilityEnabled_doesNotMatchParentHeight_false() {
setUpDisplaySizeWithApp(2800, 1000);
mActivity.mDisplayContent.setIgnoreOrientationRequest(true /* ignoreOrientationRequest */);
@@ -4053,6 +4075,32 @@
}
@Test
+ public void testPortraitCloseToSquareDisplayWithTaskbar_notLetterboxed() {
+ // Set up portrait close to square display
+ setUpDisplaySizeWithApp(2200, 2280);
+ final DisplayContent display = mActivity.mDisplayContent;
+ // Simulate taskbar, final app bounds are (0, 0, 2200, 2130) - landscape
+ final WindowState navbar = createWindow(null, TYPE_NAVIGATION_BAR, mDisplayContent,
+ "navbar");
+ final Binder owner = new Binder();
+ navbar.mAttrs.providedInsets = new InsetsFrameProvider[] {
+ new InsetsFrameProvider(owner, 0, WindowInsets.Type.navigationBars())
+ .setInsetsSize(Insets.of(0, 0, 0, 150))
+ };
+ display.getDisplayPolicy().addWindowLw(navbar, navbar.mAttrs);
+ assertTrue(navbar.providesDisplayDecorInsets()
+ && display.getDisplayPolicy().updateDecorInsetsInfo());
+ display.sendNewConfiguration();
+
+ prepareUnresizable(mActivity, SCREEN_ORIENTATION_PORTRAIT);
+
+ // Activity is fullscreen even though orientation is not respected with insets, because
+ // the display still matches or is less than the activity aspect ratio
+ assertEquals(display.getBounds(), mActivity.getBounds());
+ assertFalse(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
+ }
+
+ @Test
public void testApplyAspectRatio_activityAlignWithParentAppVertical() {
// The display's app bounds will be (0, 100, 1000, 2350)
final DisplayContent display = new TestDisplayContent.Builder(mAtm, 1000, 2500)
@@ -4275,7 +4323,7 @@
.getFixedOrientationLetterboxAspectRatio(parentConfig);
float expected = mActivity.mLetterboxUiController.getSplitScreenAspectRatio();
- assertEquals(expected, actual, 0.01);
+ assertEquals(expected, actual, DELTA_ASPECT_RATIO_TOLERANCE);
}
@Test
@@ -4671,13 +4719,12 @@
.windowConfiguration.getAppBounds());
// Check that aspect ratio of app bounds is equal to the min aspect ratio.
- final float delta = 0.01f;
assertEquals(targetMinAspectRatio, ActivityRecord
- .computeAspectRatio(fixedOrientationAppBounds), delta);
+ .computeAspectRatio(fixedOrientationAppBounds), DELTA_ASPECT_RATIO_TOLERANCE);
assertEquals(targetMinAspectRatio, ActivityRecord
- .computeAspectRatio(minAspectRatioAppBounds), delta);
+ .computeAspectRatio(minAspectRatioAppBounds), DELTA_ASPECT_RATIO_TOLERANCE);
assertEquals(targetMinAspectRatio, ActivityRecord
- .computeAspectRatio(sizeCompatAppBounds), delta);
+ .computeAspectRatio(sizeCompatAppBounds), DELTA_ASPECT_RATIO_TOLERANCE);
}
@Test
@@ -4859,6 +4906,12 @@
.build();
}
+ static void prepareMinAspectRatio(ActivityRecord activity, float minAspect,
+ int screenOrientation) {
+ prepareLimitedBounds(activity, -1 /* maxAspect */, minAspect, screenOrientation,
+ true /* isUnresizable */);
+ }
+
static void prepareUnresizable(ActivityRecord activity, int screenOrientation) {
prepareUnresizable(activity, -1 /* maxAspect */, screenOrientation);
}
@@ -4873,12 +4926,18 @@
prepareLimitedBounds(activity, -1 /* maxAspect */, screenOrientation, isUnresizable);
}
- /**
- * Setups {@link #mActivity} with restriction on its bounds, such as maxAspect, fixed
- * orientation, and/or whether it is resizable.
- */
static void prepareLimitedBounds(ActivityRecord activity, float maxAspect,
int screenOrientation, boolean isUnresizable) {
+ prepareLimitedBounds(activity, maxAspect, -1 /* minAspect */, screenOrientation,
+ isUnresizable);
+ }
+
+ /**
+ * Setups {@link #mActivity} with restriction on its bounds, such as maxAspect, minAspect,
+ * fixed orientation, and/or whether it is resizable.
+ */
+ static void prepareLimitedBounds(ActivityRecord activity, float maxAspect, float minAspect,
+ int screenOrientation, boolean isUnresizable) {
activity.info.resizeMode = isUnresizable
? RESIZE_MODE_UNRESIZEABLE
: RESIZE_MODE_RESIZEABLE;
@@ -4892,6 +4951,9 @@
if (maxAspect >= 0) {
activity.info.setMaxAspectRatio(maxAspect);
}
+ if (minAspect >= 0) {
+ activity.info.setMinAspectRatio(minAspect);
+ }
if (screenOrientation != SCREEN_ORIENTATION_UNSPECIFIED) {
activity.info.screenOrientation = screenOrientation;
activity.setRequestedOrientation(screenOrientation);
diff --git a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index 295b124..a8f6fe8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -346,7 +346,7 @@
doReturn(true).when(amInternal).hasStartedUserState(anyInt());
doReturn(false).when(amInternal).shouldConfirmCredentials(anyInt());
doReturn(false).when(amInternal).isActivityStartsLoggingEnabled();
- doReturn(true).when(amInternal).getThemeOverlayReadiness();
+ doReturn(true).when(amInternal).isThemeOverlayReady(anyInt());
LocalServices.addService(ActivityManagerInternal.class, amInternal);
final ActivityManagerService amService =
diff --git a/services/tests/wmtests/src/com/android/server/wm/TrustedOverlayTests.java b/services/tests/wmtests/src/com/android/server/wm/TrustedOverlayTests.java
index 6a15b05..f1d84cf 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TrustedOverlayTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TrustedOverlayTests.java
@@ -40,8 +40,10 @@
import androidx.test.ext.junit.rules.ActivityScenarioRule;
import androidx.test.platform.app.InstrumentationRegistry;
+import com.android.server.wm.utils.CommonUtils;
import com.android.window.flags.Flags;
+import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
@@ -77,6 +79,11 @@
});
}
+ @After
+ public void tearDown() {
+ CommonUtils.waitUntilActivityRemoved(mActivity);
+ }
+
@RequiresFlagsDisabled(Flags.FLAG_SURFACE_TRUSTED_OVERLAY)
@Test
public void setTrustedOverlayInputWindow() throws InterruptedException {
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
index 4da519c..c972e51 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -240,6 +240,22 @@
}
@Test
+ public void testTrackOverlayWindow() {
+ final WindowProcessController wpc = mSystemServicesTestRule.addProcess(
+ "pkgName", "processName", 1000 /* pid */, Process.SYSTEM_UID);
+ final Session session = createTestSession(mAtm, wpc.getPid(), wpc.mUid);
+ spyOn(session);
+ assertTrue(session.mCanAddInternalSystemWindow);
+ final WindowSurfaceController winSurface = mock(WindowSurfaceController.class);
+ session.onWindowSurfaceVisibilityChanged(winSurface, true /* visible */,
+ LayoutParams.TYPE_PHONE);
+ verify(session).setHasOverlayUi(true);
+ session.onWindowSurfaceVisibilityChanged(winSurface, false /* visible */,
+ LayoutParams.TYPE_PHONE);
+ verify(session).setHasOverlayUi(false);
+ }
+
+ @Test
public void testRelayoutExitingWindow() {
final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, "appWin");
final WindowSurfaceController surfaceController = mock(WindowSurfaceController.class);
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/DetectorSession.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/DetectorSession.java
index 368a96b..0a1f3c7 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/DetectorSession.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/DetectorSession.java
@@ -192,6 +192,7 @@
final Object mLock;
final int mVoiceInteractionServiceUid;
final Context mContext;
+ final int mUserId;
@Nullable AttentionManagerInternal mAttentionManagerInternal = null;
@@ -224,12 +225,13 @@
@NonNull IHotwordRecognitionStatusCallback callback, int voiceInteractionServiceUid,
Identity voiceInteractorIdentity,
@NonNull ScheduledExecutorService scheduledExecutorService, boolean logging,
- @NonNull DetectorRemoteExceptionListener listener) {
+ @NonNull DetectorRemoteExceptionListener listener, int userId) {
mRemoteExceptionListener = listener;
mRemoteDetectionService = remoteDetectionService;
mLock = lock;
mContext = context;
mToken = token;
+ mUserId = userId;
mCallback = callback;
mVoiceInteractionServiceUid = voiceInteractionServiceUid;
mVoiceInteractorIdentity = voiceInteractorIdentity;
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/DspTrustedHotwordDetectorSession.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/DspTrustedHotwordDetectorSession.java
index 9a4fbdc..8d08c6b 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/DspTrustedHotwordDetectorSession.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/DspTrustedHotwordDetectorSession.java
@@ -87,10 +87,10 @@
@NonNull IHotwordRecognitionStatusCallback callback, int voiceInteractionServiceUid,
Identity voiceInteractorIdentity,
@NonNull ScheduledExecutorService scheduledExecutorService, boolean logging,
- @NonNull DetectorRemoteExceptionListener listener) {
+ @NonNull DetectorRemoteExceptionListener listener, int userId) {
super(remoteHotwordDetectionService, lock, context, token, callback,
voiceInteractionServiceUid, voiceInteractorIdentity, scheduledExecutorService,
- logging, listener);
+ logging, listener, userId);
}
@SuppressWarnings("GuardedBy")
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
index f1f5458..cfcc04b 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
@@ -72,6 +72,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IHotwordRecognitionStatusCallback;
import com.android.internal.app.IVisualQueryDetectionAttentionListener;
+import com.android.internal.app.IVoiceInteractionAccessibilitySettingsListener;
import com.android.internal.infra.AndroidFuture;
import com.android.internal.infra.ServiceConnector;
import com.android.server.LocalServices;
@@ -147,8 +148,9 @@
final int mVoiceInteractionServiceUid;
final ComponentName mHotwordDetectionComponentName;
final ComponentName mVisualQueryDetectionComponentName;
- final int mUser;
+ final int mUserId;
final Context mContext;
+ final AccessibilitySettingsListener mAccessibilitySettingsListener;
volatile HotwordDetectionServiceIdentity mIdentity;
//TODO: Consider rename this to SandboxedDetectionIdentity
private Instant mLastRestartInstant;
@@ -204,6 +206,27 @@
}
};
+ /** Listen to changes of {@link Settings.Secure.VISUAL_QUERY_ACCESSIBILITY_DETECTION_ENABLED}.
+ *
+ * This is registered to the {@link VoiceInteractionManagerServiceImpl} where all settings
+ * listeners are centralized and notified.
+ */
+ private final class AccessibilitySettingsListener extends
+ IVoiceInteractionAccessibilitySettingsListener.Stub {
+ @Override
+ public void onAccessibilityDetectionChanged(boolean enable) {
+ synchronized (mLock) {
+ if (DEBUG) {
+ Slog.d(TAG, "Update settings change: " + enable);
+ }
+ VisualQueryDetectorSession session = getVisualQueryDetectorSessionLocked();
+ if (session != null) {
+ session.updateAccessibilityEgressStateLocked(enable);
+ }
+ }
+ }
+ }
+
HotwordDetectionConnection(Object lock, Context context, int voiceInteractionServiceUid,
Identity voiceInteractorIdentity, ComponentName hotwordDetectionServiceName,
@@ -216,11 +239,12 @@
mVoiceInteractorIdentity = voiceInteractorIdentity;
mHotwordDetectionComponentName = hotwordDetectionServiceName;
mVisualQueryDetectionComponentName = visualQueryDetectionServiceName;
- mUser = userId;
+ mUserId = userId;
mDetectorType = detectorType;
mRemoteExceptionListener = listener;
mReStartPeriodSeconds = DeviceConfig.getInt(DeviceConfig.NAMESPACE_VOICE_INTERACTION,
KEY_RESTART_PERIOD_IN_SECONDS, 0);
+ mAccessibilitySettingsListener = new AccessibilitySettingsListener();
final Intent hotwordDetectionServiceIntent =
new Intent(HotwordDetectionService.SERVICE_INTERFACE);
@@ -792,7 +816,7 @@
ServiceConnection createLocked() {
ServiceConnection connection =
- new ServiceConnection(mContext, mIntent, mBindingFlags, mUser,
+ new ServiceConnection(mContext, mIntent, mBindingFlags, mUserId,
ISandboxedDetectionService.Stub::asInterface,
mRestartCount % MAX_ISOLATED_PROCESS_NUMBER, mDetectionServiceType);
connection.connect();
@@ -998,7 +1022,7 @@
session = new DspTrustedHotwordDetectorSession(mRemoteHotwordDetectionService,
mLock, mContext, token, callback, mVoiceInteractionServiceUid,
mVoiceInteractorIdentity, mScheduledExecutorService, mDebugHotwordLogging,
- mRemoteExceptionListener);
+ mRemoteExceptionListener, mUserId);
} else if (detectorType == HotwordDetector.DETECTOR_TYPE_VISUAL_QUERY_DETECTOR) {
if (mRemoteVisualQueryDetectionService == null) {
mRemoteVisualQueryDetectionService =
@@ -1007,7 +1031,8 @@
session = new VisualQueryDetectorSession(
mRemoteVisualQueryDetectionService, mLock, mContext, token, callback,
mVoiceInteractionServiceUid, mVoiceInteractorIdentity,
- mScheduledExecutorService, mDebugHotwordLogging, mRemoteExceptionListener);
+ mScheduledExecutorService, mDebugHotwordLogging, mRemoteExceptionListener,
+ mUserId);
} else {
if (mRemoteHotwordDetectionService == null) {
mRemoteHotwordDetectionService =
@@ -1016,7 +1041,8 @@
session = new SoftwareTrustedHotwordDetectorSession(
mRemoteHotwordDetectionService, mLock, mContext, token, callback,
mVoiceInteractionServiceUid, mVoiceInteractorIdentity,
- mScheduledExecutorService, mDebugHotwordLogging, mRemoteExceptionListener);
+ mScheduledExecutorService, mDebugHotwordLogging,
+ mRemoteExceptionListener, mUserId);
}
mHotwordRecognitionCallback = callback;
mDetectorSessions.put(detectorType, session);
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/SoftwareTrustedHotwordDetectorSession.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/SoftwareTrustedHotwordDetectorSession.java
index f06c997..120c161 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/SoftwareTrustedHotwordDetectorSession.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/SoftwareTrustedHotwordDetectorSession.java
@@ -74,10 +74,10 @@
@NonNull IHotwordRecognitionStatusCallback callback, int voiceInteractionServiceUid,
Identity voiceInteractorIdentity,
@NonNull ScheduledExecutorService scheduledExecutorService, boolean logging,
- @NonNull DetectorRemoteExceptionListener listener) {
+ @NonNull DetectorRemoteExceptionListener listener, int userId) {
super(remoteHotwordDetectionService, lock, context, token, callback,
voiceInteractionServiceUid, voiceInteractorIdentity, scheduledExecutorService,
- logging, listener);
+ logging, listener, userId);
}
@SuppressWarnings("GuardedBy")
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VisualQueryDetectorSession.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VisualQueryDetectorSession.java
index e4ac993..aef8e6f 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VisualQueryDetectorSession.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VisualQueryDetectorSession.java
@@ -29,6 +29,7 @@
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.SharedMemory;
+import android.provider.Settings;
import android.service.voice.IDetectorSessionVisualQueryDetectionCallback;
import android.service.voice.IMicrophoneHotwordDetectionVoiceInteractionCallback;
import android.service.voice.ISandboxedDetectionService;
@@ -60,6 +61,7 @@
private IVisualQueryDetectionAttentionListener mAttentionListener;
private boolean mEgressingData;
private boolean mQueryStreaming;
+ private boolean mEnableAccessibilityDataEgress;
//TODO(b/261783819): Determines actual functionalities, e.g., startRecognition etc.
VisualQueryDetectorSession(
@@ -68,13 +70,17 @@
@NonNull IHotwordRecognitionStatusCallback callback, int voiceInteractionServiceUid,
Identity voiceInteractorIdentity,
@NonNull ScheduledExecutorService scheduledExecutorService, boolean logging,
- @NonNull DetectorRemoteExceptionListener listener) {
+ @NonNull DetectorRemoteExceptionListener listener, int userId) {
super(remoteService, lock, context, token, callback,
voiceInteractionServiceUid, voiceInteractorIdentity, scheduledExecutorService,
- logging, listener);
+ logging, listener, userId);
mEgressingData = false;
mQueryStreaming = false;
mAttentionListener = null;
+ mEnableAccessibilityDataEgress = Settings.Secure.getIntForUser(
+ mContext.getContentResolver(),
+ Settings.Secure.VISUAL_QUERY_ACCESSIBILITY_DETECTION_ENABLED, 0,
+ mUserId) == 1;
// TODO: handle notify RemoteException to client
}
@@ -186,6 +192,16 @@
"Cannot stream results without attention signals."));
return;
}
+ if (!checkDetectedResultDataLocked(partialResult)) {
+ Slog.v(TAG, "Accessibility data can be egressed only when the "
+ + "isAccessibilityDetectionEnabled() is true.");
+ callback.onVisualQueryDetectionServiceFailure(
+ new VisualQueryDetectionServiceFailure(
+ ERROR_CODE_ILLEGAL_STREAMING_STATE,
+ "Cannot stream accessibility data without "
+ + "enabling the setting."));
+ return;
+ }
mQueryStreaming = true;
callback.onResultDetected(partialResult);
Slog.i(TAG, "Egressed from visual query detection process.");
@@ -227,6 +243,12 @@
mQueryStreaming = false;
}
}
+
+ @SuppressWarnings("GuardedBy")
+ private boolean checkDetectedResultDataLocked(VisualQueryDetectedResult result) {
+ return result.getAccessibilityDetectionData() == null
+ || mEnableAccessibilityDataEgress;
+ }
};
return mRemoteDetectionService.run(
service -> service.detectWithVisualSignals(internalCallback));
@@ -251,6 +273,12 @@
+ " should not be called from VisualQueryDetectorSession.");
}
+ void updateAccessibilityEgressStateLocked(boolean enable) {
+ if (DEBUG) {
+ Slog.d(TAG, "updateAccessibilityEgressStateLocked");
+ }
+ mEnableAccessibilityDataEgress = enable;
+ }
@SuppressWarnings("GuardedBy")
public void dumpLocked(String prefix, PrintWriter pw) {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index ecb0f96..889f842 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -91,6 +91,7 @@
import com.android.internal.app.IVisualQueryDetectionAttentionListener;
import com.android.internal.app.IVisualQueryRecognitionStatusListener;
import com.android.internal.app.IVoiceActionCheckCallback;
+import com.android.internal.app.IVoiceInteractionAccessibilitySettingsListener;
import com.android.internal.app.IVoiceInteractionManagerService;
import com.android.internal.app.IVoiceInteractionSessionListener;
import com.android.internal.app.IVoiceInteractionSessionShowCallback;
@@ -2179,6 +2180,44 @@
}
}
+ public boolean getAccessibilityDetectionEnabled() {
+ synchronized (this) {
+ if (mImpl == null) {
+ Slog.w(TAG, "registerAccessibilityDetectionSettingsListener called without"
+ + " running voice interaction service");
+ return false;
+ }
+ return mImpl.getAccessibilityDetectionEnabled();
+ }
+ }
+
+ @Override
+ public void registerAccessibilityDetectionSettingsListener(
+ IVoiceInteractionAccessibilitySettingsListener listener) {
+ synchronized (this) {
+ if (mImpl == null) {
+ Slog.w(TAG, "registerAccessibilityDetectionSettingsListener called without"
+ + " running voice interaction service");
+ return;
+ }
+ mImpl.registerAccessibilityDetectionSettingsListenerLocked(listener);
+ }
+ }
+
+ @Override
+ public void unregisterAccessibilityDetectionSettingsListener(
+ IVoiceInteractionAccessibilitySettingsListener listener) {
+ synchronized (this) {
+ if (mImpl == null) {
+ Slog.w(TAG, "unregisterAccessibilityDetectionSettingsListener called "
+ + "without running voice interaction service");
+ return;
+ }
+ mImpl.unregisterAccessibilityDetectionSettingsListenerLocked(listener);
+ }
+ }
+
+
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index 84b36d5..e34e819 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -36,6 +36,7 @@
import android.app.IActivityTaskManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -45,10 +46,12 @@
import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
import android.content.pm.ServiceInfo;
+import android.database.ContentObserver;
import android.hardware.soundtrigger.IRecognitionStatusCallback;
import android.hardware.soundtrigger.SoundTrigger;
import android.media.AudioFormat;
import android.media.permission.Identity;
+import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -60,6 +63,7 @@
import android.os.SharedMemory;
import android.os.SystemProperties;
import android.os.UserHandle;
+import android.provider.Settings;
import android.service.voice.HotwordDetector;
import android.service.voice.IMicrophoneHotwordDetectionVoiceInteractionCallback;
import android.service.voice.IVisualQueryDetectionVoiceInteractionCallback;
@@ -77,6 +81,7 @@
import com.android.internal.app.IHotwordRecognitionStatusCallback;
import com.android.internal.app.IVisualQueryDetectionAttentionListener;
import com.android.internal.app.IVoiceActionCheckCallback;
+import com.android.internal.app.IVoiceInteractionAccessibilitySettingsListener;
import com.android.internal.app.IVoiceInteractionSessionShowCallback;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.util.function.pooled.PooledLambda;
@@ -199,6 +204,10 @@
}
};
+ final ArrayList<
+ IVoiceInteractionAccessibilitySettingsListener> mAccessibilitySettingsListeners =
+ new ArrayList<IVoiceInteractionAccessibilitySettingsListener>();
+
VoiceInteractionManagerServiceImpl(Context context, Handler handler,
VoiceInteractionManagerService.VoiceInteractionManagerServiceStub stub,
int userHandle, ComponentName service) {
@@ -250,6 +259,7 @@
filter.addAction(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
mContext.registerReceiver(mBroadcastReceiver, filter, null, handler,
Context.RECEIVER_EXPORTED);
+ new AccessibilitySettingsContentObserver().register(mContext.getContentResolver());
}
public void grantImplicitAccessLocked(int grantRecipientUid, @Nullable Intent intent) {
@@ -745,6 +755,8 @@
+ "exception occurred.");
}
});
+ registerAccessibilityDetectionSettingsListenerLocked(
+ mHotwordDetectionConnection.mAccessibilitySettingsListener);
} else if (detectorType != HotwordDetector.DETECTOR_TYPE_VISUAL_QUERY_DETECTOR) {
// TODO: Logger events should be handled in session instead. Temporary adding the
// checking to prevent confusion so VisualQueryDetection events won't be logged if the
@@ -782,6 +794,8 @@
return;
}
mHotwordDetectionConnection.cancelLocked();
+ unregisterAccessibilityDetectionSettingsListenerLocked(
+ mHotwordDetectionConnection.mAccessibilitySettingsListener);
mHotwordDetectionConnection = null;
}
@@ -974,6 +988,8 @@
return;
}
mHotwordDetectionConnection.cancelLocked();
+ unregisterAccessibilityDetectionSettingsListenerLocked(
+ mHotwordDetectionConnection.mAccessibilitySettingsListener);
mHotwordDetectionConnection = null;
}
@@ -1015,6 +1031,29 @@
}
}
+ boolean getAccessibilityDetectionEnabled() {
+ return Settings.Secure.getIntForUser(
+ mContext.getContentResolver(),
+ Settings.Secure.VISUAL_QUERY_ACCESSIBILITY_DETECTION_ENABLED, 0,
+ mUser) == 1;
+ }
+
+ void registerAccessibilityDetectionSettingsListenerLocked(
+ IVoiceInteractionAccessibilitySettingsListener listener) {
+ if (DEBUG) {
+ Slog.d(TAG, "registerAccessibilityDetectionSettingsListener");
+ }
+ mAccessibilitySettingsListeners.add(listener);
+ }
+
+ void unregisterAccessibilityDetectionSettingsListenerLocked(
+ IVoiceInteractionAccessibilitySettingsListener listener) {
+ if (DEBUG) {
+ Slog.d(TAG, "unregisterAccessibilityDetectionSettingsListener");
+ }
+ mAccessibilitySettingsListeners.remove(listener);
+ }
+
void startLocked() {
Intent intent = new Intent(VoiceInteractionService.SERVICE_INTERFACE);
intent.setComponent(mComponent);
@@ -1055,6 +1094,8 @@
}
if (mHotwordDetectionConnection != null) {
mHotwordDetectionConnection.cancelLocked();
+ unregisterAccessibilityDetectionSettingsListenerLocked(
+ mHotwordDetectionConnection.mAccessibilitySettingsListener);
mHotwordDetectionConnection = null;
}
if (mBound) {
@@ -1101,4 +1142,41 @@
interface DetectorRemoteExceptionListener {
void onDetectorRemoteException(@NonNull IBinder token, int detectorType);
}
+
+ private final class AccessibilitySettingsContentObserver extends ContentObserver {
+ private Uri mAccessibilitySettingsEnabledUri = Settings.Secure.getUriFor(
+ Settings.Secure.VISUAL_QUERY_ACCESSIBILITY_DETECTION_ENABLED);
+
+ AccessibilitySettingsContentObserver() {
+ super(null);
+ }
+
+ public void register(ContentResolver contentResolver) {
+ contentResolver.registerContentObserver(
+ mAccessibilitySettingsEnabledUri, false, this, UserHandle.USER_ALL);
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ Slog.i(TAG, "OnChange called with uri:" + uri);
+ if (mAccessibilitySettingsEnabledUri.equals(uri)) {
+ boolean enable = Settings.Secure.getIntForUser(
+ mContext.getContentResolver(),
+ Settings.Secure.VISUAL_QUERY_ACCESSIBILITY_DETECTION_ENABLED, 0,
+ mUser) == 1;
+ Slog.i(TAG, "Notifying listeners with Accessibility setting set to "
+ + enable);
+ mAccessibilitySettingsListeners.forEach(
+ listener -> {
+ try {
+ listener.onAccessibilityDetectionChanged(enable);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+ );
+
+ }
+ }
+ }
}
diff --git a/telecomm/java/Android.bp b/telecomm/java/Android.bp
index 3bd5953..9e9dd7d 100644
--- a/telecomm/java/Android.bp
+++ b/telecomm/java/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_fwk_telephony",
// 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"
diff --git a/telecomm/java/android/telecom/CallAudioState.java b/telecomm/java/android/telecom/CallAudioState.java
index c7cc1bd..49e9232 100644
--- a/telecomm/java/android/telecom/CallAudioState.java
+++ b/telecomm/java/android/telecom/CallAudioState.java
@@ -199,6 +199,16 @@
}
/**
+ * @return Bit mask of all routes supported by this call, won't be changed by streaming state.
+ *
+ * @hide
+ */
+ @CallAudioRoute
+ public int getRawSupportedRouteMask() {
+ return supportedRouteMask;
+ }
+
+ /**
* @return The {@link BluetoothDevice} through which audio is being routed.
* Will not be {@code null} if {@link #getRoute()} returns {@link #ROUTE_BLUETOOTH}.
*/
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 15a978d..08c76af 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -1434,6 +1434,31 @@
}
/**
+ * This API will return all {@link PhoneAccount}s registered via
+ * {@link TelecomManager#registerPhoneAccount(PhoneAccount)}. If a {@link PhoneAccount} appears
+ * to be missing from the list, Telecom has either unregistered the {@link PhoneAccount}
+ * or the caller registered the {@link PhoneAccount} under a different user and does not
+ * have the {@link android.Manifest.permission#INTERACT_ACROSS_USERS} permission.
+ *
+ * @return all the {@link PhoneAccount}s registered by the caller.
+ */
+ @SuppressLint("RequiresPermission")
+ @FlaggedApi(Flags.FLAG_GET_REGISTERED_PHONE_ACCOUNTS)
+ public @NonNull List<PhoneAccount> getRegisteredPhoneAccounts() {
+ ITelecomService service = getTelecomService();
+ if (service != null) {
+ try {
+ return service.getRegisteredPhoneAccounts(
+ mContext.getOpPackageName(),
+ mContext.getAttributionTag()).getList();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ throw new IllegalStateException("Telecom is not available");
+ }
+
+ /**
* Returns a list of {@link PhoneAccountHandle}s including those which have not been enabled
* by the user.
*
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index 7dba799e..302a472 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -93,6 +93,12 @@
PhoneAccount getPhoneAccount(in PhoneAccountHandle account, String callingPackage);
/**
+ * @see TelecomManager#getPhoneAccount
+ */
+ ParceledListSlice<PhoneAccount> getRegisteredPhoneAccounts(String callingPackage,
+ String callingFeatureId);
+
+ /**
* @see TelecomManager#getAllPhoneAccountsCount
*/
int getAllPhoneAccountsCount();
diff --git a/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java b/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
index aed8fb8..a63db88 100644
--- a/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
+++ b/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java
@@ -32,6 +32,8 @@
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
+import android.provider.Telephony;
+import android.provider.Telephony.Carriers.EditStatus;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyFrameworkInitializer;
import android.telephony.TelephonyManager;
@@ -226,6 +228,23 @@
}
/**
+ * Convert APN edited status to string.
+ *
+ * @param apnEditStatus APN edited status.
+ * @return APN edited status in string format.
+ */
+ public static @NonNull String apnEditedStatusToString(@EditStatus int apnEditStatus) {
+ return switch (apnEditStatus) {
+ case Telephony.Carriers.UNEDITED -> "UNEDITED";
+ case Telephony.Carriers.USER_EDITED -> "USER_EDITED";
+ case Telephony.Carriers.USER_DELETED -> "USER_DELETED";
+ case Telephony.Carriers.CARRIER_EDITED -> "CARRIER_EDITED";
+ case Telephony.Carriers.CARRIER_DELETED -> "CARRIER_DELETED";
+ default -> "UNKNOWN(" + apnEditStatus + ")";
+ };
+ }
+
+ /**
* Utility method to get user handle associated with this subscription.
*
* This method should be used internally as it returns null instead of throwing
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index d99abe8..5d99acd 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -4803,12 +4803,51 @@
*/
public static final String KEY_FCM_SENDER_ID_STRING = KEY_PREFIX + "fcm_sender_id_string";
+ /**
+ * Indicates the supported protocol version in the parameter entitlement_version.
+ * The default value is 2. The possible value is 2 and 8.
+ *
+ * Reference: GSMA TS.43-v8 section 2.5 Protocol version control and
+ * Table 3. GET Parameters for Entitlement Configuration in section 2.3
+ * HTTP GET method Parameters.
+ * @hide
+ */
+ public static final String KEY_ENTITLEMENT_VERSION_INT =
+ KEY_PREFIX + "entitlement_version_int";
+
+ /**
+ * Controls the service entitlement status when receiving the VERS characteristic
+ * with both version and validity set to -1 or -2.
+ * If {@code true}, default service entitlement status is enabled.
+ * If {@code false}, default service entitlement status is disabled.
+ *
+ * Reference: GSMA TS.14-v8 section 2.1, overview
+ * @hide
+ */
+ public static final String KEY_DEFAULT_SERVICE_ENTITLEMENT_STATUS_BOOL =
+ KEY_PREFIX + "default_service_entitlement_status_bool";
+
+ /**
+ * Indicates if UE can skip service entitlement check when the user turns on Wi-Fi Calling.
+ * UE still shows Wi-Fi Calling emergency address update web view when the user clicks
+ * "Update Emergency Address" on the WiFi calling setting.
+ *
+ * Note: this is effective only if the {@link #KEY_WFC_EMERGENCY_ADDRESS_CARRIER_APP_STRING}
+ * is set to this app.
+ * @hide
+ */
+ public static final String KEY_SKIP_WFC_ACTIVATION_BOOL =
+ KEY_PREFIX + "skip_wfc_activation_bool";
+
private static PersistableBundle getDefaults() {
PersistableBundle defaults = new PersistableBundle();
defaults.putString(KEY_ENTITLEMENT_SERVER_URL_STRING, "");
defaults.putString(KEY_FCM_SENDER_ID_STRING, "");
defaults.putBoolean(KEY_SHOW_VOWIFI_WEBVIEW_BOOL, false);
defaults.putBoolean(KEY_IMS_PROVISIONING_BOOL, false);
+ defaults.putBoolean(KEY_DEFAULT_SERVICE_ENTITLEMENT_STATUS_BOOL, false);
+ defaults.putBoolean(KEY_SKIP_WFC_ACTIVATION_BOOL, false);
+ defaults.putInt(KEY_ENTITLEMENT_VERSION_INT, 2);
return defaults;
}
}
diff --git a/telephony/java/android/telephony/data/ApnSetting.java b/telephony/java/android/telephony/data/ApnSetting.java
index 8679bd4..44d3fca 100644
--- a/telephony/java/android/telephony/data/ApnSetting.java
+++ b/telephony/java/android/telephony/data/ApnSetting.java
@@ -29,6 +29,7 @@
import android.os.Parcelable;
import android.provider.Telephony;
import android.provider.Telephony.Carriers;
+import android.provider.Telephony.Carriers.EditStatus;
import android.telephony.Annotation.NetworkType;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
@@ -37,6 +38,7 @@
import android.util.Log;
import com.android.internal.telephony.flags.Flags;
+import com.android.internal.telephony.util.TelephonyUtils;
import com.android.telephony.Rlog;
import java.lang.annotation.Retention;
@@ -571,6 +573,13 @@
private final boolean mEsimBootstrapProvisioning;
/**
+ * The APN edited status.
+ *
+ * Note it is intended not using this field for {@link #equals(Object)} or {@link #hashCode()}.
+ */
+ private final @EditStatus int mEditedStatus;
+
+ /**
* Returns the default MTU (Maximum Transmission Unit) size in bytes of the IPv4 routes brought
* up by this APN setting. Note this value will only be used when MTU size is not provided
* in {@code DataCallResponse#getMtuV4()} during network bring up.
@@ -992,6 +1001,22 @@
return mEsimBootstrapProvisioning;
}
+ /**
+ * @return APN edited status. APN could be added/edited/deleted by a user or carrier.
+ *
+ * @see Carriers#UNEDITED
+ * @see Carriers#USER_EDITED
+ * @see Carriers#USER_DELETED
+ * @see Carriers#CARRIER_EDITED
+ * @see Carriers#CARRIER_DELETED
+ *
+ * @hide
+ */
+ @EditStatus
+ public int getEditedStatus() {
+ return mEditedStatus;
+ }
+
private ApnSetting(Builder builder) {
this.mEntryName = builder.mEntryName;
this.mApnName = builder.mApnName;
@@ -1030,6 +1055,7 @@
this.mAlwaysOn = builder.mAlwaysOn;
this.mInfrastructureBitmask = builder.mInfrastructureBitmask;
this.mEsimBootstrapProvisioning = builder.mEsimBootstrapProvisioning;
+ this.mEditedStatus = builder.mEditedStatus;
}
/**
@@ -1113,6 +1139,8 @@
Telephony.Carriers.INFRASTRUCTURE_BITMASK)))
.setEsimBootstrapProvisioning(cursor.getInt(
cursor.getColumnIndexOrThrow(Carriers.ESIM_BOOTSTRAP_PROVISIONING)) == 1)
+ .setEditedStatus(cursor.getInt(
+ cursor.getColumnIndexOrThrow(Carriers.EDITED_STATUS)))
.buildWithoutCheck();
}
@@ -1154,6 +1182,7 @@
.setAlwaysOn(apn.mAlwaysOn)
.setInfrastructureBitmask(apn.mInfrastructureBitmask)
.setEsimBootstrapProvisioning(apn.mEsimBootstrapProvisioning)
+ .setEditedStatus(apn.mEditedStatus)
.buildWithoutCheck();
}
@@ -1202,6 +1231,7 @@
sb.append(", ").append(mInfrastructureBitmask);
sb.append(", ").append(Objects.hash(mUser, mPassword));
sb.append(", ").append(mEsimBootstrapProvisioning);
+ sb.append(", ").append(TelephonyUtils.apnEditedStatusToString(mEditedStatus));
return sb.toString();
}
@@ -1748,6 +1778,7 @@
dest.writeBoolean(mAlwaysOn);
dest.writeInt(mInfrastructureBitmask);
dest.writeBoolean(mEsimBootstrapProvisioning);
+ dest.writeInt(mEditedStatus);
}
private static ApnSetting readFromParcel(Parcel in) {
@@ -1785,6 +1816,7 @@
.setAlwaysOn(in.readBoolean())
.setInfrastructureBitmask(in.readInt())
.setEsimBootstrapProvisioning(in.readBoolean())
+ .setEditedStatus(in.readInt())
.buildWithoutCheck();
}
@@ -1868,6 +1900,7 @@
private boolean mAlwaysOn;
private int mInfrastructureBitmask = INFRASTRUCTURE_CELLULAR | INFRASTRUCTURE_SATELLITE;
private boolean mEsimBootstrapProvisioning;
+ private @EditStatus int mEditedStatus = Carriers.UNEDITED;
/**
* Default constructor for Builder.
@@ -2310,6 +2343,8 @@
*
* @param esimBootstrapProvisioning {@code true} if the APN is used for eSIM bootstrap
* provisioning, {@code false} otherwise.
+ *
+ * @return The builder.
* @hide
*/
@NonNull
@@ -2319,6 +2354,26 @@
}
/**
+ * Set the edited status. APN could be added/edited/deleted by a user or carrier.
+ *
+ * @param editedStatus The APN edited status
+ * @return The builder.
+ *
+ * @see Carriers#UNEDITED
+ * @see Carriers#USER_EDITED
+ * @see Carriers#USER_DELETED
+ * @see Carriers#CARRIER_EDITED
+ * @see Carriers#CARRIER_DELETED
+ *
+ * @hide
+ */
+ @NonNull
+ public Builder setEditedStatus(@EditStatus int editedStatus) {
+ this.mEditedStatus = editedStatus;
+ return this;
+ }
+
+ /**
* Builds {@link ApnSetting} from this builder.
*
* @return {@code null} if {@link #setApnName(String)} or {@link #setEntryName(String)}
diff --git a/telephony/java/android/telephony/emergency/EmergencyNumber.java b/telephony/java/android/telephony/emergency/EmergencyNumber.java
index b429407..d44a43e 100644
--- a/telephony/java/android/telephony/emergency/EmergencyNumber.java
+++ b/telephony/java/android/telephony/emergency/EmergencyNumber.java
@@ -37,6 +37,7 @@
import java.util.List;
import java.util.Objects;
import java.util.Set;
+import java.util.stream.Collectors;
/**
* A parcelable class that wraps and retrieves the information of number, service category(s) and
@@ -300,8 +301,8 @@
dest.writeInt(mEmergencyCallRouting);
}
- public static final @android.annotation.NonNull Parcelable.Creator<EmergencyNumber> CREATOR =
- new Parcelable.Creator<EmergencyNumber>() {
+ public static final @NonNull Creator<EmergencyNumber> CREATOR =
+ new Creator<EmergencyNumber>() {
@Override
public EmergencyNumber createFromParcel(Parcel in) {
return new EmergencyNumber(in);
@@ -500,12 +501,94 @@
@Override
public String toString() {
- return "EmergencyNumber:" + "Number-" + mNumber + "|CountryIso-" + mCountryIso
- + "|Mnc-" + mMnc
- + "|ServiceCategories-" + Integer.toBinaryString(mEmergencyServiceCategoryBitmask)
- + "|Urns-" + mEmergencyUrns
- + "|Sources-" + Integer.toBinaryString(mEmergencyNumberSourceBitmask)
- + "|Routing-" + Integer.toBinaryString(mEmergencyCallRouting);
+ return String.format("[EmergencyNumber: %s, countryIso=%s, mnc=%s, src=%s, routing=%s, "
+ + "categories=%s, urns=%s]",
+ mNumber,
+ mCountryIso,
+ mMnc,
+ sourceBitmaskToString(mEmergencyNumberSourceBitmask),
+ routingToString(mEmergencyCallRouting),
+ categoriesToString(mEmergencyServiceCategoryBitmask),
+ (mEmergencyUrns == null ? "" :
+ mEmergencyUrns.stream().collect(Collectors.joining(","))));
+ }
+
+ /**
+ * @param categories emergency service category bitmask
+ * @return loggable string describing the category bitmask
+ */
+ private String categoriesToString(@EmergencyServiceCategories int categories) {
+ StringBuilder sb = new StringBuilder();
+ if ((categories & EMERGENCY_SERVICE_CATEGORY_AIEC) == EMERGENCY_SERVICE_CATEGORY_AIEC) {
+ sb.append("auto ");
+ }
+ if ((categories & EMERGENCY_SERVICE_CATEGORY_AMBULANCE)
+ == EMERGENCY_SERVICE_CATEGORY_AMBULANCE) {
+ sb.append("ambulance ");
+ }
+ if ((categories & EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE)
+ == EMERGENCY_SERVICE_CATEGORY_FIRE_BRIGADE) {
+ sb.append("fire ");
+ }
+ if ((categories & EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD)
+ == EMERGENCY_SERVICE_CATEGORY_MARINE_GUARD) {
+ sb.append("marine ");
+ }
+ if ((categories & EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE)
+ == EMERGENCY_SERVICE_CATEGORY_MOUNTAIN_RESCUE) {
+ sb.append("mountain ");
+ }
+ if ((categories & EMERGENCY_SERVICE_CATEGORY_POLICE) == EMERGENCY_SERVICE_CATEGORY_POLICE) {
+ sb.append("police ");
+ }
+ if ((categories & EMERGENCY_SERVICE_CATEGORY_MIEC) == EMERGENCY_SERVICE_CATEGORY_MIEC) {
+ sb.append("manual ");
+ }
+ return sb.toString();
+ }
+
+ /**
+ * @param routing emergency call routing type
+ * @return loggable string describing the routing type.
+ */
+ private String routingToString(@EmergencyCallRouting int routing) {
+ return switch(routing) {
+ case EMERGENCY_CALL_ROUTING_EMERGENCY -> "emergency";
+ case EMERGENCY_CALL_ROUTING_NORMAL -> "normal";
+ case EMERGENCY_CALL_ROUTING_UNKNOWN -> "unknown";
+ default -> "🤷";
+ };
+ }
+
+ /**
+ * Builds a string describing the sources for an emergency number.
+ * @param sourceBitmask the source bitmask
+ * @return loggable string describing the sources.
+ */
+ private String sourceBitmaskToString(@EmergencyNumberSources int sourceBitmask) {
+ StringBuilder sb = new StringBuilder();
+ if ((sourceBitmask & EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING)
+ == EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING) {
+ sb.append("net ");
+ }
+ if ((sourceBitmask & EMERGENCY_NUMBER_SOURCE_SIM) == EMERGENCY_NUMBER_SOURCE_SIM) {
+ sb.append("sim ");
+ }
+ if ((sourceBitmask & EMERGENCY_NUMBER_SOURCE_DATABASE)
+ == EMERGENCY_NUMBER_SOURCE_DATABASE) {
+ sb.append("db ");
+ }
+ if ((sourceBitmask & EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG)
+ == EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG) {
+ sb.append("mdm ");
+ }
+ if ((sourceBitmask & EMERGENCY_NUMBER_SOURCE_DEFAULT) == EMERGENCY_NUMBER_SOURCE_DEFAULT) {
+ sb.append("def ");
+ }
+ if ((sourceBitmask & EMERGENCY_NUMBER_SOURCE_TEST) == EMERGENCY_NUMBER_SOURCE_TEST) {
+ sb.append("tst ");
+ }
+ return sb.toString();
}
@Override
diff --git a/tests/BootImageProfileTest/AndroidTest.xml b/tests/BootImageProfileTest/AndroidTest.xml
index 7e97fa3..9b527dc 100644
--- a/tests/BootImageProfileTest/AndroidTest.xml
+++ b/tests/BootImageProfileTest/AndroidTest.xml
@@ -14,6 +14,7 @@
limitations under the License.
-->
<configuration description="Config for BootImageProfileTest">
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
<!-- do not use DeviceSetup#set-property because it reboots the device b/136200738.
furthermore the changes in /data/local.prop don't actually seem to get picked up.
-->
diff --git a/tests/EnforcePermission/Android.bp b/tests/EnforcePermission/Android.bp
index 719a898..6a5add0 100644
--- a/tests/EnforcePermission/Android.bp
+++ b/tests/EnforcePermission/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_responsible_apis",
default_applicable_licenses: ["frameworks_base_license"],
}
diff --git a/tests/EnforcePermission/perf-app/Android.bp b/tests/EnforcePermission/perf-app/Android.bp
index b494bb7..6d04fdc 100644
--- a/tests/EnforcePermission/perf-app/Android.bp
+++ b/tests/EnforcePermission/perf-app/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_responsible_apis",
default_applicable_licenses: ["frameworks_base_license"],
}
diff --git a/tests/EnforcePermission/service-app/Android.bp b/tests/EnforcePermission/service-app/Android.bp
index 7878215..8910f2a 100644
--- a/tests/EnforcePermission/service-app/Android.bp
+++ b/tests/EnforcePermission/service-app/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_responsible_apis",
// 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"
diff --git a/tests/EnforcePermission/test-app/Android.bp b/tests/EnforcePermission/test-app/Android.bp
index cd53854..065ab33 100644
--- a/tests/EnforcePermission/test-app/Android.bp
+++ b/tests/EnforcePermission/test-app/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_responsible_apis",
default_applicable_licenses: ["frameworks_base_license"],
}
diff --git a/tests/FlickerTests/AppClose/Android.bp b/tests/FlickerTests/AppClose/Android.bp
index 93fdd65..d14a178 100644
--- a/tests/FlickerTests/AppClose/Android.bp
+++ b/tests/FlickerTests/AppClose/Android.bp
@@ -15,6 +15,7 @@
//
package {
+ default_team: "trendy_team_windowing_animations_transitions",
// 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"
diff --git a/tests/FlickerTests/IME/Android.bp b/tests/FlickerTests/IME/Android.bp
index 1141e5f..b024471 100644
--- a/tests/FlickerTests/IME/Android.bp
+++ b/tests/FlickerTests/IME/Android.bp
@@ -15,6 +15,7 @@
//
package {
+ default_team: "trendy_team_input_method_framework",
// 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"
diff --git a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt
index 47a1619..99e8ef5 100644
--- a/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt
+++ b/tests/FlickerTests/IME/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTest.kt
@@ -85,7 +85,6 @@
val visibleAreas =
imeSnapshotLayers
.mapNotNull { imeSnapshotLayer -> imeSnapshotLayer.layer.visibleRegion }
- .toTypedArray()
val imeVisibleRegion = RegionSubject(visibleAreas, timestamp)
val appVisibleRegion = it.visibleRegion(imeTestApp)
if (imeVisibleRegion.region.isNotEmpty) {
diff --git a/tests/FlickerTests/Rotation/Android.bp b/tests/FlickerTests/Rotation/Android.bp
index 233a276..b3eb934 100644
--- a/tests/FlickerTests/Rotation/Android.bp
+++ b/tests/FlickerTests/Rotation/Android.bp
@@ -15,6 +15,7 @@
//
package {
+ default_team: "trendy_team_windowing_animations_transitions",
// 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"
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
index 4032121..1abb8c2 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
@@ -281,7 +281,6 @@
val visibleAreas =
snapshotLayers
.mapNotNull { snapshotLayer -> snapshotLayer.layer.visibleRegion }
- .toTypedArray()
val snapshotRegion = RegionSubject(visibleAreas, it.timestamp)
val appVisibleRegion = it.visibleRegion(component)
// Verify the size of snapshotRegion covers appVisibleRegion exactly in animation.
diff --git a/tests/FsVerityTest/Android.bp b/tests/FsVerityTest/Android.bp
index 53606a3..02268c3 100644
--- a/tests/FsVerityTest/Android.bp
+++ b/tests/FsVerityTest/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_platform_security",
// 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"
diff --git a/tests/FsVerityTest/FsVerityTestApp/Android.bp b/tests/FsVerityTest/FsVerityTestApp/Android.bp
index 43da3ff..71a7e4f 100644
--- a/tests/FsVerityTest/FsVerityTestApp/Android.bp
+++ b/tests/FsVerityTest/FsVerityTestApp/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_platform_security",
// 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"
diff --git a/tests/FsVerityTest/block_device_writer/Android.bp b/tests/FsVerityTest/block_device_writer/Android.bp
index 0002447d..2a337de 100644
--- a/tests/FsVerityTest/block_device_writer/Android.bp
+++ b/tests/FsVerityTest/block_device_writer/Android.bp
@@ -15,6 +15,7 @@
// This is a cc_test just because it supports test_suites. This should be converted to something
// like cc_binary_test_helper once supported, thus auto_gen_config:false below.
package {
+ default_team: "trendy_team_platform_security",
// 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"
diff --git a/tests/FsVerityTest/testdata/Android.bp b/tests/FsVerityTest/testdata/Android.bp
index 2d578d3..21b63e7 100644
--- a/tests/FsVerityTest/testdata/Android.bp
+++ b/tests/FsVerityTest/testdata/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_platform_security",
// 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"
diff --git a/tests/HandwritingIme/Android.bp b/tests/HandwritingIme/Android.bp
index 1f552bf..0d2422e 100644
--- a/tests/HandwritingIme/Android.bp
+++ b/tests/HandwritingIme/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_input_method_framework",
// 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"
diff --git a/tests/InputScreenshotTest/Android.bp b/tests/InputScreenshotTest/Android.bp
index 83ced2c..927b101 100644
--- a/tests/InputScreenshotTest/Android.bp
+++ b/tests/InputScreenshotTest/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_input_framework",
// 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"
diff --git a/tests/InputScreenshotTest/assets/phone/light_landscape_layout-preview.png b/tests/InputScreenshotTest/assets/phone/light_landscape_layout-preview.png
index 70e4a71..443de8e 100644
--- a/tests/InputScreenshotTest/assets/phone/light_landscape_layout-preview.png
+++ b/tests/InputScreenshotTest/assets/phone/light_landscape_layout-preview.png
Binary files differ
diff --git a/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-ansi.png b/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-ansi.png
index 502c1b4..cb69c0e 100644
--- a/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-ansi.png
+++ b/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-ansi.png
Binary files differ
diff --git a/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-jis.png b/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-jis.png
index 591b2fa..1c6d1b3 100644
--- a/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-jis.png
+++ b/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-jis.png
Binary files differ
diff --git a/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview.png b/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview.png
index 0137a85..c51da05 100644
--- a/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview.png
+++ b/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview.png
Binary files differ
diff --git a/tests/InputScreenshotTest/assets/tablet/dark_portrait_layout-preview.png b/tests/InputScreenshotTest/assets/tablet/dark_portrait_layout-preview.png
index 37a91e1..ab23401 100644
--- a/tests/InputScreenshotTest/assets/tablet/dark_portrait_layout-preview.png
+++ b/tests/InputScreenshotTest/assets/tablet/dark_portrait_layout-preview.png
Binary files differ
diff --git a/tests/InputScreenshotTest/robotests/Android.bp b/tests/InputScreenshotTest/robotests/Android.bp
index 912f4b80..384f58a 100644
--- a/tests/InputScreenshotTest/robotests/Android.bp
+++ b/tests/InputScreenshotTest/robotests/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_input_framework",
default_applicable_licenses: ["Android-Apache-2.0"],
}
diff --git a/tests/Internal/src/com/android/internal/protolog/OWNERS b/tests/Internal/src/com/android/internal/protolog/OWNERS
new file mode 100644
index 0000000..18cf2be
--- /dev/null
+++ b/tests/Internal/src/com/android/internal/protolog/OWNERS
@@ -0,0 +1,3 @@
+# ProtoLog owners
+# Bug component: 1157642
+include platform/development:/tools/winscope/OWNERS
diff --git a/tests/graphics/HwAccelerationTest/Android.bp b/tests/graphics/HwAccelerationTest/Android.bp
index 51848f2..d95a9b9 100644
--- a/tests/graphics/HwAccelerationTest/Android.bp
+++ b/tests/graphics/HwAccelerationTest/Android.bp
@@ -15,6 +15,7 @@
//
package {
+ default_team: "trendy_team_android_core_graphics_stack",
// 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"
diff --git a/tests/graphics/HwAccelerationTest/jni/Android.bp b/tests/graphics/HwAccelerationTest/jni/Android.bp
index 8edddab..76e4f9c 100644
--- a/tests/graphics/HwAccelerationTest/jni/Android.bp
+++ b/tests/graphics/HwAccelerationTest/jni/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_android_core_graphics_stack",
default_applicable_licenses: ["Android-Apache-2.0"],
}
diff --git a/tests/graphics/RenderThreadTest/Android.bp b/tests/graphics/RenderThreadTest/Android.bp
index b18b04e..d6d85e8 100644
--- a/tests/graphics/RenderThreadTest/Android.bp
+++ b/tests/graphics/RenderThreadTest/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_android_core_graphics_stack",
// 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"
diff --git a/tests/graphics/SilkFX/Android.bp b/tests/graphics/SilkFX/Android.bp
index 1e467db..b149cf3 100644
--- a/tests/graphics/SilkFX/Android.bp
+++ b/tests/graphics/SilkFX/Android.bp
@@ -15,6 +15,7 @@
//
package {
+ default_team: "trendy_team_android_core_graphics_stack",
// 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"
diff --git a/tests/graphics/VectorDrawableTest/Android.bp b/tests/graphics/VectorDrawableTest/Android.bp
index 9da7c5f..0042a43 100644
--- a/tests/graphics/VectorDrawableTest/Android.bp
+++ b/tests/graphics/VectorDrawableTest/Android.bp
@@ -15,6 +15,7 @@
//
package {
+ default_team: "trendy_team_android_gpu",
// 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"
diff --git a/tests/inputmethod/ConcurrentMultiSessionImeTest/Android.bp b/tests/inputmethod/ConcurrentMultiSessionImeTest/Android.bp
index afaa3f0..8d05a97 100644
--- a/tests/inputmethod/ConcurrentMultiSessionImeTest/Android.bp
+++ b/tests/inputmethod/ConcurrentMultiSessionImeTest/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_input_method_framework",
default_applicable_licenses: ["frameworks_base_license"],
}
diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/IpSecPacketLossDetectorTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/IpSecPacketLossDetectorTest.java
index 9daba6a..1d7be2f 100644
--- a/tests/vcn/java/com/android/server/vcn/routeselection/IpSecPacketLossDetectorTest.java
+++ b/tests/vcn/java/com/android/server/vcn/routeselection/IpSecPacketLossDetectorTest.java
@@ -144,7 +144,7 @@
mTestLooper.dispatchAll();
verify(mIpSecTransform)
- .getIpSecTransformState(any(), mTransformStateReceiverCaptor.capture());
+ .requestIpSecTransformState(any(), mTransformStateReceiverCaptor.capture());
return mTransformStateReceiverCaptor.getValue();
}
@@ -210,7 +210,7 @@
assertNull(mIpSecPacketLossDetector.getLastTransformState());
mTestLooper.moveTimeForward(POLL_IPSEC_STATE_INTERVAL_MS);
mTestLooper.dispatchAll();
- verify(newTransform).getIpSecTransformState(any(), any());
+ verify(newTransform).requestIpSecTransformState(any(), any());
}
@Test
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/SubclassFilter.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/SubclassFilter.kt
new file mode 100644
index 0000000..83e09bf
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/SubclassFilter.kt
@@ -0,0 +1,76 @@
+/*
+ * 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.hoststubgen.filters
+
+import com.android.hoststubgen.asm.ClassNodes
+import com.android.hoststubgen.asm.toJvmClassName
+
+/**
+ * Filter to apply a policy to classes extending or implementing a class,
+ * either directly or indirectly. (with a breadth first search.)
+ *
+ * The policy won't apply to the super class itself.
+ */
+class SubclassFilter(
+ private val classes: ClassNodes,
+ fallback: OutputFilter
+) : DelegatingFilter(fallback) {
+ private val mPolicies: MutableMap<String, FilterPolicyWithReason> = mutableMapOf()
+
+ /**
+ * Add a policy to all classes extending or implementing a class, either directly or indirectly.
+ */
+ fun addPolicy(superClassName: String, policy: FilterPolicyWithReason) {
+ mPolicies[superClassName.toJvmClassName()] = policy
+ }
+
+ override fun getPolicyForClass(className: String): FilterPolicyWithReason {
+ return findPolicyForClass(className) ?: super.getPolicyForClass(className)
+ }
+
+ /**
+ * Find a policy for a class with a breadth-first search.
+ */
+ private fun findPolicyForClass(className: String): FilterPolicyWithReason? {
+ val cn = classes.findClass(className) ?: return null
+
+ if (cn.superName == null) {
+ return null
+ }
+ // First, check the direct super class / interfaces.
+ mPolicies[cn.superName]?.let { policy ->
+ return policy
+ }
+ cn.interfaces?.forEach { iface ->
+ mPolicies[iface]?.let { policy ->
+ return policy
+ }
+ }
+
+ // Then recurse.
+ cn.superName?.let { superName ->
+ findPolicyForClass(superName)?.let { policy ->
+ return policy
+ }
+ }
+ cn.interfaces?.forEach { iface ->
+ findPolicyForClass(iface)?.let { policy ->
+ return policy
+ }
+ }
+ return null
+ }
+}
\ No newline at end of file
diff --git a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
index 6ad83fb..75b5fc8 100644
--- a/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
+++ b/tools/hoststubgen/hoststubgen/src/com/android/hoststubgen/filters/TextFileFilterPolicyParser.kt
@@ -58,7 +58,8 @@
): OutputFilter {
log.i("Loading offloaded annotations from $filename ...")
log.withIndent {
- val imf = InMemoryOutputFilter(classes, fallback)
+ val subclassFilter = SubclassFilter(classes, fallback)
+ val imf = InMemoryOutputFilter(classes, subclassFilter)
var lineNo = 0
@@ -94,6 +95,10 @@
}
className = fields[1]
+ // superClass is set when the class name starts with a "*".
+ val superClass = resolveExtendingClass(className)
+
+ // :aidl, etc?
val classType = resolveSpecialClass(className)
if (fields[2].startsWith("!")) {
@@ -124,8 +129,14 @@
when (classType) {
SpecialClass.NotSpecial -> {
// TODO: Duplicate check, etc
- imf.setPolicyForClass(
- className, policy.withReason(FILTER_REASON))
+ if (superClass == null) {
+ imf.setPolicyForClass(
+ className, policy.withReason(FILTER_REASON)
+ )
+ } else {
+ subclassFilter.addPolicy(superClass,
+ policy.withReason("extends $superClass"))
+ }
}
SpecialClass.Aidl -> {
if (aidlPolicy != null) {
@@ -243,6 +254,13 @@
throw ParseException("Invalid special class name \"$className\"")
}
+private fun resolveExtendingClass(className: String): String? {
+ if (!className.startsWith("*")) {
+ return null
+ }
+ return className.substring(1)
+}
+
private fun parsePolicy(s: String): FilterPolicy {
return when (s.lowercase()) {
"s", "stub" -> FilterPolicy.Stub
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
index 70f56ae..78f277e 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/01-hoststubgen-test-tiny-framework-orig-dump.txt
@@ -2589,6 +2589,567 @@
RuntimeInvisibleAnnotations:
x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C1.class
+ Compiled from "C1.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.C1
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C1
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 1, attributes: 1
+ public com.android.hoststubgen.test.tinyframework.subclasstest.C1();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/subclasstest/C1;
+}
+SourceFile: "C1.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C2.class
+ Compiled from "C2.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.C2 extends com.android.hoststubgen.test.tinyframework.subclasstest.C1
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C2
+ super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C1
+ interfaces: 0, fields: 0, methods: 1, attributes: 1
+ public com.android.hoststubgen.test.tinyframework.subclasstest.C2();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/subclasstest/C1."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/subclasstest/C2;
+}
+SourceFile: "C2.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C3.class
+ Compiled from "C3.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.C3 extends com.android.hoststubgen.test.tinyframework.subclasstest.C2
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C3
+ super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C2
+ interfaces: 0, fields: 0, methods: 1, attributes: 1
+ public com.android.hoststubgen.test.tinyframework.subclasstest.C3();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/subclasstest/C2."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/subclasstest/C3;
+}
+SourceFile: "C3.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/CA.class
+ Compiled from "CA.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.CA
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/CA
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 1, attributes: 1
+ public com.android.hoststubgen.test.tinyframework.subclasstest.CA();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/subclasstest/CA;
+}
+SourceFile: "CA.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/CB.class
+ Compiled from "CB.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.CB
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/CB
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 1, attributes: 1
+ public com.android.hoststubgen.test.tinyframework.subclasstest.CB();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/subclasstest/CB;
+}
+SourceFile: "CB.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1.class
+ Compiled from "Class_C1.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_C1 extends com.android.hoststubgen.test.tinyframework.subclasstest.C1
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1
+ super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C1
+ interfaces: 0, fields: 0, methods: 1, attributes: 1
+ public com.android.hoststubgen.test.tinyframework.subclasstest.Class_C1();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/subclasstest/C1."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/subclasstest/Class_C1;
+}
+SourceFile: "Class_C1.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2.class
+ Compiled from "Class_C2.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_C2 extends com.android.hoststubgen.test.tinyframework.subclasstest.C2
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2
+ super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C2
+ interfaces: 0, fields: 0, methods: 1, attributes: 1
+ public com.android.hoststubgen.test.tinyframework.subclasstest.Class_C2();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/subclasstest/C2."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/subclasstest/Class_C2;
+}
+SourceFile: "Class_C2.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3.class
+ Compiled from "Class_C3.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_C3 extends com.android.hoststubgen.test.tinyframework.subclasstest.C3
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3
+ super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C3
+ interfaces: 0, fields: 0, methods: 1, attributes: 1
+ public com.android.hoststubgen.test.tinyframework.subclasstest.Class_C3();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/subclasstest/C3."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/subclasstest/Class_C3;
+}
+SourceFile: "Class_C3.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_CA.class
+ Compiled from "Class_CA.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_CA extends com.android.hoststubgen.test.tinyframework.subclasstest.CA
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_CA
+ super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/CA
+ interfaces: 0, fields: 0, methods: 1, attributes: 1
+ public com.android.hoststubgen.test.tinyframework.subclasstest.Class_CA();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/subclasstest/CA."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/subclasstest/Class_CA;
+}
+SourceFile: "Class_CA.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB.class
+ Compiled from "Class_CB.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_CB extends com.android.hoststubgen.test.tinyframework.subclasstest.CB
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB
+ super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/CB
+ interfaces: 0, fields: 0, methods: 1, attributes: 1
+ public com.android.hoststubgen.test.tinyframework.subclasstest.Class_CB();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/subclasstest/CB."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/subclasstest/Class_CB;
+}
+SourceFile: "Class_CB.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB_IA.class
+ Compiled from "Class_CB_IA.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_CB_IA extends com.android.hoststubgen.test.tinyframework.subclasstest.CB implements com.android.hoststubgen.test.tinyframework.subclasstest.IA
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB_IA
+ super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/CB
+ interfaces: 1, fields: 0, methods: 1, attributes: 1
+ public com.android.hoststubgen.test.tinyframework.subclasstest.Class_CB_IA();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method com/android/hoststubgen/test/tinyframework/subclasstest/CB."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/subclasstest/Class_CB_IA;
+}
+SourceFile: "Class_CB_IA.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1.class
+ Compiled from "Class_I1.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I1 implements com.android.hoststubgen.test.tinyframework.subclasstest.I1
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 1, attributes: 1
+ public com.android.hoststubgen.test.tinyframework.subclasstest.Class_I1();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/subclasstest/Class_I1;
+}
+SourceFile: "Class_I1.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA.class
+ Compiled from "Class_I1_IA.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I1_IA implements com.android.hoststubgen.test.tinyframework.subclasstest.I1,com.android.hoststubgen.test.tinyframework.subclasstest.IA
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA
+ super_class: #x // java/lang/Object
+ interfaces: 2, fields: 0, methods: 1, attributes: 1
+ public com.android.hoststubgen.test.tinyframework.subclasstest.Class_I1_IA();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA;
+}
+SourceFile: "Class_I1_IA.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2.class
+ Compiled from "Class_I2.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I2 implements com.android.hoststubgen.test.tinyframework.subclasstest.I2
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 1, attributes: 1
+ public com.android.hoststubgen.test.tinyframework.subclasstest.Class_I2();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/subclasstest/Class_I2;
+}
+SourceFile: "Class_I2.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3.class
+ Compiled from "Class_I3.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I3 implements com.android.hoststubgen.test.tinyframework.subclasstest.I3
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 1, attributes: 1
+ public com.android.hoststubgen.test.tinyframework.subclasstest.Class_I3();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/subclasstest/Class_I3;
+}
+SourceFile: "Class_I3.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3_IA.class
+ Compiled from "Class_I3_IA.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I3_IA implements com.android.hoststubgen.test.tinyframework.subclasstest.I3,com.android.hoststubgen.test.tinyframework.subclasstest.IA
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3_IA
+ super_class: #x // java/lang/Object
+ interfaces: 2, fields: 0, methods: 1, attributes: 1
+ public com.android.hoststubgen.test.tinyframework.subclasstest.Class_I3_IA();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/subclasstest/Class_I3_IA;
+}
+SourceFile: "Class_I3_IA.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA.class
+ Compiled from "Class_IA.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_IA implements com.android.hoststubgen.test.tinyframework.subclasstest.IA
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 1, attributes: 1
+ public com.android.hoststubgen.test.tinyframework.subclasstest.Class_IA();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/subclasstest/Class_IA;
+}
+SourceFile: "Class_IA.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I1.class
+ Compiled from "Class_IA_I1.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_IA_I1 implements com.android.hoststubgen.test.tinyframework.subclasstest.IA,com.android.hoststubgen.test.tinyframework.subclasstest.I1
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I1
+ super_class: #x // java/lang/Object
+ interfaces: 2, fields: 0, methods: 1, attributes: 1
+ public com.android.hoststubgen.test.tinyframework.subclasstest.Class_IA_I1();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I1;
+}
+SourceFile: "Class_IA_I1.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I3.class
+ Compiled from "Class_IA_I3.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_IA_I3 implements com.android.hoststubgen.test.tinyframework.subclasstest.IA,com.android.hoststubgen.test.tinyframework.subclasstest.I3
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I3
+ super_class: #x // java/lang/Object
+ interfaces: 2, fields: 0, methods: 1, attributes: 1
+ public com.android.hoststubgen.test.tinyframework.subclasstest.Class_IA_I3();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I3;
+}
+SourceFile: "Class_IA_I3.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB.class
+ Compiled from "Class_IB.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_IB implements com.android.hoststubgen.test.tinyframework.subclasstest.IB
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 1, attributes: 1
+ public com.android.hoststubgen.test.tinyframework.subclasstest.Class_IB();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/subclasstest/Class_IB;
+}
+SourceFile: "Class_IB.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB_IA.class
+ Compiled from "Class_IB_IA.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_IB_IA implements com.android.hoststubgen.test.tinyframework.subclasstest.IB,com.android.hoststubgen.test.tinyframework.subclasstest.IA
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB_IA
+ super_class: #x // java/lang/Object
+ interfaces: 2, fields: 0, methods: 1, attributes: 1
+ public com.android.hoststubgen.test.tinyframework.subclasstest.Class_IB_IA();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/subclasstest/Class_IB_IA;
+}
+SourceFile: "Class_IB_IA.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_None.class
+ Compiled from "Class_None.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_None
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_None
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 1, attributes: 1
+ public com.android.hoststubgen.test.tinyframework.subclasstest.Class_None();
+ descriptor: ()V
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=1, args_size=1
+ x: aload_0
+ x: invokespecial #x // Method java/lang/Object."<init>":()V
+ x: return
+ LineNumberTable:
+ LocalVariableTable:
+ Start Length Slot Name Signature
+ 0 5 0 this Lcom/android/hoststubgen/test/tinyframework/subclasstest/Class_None;
+}
+SourceFile: "Class_None.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I1.class
+ Compiled from "I1.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.I1
+ minor version: 0
+ major version: 61
+ flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/I1
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 0, attributes: 1
+}
+SourceFile: "I1.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I2.class
+ Compiled from "I2.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.I2 extends com.android.hoststubgen.test.tinyframework.subclasstest.I1
+ minor version: 0
+ major version: 61
+ flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/I2
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 0, attributes: 1
+}
+SourceFile: "I2.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I3.class
+ Compiled from "I3.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.I3 extends com.android.hoststubgen.test.tinyframework.subclasstest.I2
+ minor version: 0
+ major version: 61
+ flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/I3
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 0, attributes: 1
+}
+SourceFile: "I3.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IA.class
+ Compiled from "IA.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.IA
+ minor version: 0
+ major version: 61
+ flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/IA
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 0, attributes: 1
+}
+SourceFile: "IA.java"
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IB.class
+ Compiled from "IB.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.IB
+ minor version: 0
+ major version: 61
+ flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/IB
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 0, attributes: 1
+}
+SourceFile: "IB.java"
## Class: com/supported/UnsupportedClass.class
Compiled from "UnsupportedClass.java"
public class com.supported.UnsupportedClass
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
index b0db483..406cb74 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/02-hoststubgen-test-tiny-framework-host-stub-dump.txt
@@ -2055,6 +2055,166 @@
RuntimeInvisibleAnnotations:
x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C1.class
+ Compiled from "C1.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.C1
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C1
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "C1.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C2.class
+ Compiled from "C2.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.C2 extends com.android.hoststubgen.test.tinyframework.subclasstest.C1
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C2
+ super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C1
+ interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "C2.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C3.class
+ Compiled from "C3.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.C3 extends com.android.hoststubgen.test.tinyframework.subclasstest.C2
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C3
+ super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C2
+ interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "C3.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/CA.class
+ Compiled from "CA.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.CA
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/CA
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "CA.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/CB.class
+ Compiled from "CB.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.CB
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/CB
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "CB.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I1.class
+ Compiled from "I1.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.I1
+ minor version: 0
+ major version: 61
+ flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/I1
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "I1.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I2.class
+ Compiled from "I2.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.I2 extends com.android.hoststubgen.test.tinyframework.subclasstest.I1
+ minor version: 0
+ major version: 61
+ flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/I2
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "I2.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I3.class
+ Compiled from "I3.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.I3 extends com.android.hoststubgen.test.tinyframework.subclasstest.I2
+ minor version: 0
+ major version: 61
+ flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/I3
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "I3.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IA.class
+ Compiled from "IA.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.IA
+ minor version: 0
+ major version: 61
+ flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/IA
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "IA.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IB.class
+ Compiled from "IB.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.IB
+ minor version: 0
+ major version: 61
+ flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/IB
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "IB.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
## Class: com/unsupported/UnsupportedClass.class
Compiled from "UnsupportedClass.java"
public class com.unsupported.UnsupportedClass
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
index 112f69e..c673262 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/03-hoststubgen-test-tiny-framework-host-impl-dump.txt
@@ -3371,6 +3371,264 @@
RuntimeInvisibleAnnotations:
x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C1.class
+ Compiled from "C1.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.C1
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C1
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "C1.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C2.class
+ Compiled from "C2.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.C2 extends com.android.hoststubgen.test.tinyframework.subclasstest.C1
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C2
+ super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C1
+ interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "C2.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C3.class
+ Compiled from "C3.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.C3 extends com.android.hoststubgen.test.tinyframework.subclasstest.C2
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C3
+ super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C2
+ interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "C3.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/CA.class
+ Compiled from "CA.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.CA
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/CA
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "CA.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/CB.class
+ Compiled from "CB.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.CB
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/CB
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "CB.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1.class
+ Compiled from "Class_C1.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_C1 extends com.android.hoststubgen.test.tinyframework.subclasstest.C1
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1
+ super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C1
+ interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "Class_C1.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2.class
+ Compiled from "Class_C2.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_C2 extends com.android.hoststubgen.test.tinyframework.subclasstest.C2
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2
+ super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C2
+ interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "Class_C2.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3.class
+ Compiled from "Class_C3.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_C3 extends com.android.hoststubgen.test.tinyframework.subclasstest.C3
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3
+ super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C3
+ interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "Class_C3.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1.class
+ Compiled from "Class_I1.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I1 implements com.android.hoststubgen.test.tinyframework.subclasstest.I1
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "Class_I1.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA.class
+ Compiled from "Class_I1_IA.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I1_IA implements com.android.hoststubgen.test.tinyframework.subclasstest.I1,com.android.hoststubgen.test.tinyframework.subclasstest.IA
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA
+ super_class: #x // java/lang/Object
+ interfaces: 2, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "Class_I1_IA.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2.class
+ Compiled from "Class_I2.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I2 implements com.android.hoststubgen.test.tinyframework.subclasstest.I2
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "Class_I2.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3.class
+ Compiled from "Class_I3.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I3 implements com.android.hoststubgen.test.tinyframework.subclasstest.I3
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "Class_I3.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I1.class
+ Compiled from "I1.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.I1
+ minor version: 0
+ major version: 61
+ flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/I1
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "I1.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I2.class
+ Compiled from "I2.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.I2 extends com.android.hoststubgen.test.tinyframework.subclasstest.I1
+ minor version: 0
+ major version: 61
+ flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/I2
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "I2.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I3.class
+ Compiled from "I3.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.I3 extends com.android.hoststubgen.test.tinyframework.subclasstest.I2
+ minor version: 0
+ major version: 61
+ flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/I3
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "I3.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IA.class
+ Compiled from "IA.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.IA
+ minor version: 0
+ major version: 61
+ flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/IA
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "IA.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IB.class
+ Compiled from "IB.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.IB
+ minor version: 0
+ major version: 61
+ flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/IB
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "IB.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
## Class: com/supported/UnsupportedClass.class
Compiled from "UnsupportedClass.java"
public class com.supported.UnsupportedClass
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
index b0db483..406cb74 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/12-hoststubgen-test-tiny-framework-host-ext-stub-dump.txt
@@ -2055,6 +2055,166 @@
RuntimeInvisibleAnnotations:
x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C1.class
+ Compiled from "C1.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.C1
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C1
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "C1.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C2.class
+ Compiled from "C2.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.C2 extends com.android.hoststubgen.test.tinyframework.subclasstest.C1
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C2
+ super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C1
+ interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "C2.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C3.class
+ Compiled from "C3.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.C3 extends com.android.hoststubgen.test.tinyframework.subclasstest.C2
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C3
+ super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C2
+ interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "C3.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/CA.class
+ Compiled from "CA.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.CA
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/CA
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "CA.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/CB.class
+ Compiled from "CB.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.CB
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/CB
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "CB.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I1.class
+ Compiled from "I1.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.I1
+ minor version: 0
+ major version: 61
+ flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/I1
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "I1.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I2.class
+ Compiled from "I2.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.I2 extends com.android.hoststubgen.test.tinyframework.subclasstest.I1
+ minor version: 0
+ major version: 61
+ flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/I2
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "I2.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I3.class
+ Compiled from "I3.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.I3 extends com.android.hoststubgen.test.tinyframework.subclasstest.I2
+ minor version: 0
+ major version: 61
+ flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/I3
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "I3.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IA.class
+ Compiled from "IA.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.IA
+ minor version: 0
+ major version: 61
+ flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/IA
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "IA.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IB.class
+ Compiled from "IB.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.IB
+ minor version: 0
+ major version: 61
+ flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/IB
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 0, attributes: 2
+}
+SourceFile: "IB.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
## Class: com/unsupported/UnsupportedClass.class
Compiled from "UnsupportedClass.java"
public class com.unsupported.UnsupportedClass
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt
index 2357844..4fd5701 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/golden-output/13-hoststubgen-test-tiny-framework-host-ext-impl-dump.txt
@@ -4201,6 +4201,417 @@
RuntimeInvisibleAnnotations:
x: #x()
android.hosttest.annotation.HostSideTestWholeClassStub
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C1.class
+ Compiled from "C1.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.C1
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C1
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 1, attributes: 2
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/subclasstest/C1
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+}
+SourceFile: "C1.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C2.class
+ Compiled from "C2.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.C2 extends com.android.hoststubgen.test.tinyframework.subclasstest.C1
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C2
+ super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C1
+ interfaces: 0, fields: 0, methods: 1, attributes: 2
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/subclasstest/C2
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+}
+SourceFile: "C2.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/C3.class
+ Compiled from "C3.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.C3 extends com.android.hoststubgen.test.tinyframework.subclasstest.C2
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C3
+ super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C2
+ interfaces: 0, fields: 0, methods: 1, attributes: 2
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/subclasstest/C3
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+}
+SourceFile: "C3.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/CA.class
+ Compiled from "CA.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.CA
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/CA
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 1, attributes: 2
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/subclasstest/CA
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+}
+SourceFile: "CA.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/CB.class
+ Compiled from "CB.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.CB
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/CB
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 1, attributes: 2
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/subclasstest/CB
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+}
+SourceFile: "CB.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1.class
+ Compiled from "Class_C1.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_C1 extends com.android.hoststubgen.test.tinyframework.subclasstest.C1
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1
+ super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C1
+ interfaces: 0, fields: 0, methods: 1, attributes: 2
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+}
+SourceFile: "Class_C1.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2.class
+ Compiled from "Class_C2.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_C2 extends com.android.hoststubgen.test.tinyframework.subclasstest.C2
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2
+ super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C2
+ interfaces: 0, fields: 0, methods: 1, attributes: 2
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+}
+SourceFile: "Class_C2.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3.class
+ Compiled from "Class_C3.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_C3 extends com.android.hoststubgen.test.tinyframework.subclasstest.C3
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3
+ super_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/C3
+ interfaces: 0, fields: 0, methods: 1, attributes: 2
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+}
+SourceFile: "Class_C3.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1.class
+ Compiled from "Class_I1.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I1 implements com.android.hoststubgen.test.tinyframework.subclasstest.I1
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 1, attributes: 2
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+}
+SourceFile: "Class_I1.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA.class
+ Compiled from "Class_I1_IA.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I1_IA implements com.android.hoststubgen.test.tinyframework.subclasstest.I1,com.android.hoststubgen.test.tinyframework.subclasstest.IA
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA
+ super_class: #x // java/lang/Object
+ interfaces: 2, fields: 0, methods: 1, attributes: 2
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+}
+SourceFile: "Class_I1_IA.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2.class
+ Compiled from "Class_I2.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I2 implements com.android.hoststubgen.test.tinyframework.subclasstest.I2
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 1, attributes: 2
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+}
+SourceFile: "Class_I2.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3.class
+ Compiled from "Class_I3.java"
+public class com.android.hoststubgen.test.tinyframework.subclasstest.Class_I3 implements com.android.hoststubgen.test.tinyframework.subclasstest.I3
+ minor version: 0
+ major version: 61
+ flags: (0x0021) ACC_PUBLIC, ACC_SUPER
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 1, attributes: 2
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+}
+SourceFile: "Class_I3.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I1.class
+ Compiled from "I1.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.I1
+ minor version: 0
+ major version: 61
+ flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/I1
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 1, attributes: 2
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/subclasstest/I1
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+}
+SourceFile: "I1.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I2.class
+ Compiled from "I2.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.I2 extends com.android.hoststubgen.test.tinyframework.subclasstest.I1
+ minor version: 0
+ major version: 61
+ flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/I2
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 1, attributes: 2
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/subclasstest/I2
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+}
+SourceFile: "I2.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/I3.class
+ Compiled from "I3.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.I3 extends com.android.hoststubgen.test.tinyframework.subclasstest.I2
+ minor version: 0
+ major version: 61
+ flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/I3
+ super_class: #x // java/lang/Object
+ interfaces: 1, fields: 0, methods: 1, attributes: 2
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/subclasstest/I3
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+}
+SourceFile: "I3.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IA.class
+ Compiled from "IA.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.IA
+ minor version: 0
+ major version: 61
+ flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/IA
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 1, attributes: 2
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/subclasstest/IA
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+}
+SourceFile: "IA.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
+## Class: com/android/hoststubgen/test/tinyframework/subclasstest/IB.class
+ Compiled from "IB.java"
+public interface com.android.hoststubgen.test.tinyframework.subclasstest.IB
+ minor version: 0
+ major version: 61
+ flags: (0x0601) ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
+ this_class: #x // com/android/hoststubgen/test/tinyframework/subclasstest/IB
+ super_class: #x // java/lang/Object
+ interfaces: 0, fields: 0, methods: 1, attributes: 2
+ private static {};
+ descriptor: ()V
+ flags: (0x000a) ACC_PRIVATE, ACC_STATIC
+ Code:
+ stack=2, locals=0, args_size=0
+ x: ldc #x // class com/android/hoststubgen/test/tinyframework/subclasstest/IB
+ x: ldc #x // String com.android.hoststubgen.hosthelper.HostTestUtils.logClassLoaded
+ x: invokestatic #x // Method com/android/hoststubgen/hosthelper/HostTestUtils.onClassLoaded:(Ljava/lang/Class;Ljava/lang/String;)V
+ x: return
+}
+SourceFile: "IB.java"
+RuntimeVisibleAnnotations:
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInStub
+ x: #x()
+ com.android.hoststubgen.hosthelper.HostStubGenKeptInImpl
## Class: com/supported/UnsupportedClass.class
Compiled from "UnsupportedClass.java"
public class com.supported.UnsupportedClass
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt b/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
index 9b6b6e4..d302084 100644
--- a/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/policy-override-tiny-framework.txt
@@ -17,4 +17,23 @@
class com/android/hoststubgen/test/tinyframework/TinyFrameworkForTextPolicy ~com.android.hoststubgen.test.tinyframework.TinyFrameworkClassLoadHook.onClassLoaded
# Heuristics rule: Stub all the AIDL classes.
-class :aidl stubclass
\ No newline at end of file
+class :aidl stubclass
+
+# Default is "remove", so let's put all the base classes / interfaces in the stub first.
+class com.android.hoststubgen.test.tinyframework.subclasstest.C1 stub
+class com.android.hoststubgen.test.tinyframework.subclasstest.C2 stub
+class com.android.hoststubgen.test.tinyframework.subclasstest.C3 stub
+class com.android.hoststubgen.test.tinyframework.subclasstest.CA stub
+class com.android.hoststubgen.test.tinyframework.subclasstest.CB stub
+class com.android.hoststubgen.test.tinyframework.subclasstest.I1 stub
+class com.android.hoststubgen.test.tinyframework.subclasstest.I2 stub
+class com.android.hoststubgen.test.tinyframework.subclasstest.I3 stub
+class com.android.hoststubgen.test.tinyframework.subclasstest.IA stub
+class com.android.hoststubgen.test.tinyframework.subclasstest.IB stub
+
+# Then define inheritance based policies.
+class *com.android.hoststubgen.test.tinyframework.subclasstest.C1 keep
+class *com.android.hoststubgen.test.tinyframework.subclasstest.CA remove
+
+class *com.android.hoststubgen.test.tinyframework.subclasstest.I1 keep
+class *com.android.hoststubgen.test.tinyframework.subclasstest.IA remove
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C1.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C1.java
new file mode 100644
index 0000000..03c9e2a
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C1.java
@@ -0,0 +1,19 @@
+/*
+ * 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.hoststubgen.test.tinyframework.subclasstest;
+
+public class C1 {
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C2.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C2.java
new file mode 100644
index 0000000..3ca8f1f
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C2.java
@@ -0,0 +1,19 @@
+/*
+ * 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.hoststubgen.test.tinyframework.subclasstest;
+
+public class C2 extends C1 {
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C3.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C3.java
new file mode 100644
index 0000000..a6c14f0
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/C3.java
@@ -0,0 +1,19 @@
+/*
+ * 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.hoststubgen.test.tinyframework.subclasstest;
+
+public class C3 extends C2 {
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CA.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CA.java
new file mode 100644
index 0000000..2e35370
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CA.java
@@ -0,0 +1,19 @@
+/*
+ * 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.hoststubgen.test.tinyframework.subclasstest;
+
+public class CA {
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CB.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CB.java
new file mode 100644
index 0000000..fe4cee6
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/CB.java
@@ -0,0 +1,19 @@
+/*
+ * 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.hoststubgen.test.tinyframework.subclasstest;
+
+public class CB {
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1.java
new file mode 100644
index 0000000..12012fc
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C1.java
@@ -0,0 +1,19 @@
+/*
+ * 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.hoststubgen.test.tinyframework.subclasstest;
+
+public class Class_C1 extends C1 {
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2.java
new file mode 100644
index 0000000..8d48ee6
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C2.java
@@ -0,0 +1,19 @@
+/*
+ * 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.hoststubgen.test.tinyframework.subclasstest;
+
+public class Class_C2 extends C2 {
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3.java
new file mode 100644
index 0000000..6748430
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_C3.java
@@ -0,0 +1,19 @@
+/*
+ * 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.hoststubgen.test.tinyframework.subclasstest;
+
+public class Class_C3 extends C3 {
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CA.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CA.java
new file mode 100644
index 0000000..58aa5c3
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CA.java
@@ -0,0 +1,19 @@
+/*
+ * 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.hoststubgen.test.tinyframework.subclasstest;
+
+public class Class_CA extends CA {
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB.java
new file mode 100644
index 0000000..c1c3d62
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB.java
@@ -0,0 +1,19 @@
+/*
+ * 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.hoststubgen.test.tinyframework.subclasstest;
+
+public class Class_CB extends CB {
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB_IA.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB_IA.java
new file mode 100644
index 0000000..398b569
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_CB_IA.java
@@ -0,0 +1,19 @@
+/*
+ * 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.hoststubgen.test.tinyframework.subclasstest;
+
+public class Class_CB_IA extends CB implements IA {
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1.java
new file mode 100644
index 0000000..44cbd8f
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1.java
@@ -0,0 +1,19 @@
+/*
+ * 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.hoststubgen.test.tinyframework.subclasstest;
+
+public class Class_I1 implements I1 {
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA.java
new file mode 100644
index 0000000..42355a3
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I1_IA.java
@@ -0,0 +1,19 @@
+/*
+ * 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.hoststubgen.test.tinyframework.subclasstest;
+
+public class Class_I1_IA implements I1, IA {
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2.java
new file mode 100644
index 0000000..09c8099
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I2.java
@@ -0,0 +1,19 @@
+/*
+ * 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.hoststubgen.test.tinyframework.subclasstest;
+
+public class Class_I2 implements I2 {
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3.java
new file mode 100644
index 0000000..0806a47
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3.java
@@ -0,0 +1,19 @@
+/*
+ * 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.hoststubgen.test.tinyframework.subclasstest;
+
+public class Class_I3 implements I3 {
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3_IA.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3_IA.java
new file mode 100644
index 0000000..eaa8528
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_I3_IA.java
@@ -0,0 +1,19 @@
+/*
+ * 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.hoststubgen.test.tinyframework.subclasstest;
+
+public class Class_I3_IA implements I3, IA {
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA.java
new file mode 100644
index 0000000..778c5aa
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA.java
@@ -0,0 +1,19 @@
+/*
+ * 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.hoststubgen.test.tinyframework.subclasstest;
+
+public class Class_IA implements IA {
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I1.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I1.java
new file mode 100644
index 0000000..493f7c8
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I1.java
@@ -0,0 +1,19 @@
+/*
+ * 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.hoststubgen.test.tinyframework.subclasstest;
+
+public class Class_IA_I1 implements IA, I1 {
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I3.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I3.java
new file mode 100644
index 0000000..2aa1de1
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IA_I3.java
@@ -0,0 +1,19 @@
+/*
+ * 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.hoststubgen.test.tinyframework.subclasstest;
+
+public class Class_IA_I3 implements IA, I3 {
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB.java
new file mode 100644
index 0000000..d9eae09
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB.java
@@ -0,0 +1,19 @@
+/*
+ * 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.hoststubgen.test.tinyframework.subclasstest;
+
+public class Class_IB implements IB {
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB_IA.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB_IA.java
new file mode 100644
index 0000000..9ee4283
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_IB_IA.java
@@ -0,0 +1,19 @@
+/*
+ * 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.hoststubgen.test.tinyframework.subclasstest;
+
+public class Class_IB_IA implements IB, IA {
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_None.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_None.java
new file mode 100644
index 0000000..50ec2cb
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/Class_None.java
@@ -0,0 +1,19 @@
+/*
+ * 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.hoststubgen.test.tinyframework.subclasstest;
+
+public class Class_None {
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I1.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I1.java
new file mode 100644
index 0000000..3f36596
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I1.java
@@ -0,0 +1,19 @@
+/*
+ * 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.hoststubgen.test.tinyframework.subclasstest;
+
+public interface I1 {
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I2.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I2.java
new file mode 100644
index 0000000..960060c
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I2.java
@@ -0,0 +1,19 @@
+/*
+ * 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.hoststubgen.test.tinyframework.subclasstest;
+
+public interface I2 extends I1 {
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I3.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I3.java
new file mode 100644
index 0000000..c678eaa
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/I3.java
@@ -0,0 +1,19 @@
+/*
+ * 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.hoststubgen.test.tinyframework.subclasstest;
+
+public interface I3 extends I2 {
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IA.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IA.java
new file mode 100644
index 0000000..1cff484
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IA.java
@@ -0,0 +1,19 @@
+/*
+ * 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.hoststubgen.test.tinyframework.subclasstest;
+
+public interface IA {
+}
diff --git a/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IB.java b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IB.java
new file mode 100644
index 0000000..84e7173
--- /dev/null
+++ b/tools/hoststubgen/hoststubgen/test-tiny-framework/tiny-framework/src/com/android/hoststubgen/test/tinyframework/subclasstest/IB.java
@@ -0,0 +1,19 @@
+/*
+ * 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.hoststubgen.test.tinyframework.subclasstest;
+
+public interface IB {
+}