Merge "Return empty array rather than null" into sc-dev
diff --git a/METADATA b/METADATA
index d97975c..95577d8 100644
--- a/METADATA
+++ b/METADATA
@@ -1,3 +1,4 @@
third_party {
- license_type: NOTICE
+ # would be NOTICE save for libs/usb/tests/accessorytest/f_accessory.h
+ license_type: RESTRICTED
}
diff --git a/StubLibraries.bp b/StubLibraries.bp
index d1ad189..49433f1 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -116,7 +116,7 @@
last_released: {
api_file: ":android-non-updatable.api.public.latest",
removed_api_file: ":android-non-updatable-removed.api.public.latest",
- baseline_file: ":public-api-incompatibilities-with-last-released",
+ baseline_file: ":android-incompatibilities.api.public.latest",
},
api_lint: {
enabled: true,
@@ -168,7 +168,7 @@
last_released: {
api_file: ":android-non-updatable.api.system.latest",
removed_api_file: ":android-non-updatable-removed.api.system.latest",
- baseline_file: ":system-api-incompatibilities-with-last-released"
+ baseline_file: ":android-incompatibilities.api.system.latest"
},
api_lint: {
enabled: true,
diff --git a/apct-tests/perftests/core/src/android/os/OWNERS b/apct-tests/perftests/core/src/android/os/OWNERS
new file mode 100644
index 0000000..a1719c9
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/os/OWNERS
@@ -0,0 +1 @@
+per-file PackageParsingPerfTest.kt = file:/services/core/java/com/android/server/pm/OWNERS
\ No newline at end of file
diff --git a/apex/Android.bp b/apex/Android.bp
index 77ff6db..1876110 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -101,6 +101,10 @@
sdk_version: "module_current",
},
+ // installable implies we'll create a non-apex (platform) variant, which
+ // we shouldn't ordinarily need (and it can create issues), so disable that.
+ installable: false,
+
// Configure framework module specific metalava options.
droiddoc_options: [mainline_stubs_args],
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index d44169d1..d4ee9bb 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -151,8 +151,7 @@
static final boolean DEBUG_BG_LIMIT = localLOGV || false;
static final boolean DEBUG_STANDBY = localLOGV || false;
static final boolean RECORD_ALARMS_IN_HISTORY = true;
- // TODO (b/178484639): Turn off once allow-while-idle revamp is completed.
- static final boolean RECORD_DEVICE_IDLE_ALARMS = true;
+ static final boolean RECORD_DEVICE_IDLE_ALARMS = false;
static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
static final int TICK_HISTORY_DEPTH = 10;
diff --git a/apex/permission/Android.bp b/apex/permission/Android.bp
deleted file mode 100644
index be51143..0000000
--- a/apex/permission/Android.bp
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright (C) 2019 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-apex {
- name: "com.android.permission",
- defaults: ["com.android.permission-defaults"],
- manifest: "apex_manifest.json",
-}
-
-apex_defaults {
- name: "com.android.permission-defaults",
- updatable: true,
- min_sdk_version: "30",
- key: "com.android.permission.key",
- certificate: ":com.android.permission.certificate",
- java_libs: [
- "framework-permission",
- "framework-permission-s",
- "service-permission",
- ],
- apps: ["PermissionController"],
-}
-
-apex_key {
- name: "com.android.permission.key",
- public_key: "com.android.permission.avbpubkey",
- private_key: "com.android.permission.pem",
-}
-
-android_app_certificate {
- name: "com.android.permission.certificate",
- certificate: "com.android.permission",
-}
-
-filegroup {
- name: "permission-jarjar-rules",
- srcs: ["jarjar-rules.txt"],
-}
diff --git a/apex/permission/OWNERS b/apex/permission/OWNERS
deleted file mode 100644
index 957e10a..0000000
--- a/apex/permission/OWNERS
+++ /dev/null
@@ -1,6 +0,0 @@
-svetoslavganov@google.com
-moltmann@google.com
-eugenesusla@google.com
-zhanghai@google.com
-evanseverson@google.com
-ntmyren@google.com
diff --git a/apex/permission/TEST_MAPPING b/apex/permission/TEST_MAPPING
deleted file mode 100644
index 6e67ce9..0000000
--- a/apex/permission/TEST_MAPPING
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "presubmit" : [
- {
- "name" : "PermissionApexTests"
- }
- ]
-}
diff --git a/apex/permission/apex_manifest.json b/apex/permission/apex_manifest.json
deleted file mode 100644
index 6350d54..0000000
--- a/apex/permission/apex_manifest.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "name": "com.android.permission",
- "version": 309999999
-}
diff --git a/apex/permission/com.android.permission.avbpubkey b/apex/permission/com.android.permission.avbpubkey
deleted file mode 100644
index 9eaf852..0000000
--- a/apex/permission/com.android.permission.avbpubkey
+++ /dev/null
Binary files differ
diff --git a/apex/permission/com.android.permission.pem b/apex/permission/com.android.permission.pem
deleted file mode 100644
index 3d584be..0000000
--- a/apex/permission/com.android.permission.pem
+++ /dev/null
@@ -1,51 +0,0 @@
------BEGIN RSA PRIVATE KEY-----
-MIIJKgIBAAKCAgEA6snt4eqoz85xiL9Sf6w1S1b9FgSHK05zYTh2JYPvQKQ3yeZp
-E6avJ6FN6XcbmkDzSd658BvUGDBSPhOlzuUO4BsoKBuLMxP6TxIQXFKidzDqY0vQ
-4qkS++bdIhUjwBP3OSZ3Czu0BiihK8GC75Abr//EyCyObGIGGfHEGANiOgrpP4X5
-+OmLzQLCjk4iE1kg+U6cRSRI/XLaoWC0TvIIuzxznrQ6r5GmzgTOwyBWyIB+bj73
-bmsweHTU+w9Y7kGOx4hO3XCLIhoBWEw0EbuW9nZmQ4sZls5Jo/CbyJlCclF11yVo
-SCf2LG/T+9pah5NOmDQ1kPbU+0iKZIV4YFHGTIhyGDE/aPOuUT05ziCGDifgHr0u
-SG1x/RLqsVh/POvNxnvP9cQFMQ08BvbEJaTTgB785iwKsvdqCfmng/SAyxSetmzP
-StXVB3fh1OoZ8vunRbQYxnmUxycVqaA96zmBx2wLvbvzKo7pZFDE6nbhnT5+MRAM
-z/VIK89W26uB4gj8sBFslqZjT0jPqsAZuvDm7swOtMwIcEolyGJuFLqlhN7UwMz2
-9y8+IpYixR+HvD1TZI9NtmuCmv3kPrWgoMZg6yvaBayTIr8RdYzi6FO/C1lLiraz
-48dH3sXWRa8cgw6VcSUwYrEBIc3sotdsupO1iOjcFybIwaee0YTZJfjvbqkCAwEA
-AQKCAgEArRnfdpaJi1xLPGTCMDsIt9kUku0XswgN7PmxsYsKFAB+2S40/jYAIRm9
-1YjpItsMA8RgFfSOdJ77o6TctCMQyo17F8bm4+uwuic5RLfv7Cx2QmsdQF8jDfFx
-y7UGPJD7znjbf76uxXOjEB2FqZX3s9TAgkzHXIUQtoQW7RVhkCWHPjxKxgd5+NY2
-FrDoUpd9xhD9CcTsw1+wbRZdGW88nL6/B50dP2AFORM2VYo8MWr6y9FEn3YLsGOC
-uu7fxBk1aUrHyl81VRkTMMROB1zkuiUk1FtzrEm+5U15rXXBFYOVe9+qeLhtuOlh
-wueDoz0pzvF/JLe24uTik6YL0Ae6SD0pFXQ2KDrdH3cUHLok3r76/yGzaDNTFjS2
-2WbQ8dEJV8veNHk8gjGpFTJIsBUlcZpmUCDHlfvVMb3+2ahQ+28piQUt5t3zqJdZ
-NDqsOHzY6BRPc+Wm85Xii/lWiQceZSee/b1Enu+nchsyXhSenBfC6bIGZReyMI0K
-KKKuVhyR6OSOiR5ZdZ/NyXGqsWy05fn/h0X9hnpETsNaNYNKWvpHLfKll+STJpf7
-AZquJPIclQyiq5NONx6kfPztoCLkKV/zOgIj3Sx5oSZq+5gpO91nXWVwkTbqK1d1
-004q2Mah6UQyAk1XGQc2pHx7ouVcWawjU30vZ4C015Hv2lm/gVkCggEBAPltATYS
-OqOSL1YAtIHPiHxMjNAgUdglq8JiJFXVfkocGU9eNub3Ed3sSWu6GB9Myu/sSKje
-bJ5DZqxJnvB2Fqmu9I9OunLGFSD0aXs4prwsQ1Rm5FcbImtrxcciASdkoo8Pj0z4
-vk2r2NZD3VtER5Uh+YjSDkxcS9gBStXUpCL6gj69UpOxMmWqZVjyHatVB4lEvYJl
-N82uT7N7QVNL1DzcZ9z4C4r7ks1Pm7ka12s5m/oaAlAMdVeofiPJe1xA9zRToSr4
-tIbMkOeXFLVRLuji/7XsOgal5Rl59p+OwLshX5cswPVOMrH6zt+hbsJ5q8M5dqnX
-VAOBK7KNQ/EKZwcCggEBAPD6KVvyCim46n5EbcEqCkO7gevwZkw/9vLwmM5YsxTh
-z9FQkPO0iB7mwbX8w04I91Pre4NdfcgMG0pP1b13Sb4KHBchqW1a+TCs3kSGC6gn
-1SxmXHnA9jRxAkrWlGkoAQEz+aP61cXiiy2tXpQwJ8xQCKprfoqWZwhkCtEVU6CE
-S7v9cscOHIqgNxx4WoceMmq4EoihHAZzHxTcNVbByckMjb2XQJ0iNw3lDP4ddvc+
-a4HzHfHkhzeQ5ZNc8SvWU8z80aSCOKRsSD3aUTZzxhZ4O2tZSW7v7p+FpvVee7bC
-g8YCfszTdpVUMlLRLjScimAcovcFLSvtyupinxWg4M8CggEAN9YGEmOsSte7zwXj
-YrfhtumwEBtcFwX/2Ej+F1Tuq4p0xAa0RaoDjumJWhtTsRYQy/raHSuFpzwxbNoi
-QXQ+CIhI6RfXtz/OlQ0B2/rHoJJMFEXgUfuaDfAXW0eqeHYXyezSyIlamKqipPyW
-Pgsf9yue39keKEv1EorfhNTQVaA8rezV4oglXwrxGyNALw2e3UTNI7ai8mFWKDis
-XAg6n9E7UwUYGGnO6DUtCBgRJ0jDOQ6/e8n+LrxiWIKPIgzNCiK6jpMUXqTGv4Fb
-umdNGAdQ9RnHt5tFmRlrczaSwJFtA7uaCpAR2zPpQbiywchZAiAIB2dTwGEXNiZX
-kksg2wKCAQEA6pNad3qhkgPDoK6T+Jkn7M82paoaqtcJWWwEE7oceZNnbWZz9Agl
-CY+vuawXonrv5+0vCq2Tp4zBdBFLC2h3jFrjBVFrUFxifpOIukOSTVqZFON/2bWQ
-9XOcu6UuSz7522Xw+UNPnZXtzcUacD6AP08ZYGvLfrTyDyTzspyED5k48ALEHCkM
-d5WGkFxII4etpF0TDZVnZo/iDbhe49k4yFFEGO6Ho26PESOLBkNAb2V/2bwDxlij
-l9+g21Z6HiZA5SamHPH2mXgeyrcen1cL2QupK9J6vVcqfnboE6qp2zp2c+Yx8MlY
-gfy4EA44YFaSDQVTTgrn8f9Eq+zc130H2QKCAQEAqOKgv68nIPdDSngNyCVyWego
-boFiDaEJoBBg8FrBjTJ6wFLrNAnXmbvfTtgNmNAzF1cUPJZlIIsHgGrMCfpehbXq
-WQQIw+E+yFbTGLxseGRfsLrV0CsgnAoOVeod+yIHmqc3livaUbrWhL1V2f6Ue+sE
-7YLp/iP43NaMfA4kYk2ep7+ZJoEVkCjHJJaHWgAG3RynPJHkTJlSgu7wLYvGc9uE
-ZsEFUM46lX02t7rrtMfasVGrUy1c2xOxFb4v1vG6iEZ7+YWeq5o3AkxUwEGn+mG4
-/3p+k4AaTXJDXgyZ0Sv6CkGuPHenAYG4cswcUUEf/G4Ag77x6LBNMgycJBxUJA==
------END RSA PRIVATE KEY-----
diff --git a/apex/permission/com.android.permission.pk8 b/apex/permission/com.android.permission.pk8
deleted file mode 100644
index d51673d..0000000
--- a/apex/permission/com.android.permission.pk8
+++ /dev/null
Binary files differ
diff --git a/apex/permission/com.android.permission.x509.pem b/apex/permission/com.android.permission.x509.pem
deleted file mode 100644
index 4b146c9..0000000
--- a/apex/permission/com.android.permission.x509.pem
+++ /dev/null
@@ -1,35 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIGKzCCBBOgAwIBAgIUezo3fQeVZsmLpm/dkpGWJ/G/MN8wDQYJKoZIhvcNAQEL
-BQAwgaMxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQH
-DA1Nb3VudGFpbiBWaWV3MRAwDgYDVQQKDAdBbmRyb2lkMRAwDgYDVQQLDAdBbmRy
-b2lkMR8wHQYDVQQDDBZjb20uYW5kcm9pZC5wZXJtaXNzaW9uMSIwIAYJKoZIhvcN
-AQkBFhNhbmRyb2lkQGFuZHJvaWQuY29tMCAXDTE5MTAwOTIxMzExOVoYDzQ3NTcw
-OTA0MjEzMTE5WjCBozELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWEx
-FjAUBgNVBAcMDU1vdW50YWluIFZpZXcxEDAOBgNVBAoMB0FuZHJvaWQxEDAOBgNV
-BAsMB0FuZHJvaWQxHzAdBgNVBAMMFmNvbS5hbmRyb2lkLnBlcm1pc3Npb24xIjAg
-BgkqhkiG9w0BCQEWE2FuZHJvaWRAYW5kcm9pZC5jb20wggIiMA0GCSqGSIb3DQEB
-AQUAA4ICDwAwggIKAoICAQCxefguRJ7E6tBCTEOeU2HJEGs6AQQapLz9hMed0aaJ
-Qr7aTQiYJEk+sG4+jPYbjpxa8JDDzJHp+4g7DjfSb+dvT9n84A8lWaI/yRXTZTQN
-Hu5m/bgHhi0LbySpiaFyodXBKUAnOhZyGPtYjtBFywFylueub8ryc1Z6UxxU7udH
-1mkIr7sE48Qkq5SyjFROE96iFmYA+vS/JXOfS0NBHiMB4GBxx4V7kXpvrTI7hhZG
-HiyhKvNh7wyHIhO9nDEw1rwtAH6CsL3YkQEVBeAU98m+0Au+qStLYkKHh2l8zT4W
-7sVK1VSqfB+VqOUmeIGdzlBfqMsoXD+FJz6KnIdUHIwjFDjL7Xr+hd+7xve+Q3S+
-U3Blk/U6atY8PM09wNfilG+SvwcKk5IgriDcu3rWKgIFxbUUaxLrDW7pLlu6wt/d
-GGtKK+Bc0jF+9Z901Tl33i5xhc5yOktT0btkKs7lSeE6VzP/Nk5g0SuzixmuRoh9
-f5Ge41N2ZCEHNXx3wZeVZwHIIPfYrL7Yql1Xoxbfs4ETFk6ChzVQcvjfDQQuK58J
-uNc+TOCoI/qflXwGCwpuHl0ier8V5Z4tpMUl5rWyVR/QGRtLPvs2lLuxczDw1OXq
-wEVtCMn9aNnd4y7R9PZ52hi53HAvDjpWefrLYi+Q04J6iGFQ1qAFBClK9DquBvmR
-swIDAQABo1MwUTAdBgNVHQ4EFgQULpfus5s5SrqLkoUKyPXA0D1iHPMwHwYDVR0j
-BBgwFoAULpfus5s5SrqLkoUKyPXA0D1iHPMwDwYDVR0TAQH/BAUwAwEB/zANBgkq
-hkiG9w0BAQsFAAOCAgEAjxQG5EFv8V/9yV2glI53VOmlWMjfEgvUjd39s/XLyPlr
-OzPOKSB0NFo8To3l4l+MsManxPK8y0OyfEVKbWVz9onv0ovo5MVokBmV/2G0jmsV
-B4e9yjOq+DmqIvY/Qh63Ywb97sTgcFI8620MhQDbh2IpEGv4ZNV0H6rgXmgdSCBw
-1EjBoYfFpN5aMgZjeyzZcq+d1IapdWqdhuEJQkMvoYS4WIumNIJlEXPQRoq/F5Ih
-nszdbKI/jVyiGFa2oeZ3rja1Y6GCRU8TYEoKx1pjS8uQDOEDTwsG/QnUe9peEj0V
-SsCkIidJWTomAmq9Tub9vpBe1zuTpuRAwxwR0qwgSxozV1Mvow1dJ19oFtHX0yD6
-ZjCpRn5PW9kMvSWSlrcrFs1NJf0j1Cvf7bHpkEDqLqpMnnh9jaFQq3nzDY+MWcIR
-jDcgQpI+AiE2/qtauZnFEVhbce49nCnk9+5bpTTIZJdzqeaExe5KXHwEtZLaEDh4
-atLY9LuEvPsjmDIMOR6hycD9FvwGXhJOQBjESIWFwigtSb1Yud9n6201jw3MLJ4k
-+WhkbmZgWy+xc+Mdm5H3XyB1lvHaHGkxu+QB9KyQuVQKwbUVcbwZIfTFPN6Zr/dS
-ZXJqAbBhG/dBgF0LazuLaPVpibi+a3Y+tb9b8eXGkz4F97PWZIEDkELQ+9KOvhc=
------END CERTIFICATE-----
diff --git a/apex/permission/framework-s/Android.bp b/apex/permission/framework-s/Android.bp
deleted file mode 100644
index e71cc43..0000000
--- a/apex/permission/framework-s/Android.bp
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright (C) 2021 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-filegroup {
- name: "framework-permission-s-sources",
- srcs: [
- "java/**/*.java",
- "java/**/*.aidl",
- ],
- path: "java",
- visibility: ["//frameworks/base"],
-}
-
-java_library {
- name: "framework-permission-s-shared",
- srcs: [":framework-permission-s-shared-srcs"],
- libs: [
- "framework-annotations-lib",
- "unsupportedappusage",
- ],
- apex_available: [
- "com.android.permission",
- "test_com.android.permission",
- ],
- installable: false,
- min_sdk_version: "30",
- sdk_version: "module_current",
-}
-
-java_sdk_library {
- name: "framework-permission-s",
- defaults: ["framework-module-defaults"],
- srcs: [
- ":framework-permission-s-sources",
- ],
- libs: [
- "framework-annotations-lib"
- ],
- static_libs: [
- "framework-permission-s-shared",
- ],
- apex_available: [
- "com.android.permission",
- "test_com.android.permission",
- ],
- hostdex: true,
- // Restrict access to implementation library.
- impl_library_visibility: [
- "//frameworks/base/apex/permission:__subpackages__",
- "//packages/modules/Permission:__subpackages__",
- ],
- installable: true,
- jarjar_rules: ":permission-jarjar-rules",
- min_sdk_version: "30",
- permitted_packages: [
- "android.permission",
- "android.app.role",
- // For com.android.permission.jarjar.
- "com.android.permission",
- ],
-}
diff --git a/apex/permission/framework-s/api/current.txt b/apex/permission/framework-s/api/current.txt
deleted file mode 100644
index 4ecc989..0000000
--- a/apex/permission/framework-s/api/current.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-// Signature format: 2.0
-package android.app.role {
-
- public final class RoleManager {
- method @NonNull public android.content.Intent createRequestRoleIntent(@NonNull String);
- method public boolean isRoleAvailable(@NonNull String);
- method public boolean isRoleHeld(@NonNull String);
- field public static final String ROLE_ASSISTANT = "android.app.role.ASSISTANT";
- field public static final String ROLE_BROWSER = "android.app.role.BROWSER";
- field public static final String ROLE_CALL_REDIRECTION = "android.app.role.CALL_REDIRECTION";
- field public static final String ROLE_CALL_SCREENING = "android.app.role.CALL_SCREENING";
- field public static final String ROLE_DIALER = "android.app.role.DIALER";
- field public static final String ROLE_EMERGENCY = "android.app.role.EMERGENCY";
- field public static final String ROLE_HOME = "android.app.role.HOME";
- field public static final String ROLE_SMS = "android.app.role.SMS";
- }
-
-}
-
diff --git a/apex/permission/framework-s/api/module-lib-current.txt b/apex/permission/framework-s/api/module-lib-current.txt
deleted file mode 100644
index d7c9a23..0000000
--- a/apex/permission/framework-s/api/module-lib-current.txt
+++ /dev/null
@@ -1,11 +0,0 @@
-// Signature format: 2.0
-package android.app.role {
-
- public final class RoleManager {
- method @Nullable public String getBrowserRoleHolder(int);
- method @Nullable public String getSmsRoleHolder(int);
- method @Nullable @RequiresPermission(android.Manifest.permission.SET_PREFERRED_APPLICATIONS) public boolean setBrowserRoleHolder(@Nullable String, int);
- }
-
-}
-
diff --git a/apex/permission/framework-s/api/module-lib-removed.txt b/apex/permission/framework-s/api/module-lib-removed.txt
deleted file mode 100644
index d802177..0000000
--- a/apex/permission/framework-s/api/module-lib-removed.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/apex/permission/framework-s/api/removed.txt b/apex/permission/framework-s/api/removed.txt
deleted file mode 100644
index d802177..0000000
--- a/apex/permission/framework-s/api/removed.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/apex/permission/framework-s/api/system-current.txt b/apex/permission/framework-s/api/system-current.txt
deleted file mode 100644
index 6778d48..0000000
--- a/apex/permission/framework-s/api/system-current.txt
+++ /dev/null
@@ -1,43 +0,0 @@
-// Signature format: 2.0
-package android.app.role {
-
- public interface OnRoleHoldersChangedListener {
- method public void onRoleHoldersChanged(@NonNull String, @NonNull android.os.UserHandle);
- }
-
- @Deprecated public abstract class RoleControllerService extends android.app.Service {
- ctor @Deprecated public RoleControllerService();
- method @Deprecated @WorkerThread public abstract boolean onAddRoleHolder(@NonNull String, @NonNull String, int);
- method @Deprecated @Nullable public final android.os.IBinder onBind(@Nullable android.content.Intent);
- method @Deprecated @WorkerThread public abstract boolean onClearRoleHolders(@NonNull String, int);
- method @Deprecated @WorkerThread public abstract boolean onGrantDefaultRoles();
- method @Deprecated public abstract boolean onIsApplicationQualifiedForRole(@NonNull String, @NonNull String);
- method @Deprecated public boolean onIsApplicationVisibleForRole(@NonNull String, @NonNull String);
- method @Deprecated public abstract boolean onIsRoleVisible(@NonNull String);
- method @Deprecated @WorkerThread public abstract boolean onRemoveRoleHolder(@NonNull String, @NonNull String, int);
- field @Deprecated public static final String SERVICE_INTERFACE = "android.app.role.RoleControllerService";
- }
-
- public class RoleFrameworkInitializer {
- method public static void registerServiceWrappers();
- }
-
- public final class RoleManager {
- method @RequiresPermission(android.Manifest.permission.OBSERVE_ROLE_HOLDERS) public void addOnRoleHoldersChangedListenerAsUser(@NonNull java.util.concurrent.Executor, @NonNull android.app.role.OnRoleHoldersChangedListener, @NonNull android.os.UserHandle);
- method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void addRoleHolderAsUser(@NonNull String, @NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
- method @Deprecated @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public boolean addRoleHolderFromController(@NonNull String, @NonNull String);
- method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void clearRoleHoldersAsUser(@NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
- method @Deprecated @NonNull @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public java.util.List<java.lang.String> getHeldRolesFromController(@NonNull String);
- method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public java.util.List<java.lang.String> getRoleHolders(@NonNull String);
- method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public java.util.List<java.lang.String> getRoleHoldersAsUser(@NonNull String, @NonNull android.os.UserHandle);
- method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void isApplicationVisibleForRole(@NonNull String, @NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
- method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void isRoleVisible(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
- method @RequiresPermission(android.Manifest.permission.OBSERVE_ROLE_HOLDERS) public void removeOnRoleHoldersChangedListenerAsUser(@NonNull android.app.role.OnRoleHoldersChangedListener, @NonNull android.os.UserHandle);
- method @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public void removeRoleHolderAsUser(@NonNull String, @NonNull String, int, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
- method @Deprecated @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public boolean removeRoleHolderFromController(@NonNull String, @NonNull String);
- method @Deprecated @RequiresPermission("com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER") public void setRoleNamesFromController(@NonNull java.util.List<java.lang.String>);
- field public static final int MANAGE_HOLDERS_FLAG_DONT_KILL_APP = 1; // 0x1
- }
-
-}
-
diff --git a/apex/permission/framework-s/api/system-removed.txt b/apex/permission/framework-s/api/system-removed.txt
deleted file mode 100644
index d802177..0000000
--- a/apex/permission/framework-s/api/system-removed.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/apex/permission/framework-s/java/android/app/role/IOnRoleHoldersChangedListener.aidl b/apex/permission/framework-s/java/android/app/role/IOnRoleHoldersChangedListener.aidl
deleted file mode 100644
index 6cf961f..0000000
--- a/apex/permission/framework-s/java/android/app/role/IOnRoleHoldersChangedListener.aidl
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.role;
-
-/**
- * @hide
- */
-oneway interface IOnRoleHoldersChangedListener {
-
- void onRoleHoldersChanged(String roleName, int userId);
-}
diff --git a/apex/permission/framework-s/java/android/app/role/IRoleController.aidl b/apex/permission/framework-s/java/android/app/role/IRoleController.aidl
deleted file mode 100644
index 8a43d7f..0000000
--- a/apex/permission/framework-s/java/android/app/role/IRoleController.aidl
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.role;
-
-import android.os.RemoteCallback;
-
-/**
- * @hide
- */
-oneway interface IRoleController {
-
- void grantDefaultRoles(in RemoteCallback callback);
-
- void onAddRoleHolder(in String roleName, in String packageName, int flags,
- in RemoteCallback callback);
-
- void onRemoveRoleHolder(in String roleName, in String packageName, int flags,
- in RemoteCallback callback);
-
- void onClearRoleHolders(in String roleName, int flags, in RemoteCallback callback);
-
- void isApplicationQualifiedForRole(in String roleName, in String packageName,
- in RemoteCallback callback);
-
- void isApplicationVisibleForRole(in String roleName, in String packageName,
- in RemoteCallback callback);
-
- void isRoleVisible(in String roleName, in RemoteCallback callback);
-}
diff --git a/apex/permission/framework-s/java/android/app/role/IRoleManager.aidl b/apex/permission/framework-s/java/android/app/role/IRoleManager.aidl
deleted file mode 100644
index 5fc25f0..0000000
--- a/apex/permission/framework-s/java/android/app/role/IRoleManager.aidl
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.role;
-
-import android.app.role.IOnRoleHoldersChangedListener;
-import android.os.Bundle;
-import android.os.RemoteCallback;
-
-/**
- * @hide
- */
-interface IRoleManager {
-
- boolean isRoleAvailable(in String roleName);
-
- boolean isRoleHeld(in String roleName, in String packageName);
-
- List<String> getRoleHoldersAsUser(in String roleName, int userId);
-
- void addRoleHolderAsUser(in String roleName, in String packageName, int flags, int userId,
- in RemoteCallback callback);
-
- void removeRoleHolderAsUser(in String roleName, in String packageName, int flags, int userId,
- in RemoteCallback callback);
-
- void clearRoleHoldersAsUser(in String roleName, int flags, int userId,
- in RemoteCallback callback);
-
- void addOnRoleHoldersChangedListenerAsUser(IOnRoleHoldersChangedListener listener, int userId);
-
- void removeOnRoleHoldersChangedListenerAsUser(IOnRoleHoldersChangedListener listener,
- int userId);
-
- void setRoleNamesFromController(in List<String> roleNames);
-
- boolean addRoleHolderFromController(in String roleName, in String packageName);
-
- boolean removeRoleHolderFromController(in String roleName, in String packageName);
-
- List<String> getHeldRolesFromController(in String packageName);
-
- String getBrowserRoleHolder(int userId);
-
- boolean setBrowserRoleHolder(String packageName, int userId);
-
- String getSmsRoleHolder(int userId);
-}
diff --git a/apex/permission/framework-s/java/android/app/role/OnRoleHoldersChangedListener.java b/apex/permission/framework-s/java/android/app/role/OnRoleHoldersChangedListener.java
deleted file mode 100644
index 5958deb..0000000
--- a/apex/permission/framework-s/java/android/app/role/OnRoleHoldersChangedListener.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.role;
-
-import android.annotation.NonNull;
-import android.annotation.SystemApi;
-import android.os.UserHandle;
-
-/**
- * Listener for role holder changes.
- *
- * @hide
- */
-@SystemApi
-public interface OnRoleHoldersChangedListener {
-
- /**
- * Called when the holders of roles are changed.
- *
- * @param roleName the name of the role whose holders are changed
- * @param user the user for this role holder change
- */
- void onRoleHoldersChanged(@NonNull String roleName, @NonNull UserHandle user);
-}
diff --git a/apex/permission/framework-s/java/android/app/role/RoleControllerManager.java b/apex/permission/framework-s/java/android/app/role/RoleControllerManager.java
deleted file mode 100644
index 93a7ae0..0000000
--- a/apex/permission/framework-s/java/android/app/role/RoleControllerManager.java
+++ /dev/null
@@ -1,265 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.role;
-
-import android.Manifest;
-import android.annotation.CallbackExecutor;
-import android.annotation.NonNull;
-import android.annotation.RequiresPermission;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ServiceInfo;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.RemoteCallback;
-import android.util.Log;
-import android.util.SparseArray;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.infra.AndroidFuture;
-import com.android.internal.infra.ServiceConnector;
-
-import java.util.concurrent.Executor;
-import java.util.concurrent.TimeUnit;
-import java.util.function.Consumer;
-
-/**
- * Interface for communicating with the role controller.
- *
- * @hide
- */
-public class RoleControllerManager {
-
- private static final String LOG_TAG = RoleControllerManager.class.getSimpleName();
-
- private static final long REQUEST_TIMEOUT_MILLIS = 15 * 1000;
-
- private static volatile ComponentName sRemoteServiceComponentName;
-
- private static final Object sRemoteServicesLock = new Object();
-
- /**
- * Global remote services (per user) used by all {@link RoleControllerManager managers}.
- */
- @GuardedBy("sRemoteServicesLock")
- private static final SparseArray<ServiceConnector<IRoleController>> sRemoteServices =
- new SparseArray<>();
-
- @NonNull
- private final ServiceConnector<IRoleController> mRemoteService;
-
- /**
- * Initialize the remote service component name once so that we can avoid acquiring the
- * PackageManagerService lock in constructor.
- *
- * @see #createWithInitializedRemoteServiceComponentName(Handler, Context)
- *
- * @hide
- */
- public static void initializeRemoteServiceComponentName(@NonNull Context context) {
- sRemoteServiceComponentName = getRemoteServiceComponentName(context);
- }
-
- /**
- * Create a {@link RoleControllerManager} instance with the initialized remote service component
- * name so that we can avoid acquiring the PackageManagerService lock in constructor.
- *
- * @see #initializeRemoteServiceComponentName(Context)
- *
- * @hide
- */
- @NonNull
- public static RoleControllerManager createWithInitializedRemoteServiceComponentName(
- @NonNull Handler handler, @NonNull Context context) {
- return new RoleControllerManager(sRemoteServiceComponentName, handler, context);
- }
-
- private RoleControllerManager(@NonNull ComponentName remoteServiceComponentName,
- @NonNull Handler handler, @NonNull Context context) {
- synchronized (sRemoteServicesLock) {
- int userId = context.getUser().getIdentifier();
- ServiceConnector<IRoleController> remoteService = sRemoteServices.get(userId);
- if (remoteService == null) {
- remoteService = new ServiceConnector.Impl<IRoleController>(context,
- new Intent(RoleControllerService.SERVICE_INTERFACE)
- .setComponent(remoteServiceComponentName),
- 0 /* bindingFlags */, userId, IRoleController.Stub::asInterface) {
-
- @Override
- protected Handler getJobHandler() {
- return handler;
- }
- };
- sRemoteServices.put(userId, remoteService);
- }
- mRemoteService = remoteService;
- }
- }
-
- /**
- * @hide
- */
- public RoleControllerManager(@NonNull Context context) {
- this(getRemoteServiceComponentName(context), new Handler(Looper.getMainLooper()), context);
- }
-
- @NonNull
- private static ComponentName getRemoteServiceComponentName(@NonNull Context context) {
- Intent intent = new Intent(RoleControllerService.SERVICE_INTERFACE);
- PackageManager packageManager = context.getPackageManager();
- intent.setPackage(packageManager.getPermissionControllerPackageName());
- ServiceInfo serviceInfo = packageManager.resolveService(intent, 0).serviceInfo;
- return new ComponentName(serviceInfo.packageName, serviceInfo.name);
- }
-
- /**
- * @see RoleControllerService#onGrantDefaultRoles()
- *
- * @hide
- */
- public void grantDefaultRoles(@NonNull @CallbackExecutor Executor executor,
- @NonNull Consumer<Boolean> callback) {
- AndroidFuture<Bundle> operation = mRemoteService.postAsync(service -> {
- AndroidFuture<Bundle> future = new AndroidFuture<>();
- service.grantDefaultRoles(new RemoteCallback(future::complete));
- return future;
- });
- propagateCallback(operation, "grantDefaultRoles", executor, callback);
- }
-
- /**
- * @see RoleControllerService#onAddRoleHolder(String, String, int)
- *
- * @hide
- */
- public void onAddRoleHolder(@NonNull String roleName, @NonNull String packageName,
- @RoleManager.ManageHoldersFlags int flags, @NonNull RemoteCallback callback) {
- AndroidFuture<Bundle> operation = mRemoteService.postAsync(service -> {
- AndroidFuture<Bundle> future = new AndroidFuture<>();
- service.onAddRoleHolder(roleName, packageName, flags,
- new RemoteCallback(future::complete));
- return future;
- });
- propagateCallback(operation, "onAddRoleHolder", callback);
- }
-
- /**
- * @see RoleControllerService#onRemoveRoleHolder(String, String, int)
- *
- * @hide
- */
- public void onRemoveRoleHolder(@NonNull String roleName, @NonNull String packageName,
- @RoleManager.ManageHoldersFlags int flags, @NonNull RemoteCallback callback) {
- AndroidFuture<Bundle> operation = mRemoteService.postAsync(service -> {
- AndroidFuture<Bundle> future = new AndroidFuture<>();
- service.onRemoveRoleHolder(roleName, packageName, flags,
- new RemoteCallback(future::complete));
- return future;
- });
- propagateCallback(operation, "onRemoveRoleHolder", callback);
- }
-
- /**
- * @see RoleControllerService#onClearRoleHolders(String, int)
- *
- * @hide
- */
- public void onClearRoleHolders(@NonNull String roleName,
- @RoleManager.ManageHoldersFlags int flags, @NonNull RemoteCallback callback) {
- AndroidFuture<Bundle> operation = mRemoteService.postAsync(service -> {
- AndroidFuture<Bundle> future = new AndroidFuture<>();
- service.onClearRoleHolders(roleName, flags,
- new RemoteCallback(future::complete));
- return future;
- });
- propagateCallback(operation, "onClearRoleHolders", callback);
- }
-
- /**
- * @see RoleControllerService#onIsApplicationVisibleForRole(String, String)
- *
- * @hide
- */
- @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
- public void isApplicationVisibleForRole(@NonNull String roleName, @NonNull String packageName,
- @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) {
- AndroidFuture<Bundle> operation = mRemoteService.postAsync(service -> {
- AndroidFuture<Bundle> future = new AndroidFuture<>();
- service.isApplicationVisibleForRole(roleName, packageName,
- new RemoteCallback(future::complete));
- return future;
- });
- propagateCallback(operation, "isApplicationVisibleForRole", executor, callback);
- }
-
- /**
- * @see RoleControllerService#onIsRoleVisible(String)
- *
- * @hide
- */
- @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
- public void isRoleVisible(@NonNull String roleName,
- @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) {
- AndroidFuture<Bundle> operation = mRemoteService.postAsync(service -> {
- AndroidFuture<Bundle> future = new AndroidFuture<>();
- service.isRoleVisible(roleName, new RemoteCallback(future::complete));
- return future;
- });
- propagateCallback(operation, "isRoleVisible", executor, callback);
- }
-
- private void propagateCallback(AndroidFuture<Bundle> operation, String opName,
- @CallbackExecutor @NonNull Executor executor,
- Consumer<Boolean> destination) {
- operation.orTimeout(REQUEST_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
- .whenComplete((res, err) -> executor.execute(() -> {
- final long token = Binder.clearCallingIdentity();
- try {
- if (err != null) {
- Log.e(LOG_TAG, "Error calling " + opName + "()", err);
- destination.accept(false);
- } else {
- destination.accept(res != null);
- }
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }));
- }
-
- private void propagateCallback(AndroidFuture<Bundle> operation, String opName,
- RemoteCallback destination) {
- operation.orTimeout(REQUEST_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
- .whenComplete((res, err) -> {
- final long token = Binder.clearCallingIdentity();
- try {
- if (err != null) {
- Log.e(LOG_TAG, "Error calling " + opName + "()", err);
- destination.sendResult(null);
- } else {
- destination.sendResult(res);
- }
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- });
- }
-}
diff --git a/apex/permission/framework-s/java/android/app/role/RoleControllerService.java b/apex/permission/framework-s/java/android/app/role/RoleControllerService.java
deleted file mode 100644
index cf78729..0000000
--- a/apex/permission/framework-s/java/android/app/role/RoleControllerService.java
+++ /dev/null
@@ -1,304 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.role;
-
-import android.Manifest;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.annotation.WorkerThread;
-import android.app.Service;
-import android.content.Intent;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.IBinder;
-import android.os.Process;
-import android.os.RemoteCallback;
-import android.os.UserHandle;
-
-import com.android.internal.util.Preconditions;
-
-import java.util.Objects;
-import java.util.concurrent.Executor;
-
-/**
- * Abstract base class for the role controller service.
- * <p>
- * Subclass should implement the business logic for role management, including enforcing role
- * requirements and granting or revoking relevant privileges of roles. This class can only be
- * implemented by the permission controller app which is registered in {@code PackageManager}.
- *
- * @deprecated The role controller service is an internal implementation detail inside role, and it
- * may be replaced by other mechanisms in the future and no longer be called.
- *
- * @hide
- */
-@Deprecated
-@SystemApi
-public abstract class RoleControllerService extends Service {
-
- /**
- * The {@link Intent} that must be declared as handled by the service.
- */
- public static final String SERVICE_INTERFACE = "android.app.role.RoleControllerService";
-
- private HandlerThread mWorkerThread;
- private Handler mWorkerHandler;
-
- @Override
- public void onCreate() {
- super.onCreate();
-
- mWorkerThread = new HandlerThread(RoleControllerService.class.getSimpleName());
- mWorkerThread.start();
- mWorkerHandler = new Handler(mWorkerThread.getLooper());
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
-
- mWorkerThread.quitSafely();
- }
-
- @Nullable
- @Override
- public final IBinder onBind(@Nullable Intent intent) {
- return new IRoleController.Stub() {
-
- @Override
- public void grantDefaultRoles(RemoteCallback callback) {
- enforceCallerSystemUid("grantDefaultRoles");
-
- Objects.requireNonNull(callback, "callback cannot be null");
-
- mWorkerHandler.post(() -> RoleControllerService.this.grantDefaultRoles(callback));
- }
-
- @Override
- public void onAddRoleHolder(String roleName, String packageName, int flags,
- RemoteCallback callback) {
- enforceCallerSystemUid("onAddRoleHolder");
-
- Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
- Preconditions.checkStringNotEmpty(packageName,
- "packageName cannot be null or empty");
- Objects.requireNonNull(callback, "callback cannot be null");
-
- mWorkerHandler.post(() -> RoleControllerService.this.onAddRoleHolder(roleName,
- packageName, flags, callback));
- }
-
- @Override
- public void onRemoveRoleHolder(String roleName, String packageName, int flags,
- RemoteCallback callback) {
- enforceCallerSystemUid("onRemoveRoleHolder");
-
- Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
- Preconditions.checkStringNotEmpty(packageName,
- "packageName cannot be null or empty");
- Objects.requireNonNull(callback, "callback cannot be null");
-
- mWorkerHandler.post(() -> RoleControllerService.this.onRemoveRoleHolder(roleName,
- packageName, flags, callback));
- }
-
- @Override
- public void onClearRoleHolders(String roleName, int flags, RemoteCallback callback) {
- enforceCallerSystemUid("onClearRoleHolders");
-
- Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
- Objects.requireNonNull(callback, "callback cannot be null");
-
- mWorkerHandler.post(() -> RoleControllerService.this.onClearRoleHolders(roleName,
- flags, callback));
- }
-
- private void enforceCallerSystemUid(@NonNull String methodName) {
- if (Binder.getCallingUid() != Process.SYSTEM_UID) {
- throw new SecurityException("Only the system process can call " + methodName
- + "()");
- }
- }
-
- @Override
- public void isApplicationQualifiedForRole(String roleName, String packageName,
- RemoteCallback callback) {
- enforceCallingPermission(Manifest.permission.MANAGE_ROLE_HOLDERS, null);
-
- Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
- Preconditions.checkStringNotEmpty(packageName,
- "packageName cannot be null or empty");
- Objects.requireNonNull(callback, "callback cannot be null");
-
- boolean qualified = onIsApplicationQualifiedForRole(roleName, packageName);
- callback.sendResult(qualified ? Bundle.EMPTY : null);
- }
-
- @Override
- public void isApplicationVisibleForRole(String roleName, String packageName,
- RemoteCallback callback) {
- enforceCallingPermission(Manifest.permission.MANAGE_ROLE_HOLDERS, null);
-
- Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
- Preconditions.checkStringNotEmpty(packageName,
- "packageName cannot be null or empty");
- Objects.requireNonNull(callback, "callback cannot be null");
-
- boolean visible = onIsApplicationVisibleForRole(roleName, packageName);
- callback.sendResult(visible ? Bundle.EMPTY : null);
- }
-
- @Override
- public void isRoleVisible(String roleName, RemoteCallback callback) {
- enforceCallingPermission(Manifest.permission.MANAGE_ROLE_HOLDERS, null);
-
- Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
- Objects.requireNonNull(callback, "callback cannot be null");
-
- boolean visible = onIsRoleVisible(roleName);
- callback.sendResult(visible ? Bundle.EMPTY : null);
- }
- };
- }
-
- private void grantDefaultRoles(@NonNull RemoteCallback callback) {
- boolean successful = onGrantDefaultRoles();
- callback.sendResult(successful ? Bundle.EMPTY : null);
- }
-
- private void onAddRoleHolder(@NonNull String roleName, @NonNull String packageName,
- @RoleManager.ManageHoldersFlags int flags, @NonNull RemoteCallback callback) {
- boolean successful = onAddRoleHolder(roleName, packageName, flags);
- callback.sendResult(successful ? Bundle.EMPTY : null);
- }
-
- private void onRemoveRoleHolder(@NonNull String roleName, @NonNull String packageName,
- @RoleManager.ManageHoldersFlags int flags, @NonNull RemoteCallback callback) {
- boolean successful = onRemoveRoleHolder(roleName, packageName, flags);
- callback.sendResult(successful ? Bundle.EMPTY : null);
- }
-
- private void onClearRoleHolders(@NonNull String roleName,
- @RoleManager.ManageHoldersFlags int flags, @NonNull RemoteCallback callback) {
- boolean successful = onClearRoleHolders(roleName, flags);
- callback.sendResult(successful ? Bundle.EMPTY : null);
- }
-
- /**
- * Called by system to grant default permissions and roles.
- * <p>
- * This is typically when creating a new user or upgrading either system or
- * permission controller package
- *
- * @return whether this call was successful
- */
- @WorkerThread
- public abstract boolean onGrantDefaultRoles();
-
- /**
- * Add a specific application to the holders of a role. If the role is exclusive, the previous
- * holder will be replaced.
- * <p>
- * Implementation should enforce the role requirements and grant or revoke the relevant
- * privileges of roles.
- *
- * @param roleName the name of the role to add the role holder for
- * @param packageName the package name of the application to add to the role holders
- * @param flags optional behavior flags
- *
- * @return whether this call was successful
- *
- * @see RoleManager#addRoleHolderAsUser(String, String, int, UserHandle, Executor,
- * RemoteCallback)
- */
- @WorkerThread
- public abstract boolean onAddRoleHolder(@NonNull String roleName, @NonNull String packageName,
- @RoleManager.ManageHoldersFlags int flags);
-
- /**
- * Remove a specific application from the holders of a role.
- *
- * @param roleName the name of the role to remove the role holder for
- * @param packageName the package name of the application to remove from the role holders
- * @param flags optional behavior flags
- *
- * @return whether this call was successful
- *
- * @see RoleManager#removeRoleHolderAsUser(String, String, int, UserHandle, Executor,
- * RemoteCallback)
- */
- @WorkerThread
- public abstract boolean onRemoveRoleHolder(@NonNull String roleName,
- @NonNull String packageName, @RoleManager.ManageHoldersFlags int flags);
-
- /**
- * Remove all holders of a role.
- *
- * @param roleName the name of the role to remove role holders for
- * @param flags optional behavior flags
- *
- * @return whether this call was successful
- *
- * @see RoleManager#clearRoleHoldersAsUser(String, int, UserHandle, Executor, RemoteCallback)
- */
- @WorkerThread
- public abstract boolean onClearRoleHolders(@NonNull String roleName,
- @RoleManager.ManageHoldersFlags int flags);
-
- /**
- * Check whether an application is qualified for a role.
- *
- * @param roleName name of the role to check for
- * @param packageName package name of the application to check for
- *
- * @return whether the application is qualified for the role
- *
- * @deprecated Implement {@link #onIsApplicationVisibleForRole(String, String)} instead.
- */
- @Deprecated
- public abstract boolean onIsApplicationQualifiedForRole(@NonNull String roleName,
- @NonNull String packageName);
-
- /**
- * Check whether an application is visible for a role.
- *
- * While an application can be qualified for a role, it can still stay hidden from user (thus
- * not visible). If an application is visible for a role, we may show things related to the role
- * for it, e.g. showing an entry pointing to the role settings in its application info page.
- *
- * @param roleName name of the role to check for
- * @param packageName package name of the application to check for
- *
- * @return whether the application is visible for the role
- */
- public boolean onIsApplicationVisibleForRole(@NonNull String roleName,
- @NonNull String packageName) {
- return onIsApplicationQualifiedForRole(roleName, packageName);
- }
-
- /**
- * Check whether a role should be visible to user.
- *
- * @param roleName name of the role to check for
- *
- * @return whether the role should be visible to user
- */
- public abstract boolean onIsRoleVisible(@NonNull String roleName);
-}
diff --git a/apex/permission/framework-s/java/android/app/role/RoleFrameworkInitializer.java b/apex/permission/framework-s/java/android/app/role/RoleFrameworkInitializer.java
deleted file mode 100644
index 7a97770..0000000
--- a/apex/permission/framework-s/java/android/app/role/RoleFrameworkInitializer.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.role;
-
-import android.annotation.SystemApi;
-import android.app.SystemServiceRegistry;
-import android.content.Context;
-
-/**
- * Class holding initialization code for role in the permission module.
- *
- * @hide
- */
-@SystemApi
-public class RoleFrameworkInitializer {
- private RoleFrameworkInitializer() {}
-
- /**
- * Called by {@link SystemServiceRegistry}'s static initializer and registers
- * {@link RoleManager} to {@link Context}, so that {@link Context#getSystemService} can return
- * it.
- *
- * <p>If this is called from other places, it throws a {@link IllegalStateException).
- */
- public static void registerServiceWrappers() {
- SystemServiceRegistry.registerContextAwareService(Context.ROLE_SERVICE, RoleManager.class,
- (context, serviceBinder) -> new RoleManager(context,
- IRoleManager.Stub.asInterface(serviceBinder)));
- }
-}
diff --git a/apex/permission/framework-s/java/android/app/role/RoleManager.java b/apex/permission/framework-s/java/android/app/role/RoleManager.java
deleted file mode 100644
index ceccc4c..0000000
--- a/apex/permission/framework-s/java/android/app/role/RoleManager.java
+++ /dev/null
@@ -1,773 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.app.role;
-
-import android.Manifest;
-import android.annotation.CallbackExecutor;
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.RequiresPermission;
-import android.annotation.SystemApi;
-import android.annotation.SystemService;
-import android.annotation.UserIdInt;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Binder;
-import android.os.Process;
-import android.os.RemoteCallback;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.util.ArrayMap;
-import android.util.SparseArray;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.Preconditions;
-
-import java.util.List;
-import java.util.Objects;
-import java.util.concurrent.Executor;
-import java.util.function.Consumer;
-
-/**
- * This class provides information about and manages roles.
- * <p>
- * A role is a unique name within the system associated with certain privileges. The list of
- * available roles might change with a system app update, so apps should not make assumption about
- * the availability of roles. Instead, they should always query if the role is available using
- * {@link #isRoleAvailable(String)} before trying to do anything with it. Some predefined role names
- * are available as constants in this class, and a list of possibly available roles can be found in
- * the <a href="{@docRoot}reference/androidx/core/role/package-summary.html">AndroidX Role
- * library</a>.
- * <p>
- * There can be multiple applications qualifying for a role, but only a subset of them can become
- * role holders. To qualify for a role, an application must meet certain requirements, including
- * defining certain components in its manifest. These requirements can be found in the AndroidX
- * Libraries. Then the application will need user consent to become a role holder, which can be
- * requested using {@link android.app.Activity#startActivityForResult(Intent, int)} with the
- * {@code Intent} obtained from {@link #createRequestRoleIntent(String)}.
- * <p>
- * Upon becoming a role holder, the application may be granted certain privileges that are role
- * specific. When the application loses its role, these privileges will also be revoked.
- */
-@SystemService(Context.ROLE_SERVICE)
-public final class RoleManager {
-
- private static final String LOG_TAG = RoleManager.class.getSimpleName();
-
- /**
- * The name of the assistant app role.
- *
- * @see android.service.voice.VoiceInteractionService
- */
- public static final String ROLE_ASSISTANT = "android.app.role.ASSISTANT";
-
- /**
- * The name of the browser role.
- *
- * @see Intent#CATEGORY_APP_BROWSER
- */
- public static final String ROLE_BROWSER = "android.app.role.BROWSER";
-
- /**
- * The name of the dialer role.
- *
- * @see Intent#ACTION_DIAL
- * @see android.telecom.InCallService
- */
- public static final String ROLE_DIALER = "android.app.role.DIALER";
-
- /**
- * The name of the SMS role.
- *
- * @see Intent#CATEGORY_APP_MESSAGING
- */
- public static final String ROLE_SMS = "android.app.role.SMS";
-
- /**
- * The name of the emergency role
- */
- public static final String ROLE_EMERGENCY = "android.app.role.EMERGENCY";
-
- /**
- * The name of the home role.
- *
- * @see Intent#CATEGORY_HOME
- */
- public static final String ROLE_HOME = "android.app.role.HOME";
-
- /**
- * The name of the call redirection role.
- * <p>
- * A call redirection app provides a means to re-write the phone number for an outgoing call to
- * place the call through a call redirection service.
- *
- * @see android.telecom.CallRedirectionService
- */
- public static final String ROLE_CALL_REDIRECTION = "android.app.role.CALL_REDIRECTION";
-
- /**
- * The name of the call screening and caller id role.
- *
- * @see android.telecom.CallScreeningService
- */
- public static final String ROLE_CALL_SCREENING = "android.app.role.CALL_SCREENING";
-
- /**
- * @hide
- */
- @IntDef(flag = true, value = { MANAGE_HOLDERS_FLAG_DONT_KILL_APP })
- public @interface ManageHoldersFlags {}
-
- /**
- * Flag parameter for {@link #addRoleHolderAsUser}, {@link #removeRoleHolderAsUser} and
- * {@link #clearRoleHoldersAsUser} to indicate that apps should not be killed when changing
- * their role holder status.
- *
- * @hide
- */
- @SystemApi
- public static final int MANAGE_HOLDERS_FLAG_DONT_KILL_APP = 1;
-
- /**
- * The action used to request user approval of a role for an application.
- *
- * @hide
- */
- public static final String ACTION_REQUEST_ROLE = "android.app.role.action.REQUEST_ROLE";
-
- /**
- * The permission required to manage records of role holders in {@link RoleManager} directly.
- *
- * @hide
- */
- public static final String PERMISSION_MANAGE_ROLES_FROM_CONTROLLER =
- "com.android.permissioncontroller.permission.MANAGE_ROLES_FROM_CONTROLLER";
-
- @NonNull
- private final Context mContext;
-
- @NonNull
- private final IRoleManager mService;
-
- @GuardedBy("mListenersLock")
- @NonNull
- private final SparseArray<ArrayMap<OnRoleHoldersChangedListener,
- OnRoleHoldersChangedListenerDelegate>> mListeners = new SparseArray<>();
- @NonNull
- private final Object mListenersLock = new Object();
-
- @GuardedBy("mRoleControllerManagerLock")
- @Nullable
- private RoleControllerManager mRoleControllerManager;
- private final Object mRoleControllerManagerLock = new Object();
-
- /**
- * Create a new instance of this class.
- *
- * @param context the {@link Context}
- * @param service the {@link IRoleManager} service
- *
- * @hide
- */
- public RoleManager(@NonNull Context context, @NonNull IRoleManager service) {
- mContext = context;
- mService = service;
- }
-
- /**
- * Returns an {@code Intent} suitable for passing to
- * {@link android.app.Activity#startActivityForResult(Intent, int)} which prompts the user to
- * grant a role to this application.
- * <p>
- * If the role is granted, the {@code resultCode} will be
- * {@link android.app.Activity#RESULT_OK}, otherwise it will be
- * {@link android.app.Activity#RESULT_CANCELED}.
- *
- * @param roleName the name of requested role
- *
- * @return the {@code Intent} to prompt user to grant the role
- */
- @NonNull
- public Intent createRequestRoleIntent(@NonNull String roleName) {
- Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
- Intent intent = new Intent(ACTION_REQUEST_ROLE);
- intent.setPackage(mContext.getPackageManager().getPermissionControllerPackageName());
- intent.putExtra(Intent.EXTRA_ROLE_NAME, roleName);
- return intent;
- }
-
- /**
- * Check whether a role is available in the system.
- *
- * @param roleName the name of role to checking for
- *
- * @return whether the role is available in the system
- */
- public boolean isRoleAvailable(@NonNull String roleName) {
- Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
- try {
- return mService.isRoleAvailable(roleName);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Check whether the calling application is holding a particular role.
- *
- * @param roleName the name of the role to check for
- *
- * @return whether the calling application is holding the role
- */
- public boolean isRoleHeld(@NonNull String roleName) {
- Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
- try {
- return mService.isRoleHeld(roleName, mContext.getPackageName());
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Get package names of the applications holding the role.
- * <p>
- * <strong>Note:</strong> Using this API requires holding
- * {@code android.permission.MANAGE_ROLE_HOLDERS}.
- *
- * @param roleName the name of the role to get the role holder for
- *
- * @return a list of package names of the role holders, or an empty list if none.
- *
- * @see #getRoleHoldersAsUser(String, UserHandle)
- *
- * @hide
- */
- @NonNull
- @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
- @SystemApi
- public List<String> getRoleHolders(@NonNull String roleName) {
- return getRoleHoldersAsUser(roleName, Process.myUserHandle());
- }
-
- /**
- * Get package names of the applications holding the role.
- * <p>
- * <strong>Note:</strong> Using this API requires holding
- * {@code android.permission.MANAGE_ROLE_HOLDERS} and if the user id is not the current user
- * {@code android.permission.INTERACT_ACROSS_USERS_FULL}.
- *
- * @param roleName the name of the role to get the role holder for
- * @param user the user to get the role holder for
- *
- * @return a list of package names of the role holders, or an empty list if none.
- *
- * @see #addRoleHolderAsUser(String, String, int, UserHandle, Executor, Consumer)
- * @see #removeRoleHolderAsUser(String, String, int, UserHandle, Executor, Consumer)
- * @see #clearRoleHoldersAsUser(String, int, UserHandle, Executor, Consumer)
- *
- * @hide
- */
- @NonNull
- @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
- @SystemApi
- public List<String> getRoleHoldersAsUser(@NonNull String roleName, @NonNull UserHandle user) {
- Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
- Objects.requireNonNull(user, "user cannot be null");
- try {
- return mService.getRoleHoldersAsUser(roleName, user.getIdentifier());
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Add a specific application to the holders of a role. If the role is exclusive, the previous
- * holder will be replaced.
- * <p>
- * <strong>Note:</strong> Using this API requires holding
- * {@code android.permission.MANAGE_ROLE_HOLDERS} and if the user id is not the current user
- * {@code android.permission.INTERACT_ACROSS_USERS_FULL}.
- *
- * @param roleName the name of the role to add the role holder for
- * @param packageName the package name of the application to add to the role holders
- * @param flags optional behavior flags
- * @param user the user to add the role holder for
- * @param executor the {@code Executor} to run the callback on.
- * @param callback the callback for whether this call is successful
- *
- * @see #getRoleHoldersAsUser(String, UserHandle)
- * @see #removeRoleHolderAsUser(String, String, int, UserHandle, Executor, Consumer)
- * @see #clearRoleHoldersAsUser(String, int, UserHandle, Executor, Consumer)
- *
- * @hide
- */
- @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
- @SystemApi
- public void addRoleHolderAsUser(@NonNull String roleName, @NonNull String packageName,
- @ManageHoldersFlags int flags, @NonNull UserHandle user,
- @CallbackExecutor @NonNull Executor executor, @NonNull Consumer<Boolean> callback) {
- Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
- Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
- Objects.requireNonNull(user, "user cannot be null");
- Objects.requireNonNull(executor, "executor cannot be null");
- Objects.requireNonNull(callback, "callback cannot be null");
- try {
- mService.addRoleHolderAsUser(roleName, packageName, flags, user.getIdentifier(),
- createRemoteCallback(executor, callback));
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Remove a specific application from the holders of a role.
- * <p>
- * <strong>Note:</strong> Using this API requires holding
- * {@code android.permission.MANAGE_ROLE_HOLDERS} and if the user id is not the current user
- * {@code android.permission.INTERACT_ACROSS_USERS_FULL}.
- *
- * @param roleName the name of the role to remove the role holder for
- * @param packageName the package name of the application to remove from the role holders
- * @param flags optional behavior flags
- * @param user the user to remove the role holder for
- * @param executor the {@code Executor} to run the callback on.
- * @param callback the callback for whether this call is successful
- *
- * @see #getRoleHoldersAsUser(String, UserHandle)
- * @see #addRoleHolderAsUser(String, String, int, UserHandle, Executor, Consumer)
- * @see #clearRoleHoldersAsUser(String, int, UserHandle, Executor, Consumer)
- *
- * @hide
- */
- @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
- @SystemApi
- public void removeRoleHolderAsUser(@NonNull String roleName, @NonNull String packageName,
- @ManageHoldersFlags int flags, @NonNull UserHandle user,
- @CallbackExecutor @NonNull Executor executor, @NonNull Consumer<Boolean> callback) {
- Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
- Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
- Objects.requireNonNull(user, "user cannot be null");
- Objects.requireNonNull(executor, "executor cannot be null");
- Objects.requireNonNull(callback, "callback cannot be null");
- try {
- mService.removeRoleHolderAsUser(roleName, packageName, flags, user.getIdentifier(),
- createRemoteCallback(executor, callback));
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Remove all holders of a role.
- * <p>
- * <strong>Note:</strong> Using this API requires holding
- * {@code android.permission.MANAGE_ROLE_HOLDERS} and if the user id is not the current user
- * {@code android.permission.INTERACT_ACROSS_USERS_FULL}.
- *
- * @param roleName the name of the role to remove role holders for
- * @param flags optional behavior flags
- * @param user the user to remove role holders for
- * @param executor the {@code Executor} to run the callback on.
- * @param callback the callback for whether this call is successful
- *
- * @see #getRoleHoldersAsUser(String, UserHandle)
- * @see #addRoleHolderAsUser(String, String, int, UserHandle, Executor, Consumer)
- * @see #removeRoleHolderAsUser(String, String, int, UserHandle, Executor, Consumer)
- *
- * @hide
- */
- @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
- @SystemApi
- public void clearRoleHoldersAsUser(@NonNull String roleName, @ManageHoldersFlags int flags,
- @NonNull UserHandle user, @CallbackExecutor @NonNull Executor executor,
- @NonNull Consumer<Boolean> callback) {
- Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
- Objects.requireNonNull(user, "user cannot be null");
- Objects.requireNonNull(executor, "executor cannot be null");
- Objects.requireNonNull(callback, "callback cannot be null");
- try {
- mService.clearRoleHoldersAsUser(roleName, flags, user.getIdentifier(),
- createRemoteCallback(executor, callback));
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- @NonNull
- private static RemoteCallback createRemoteCallback(@NonNull Executor executor,
- @NonNull Consumer<Boolean> callback) {
- return new RemoteCallback(result -> executor.execute(() -> {
- boolean successful = result != null;
- final long token = Binder.clearCallingIdentity();
- try {
- callback.accept(successful);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }));
- }
-
- /**
- * Add a listener to observe role holder changes
- * <p>
- * <strong>Note:</strong> Using this API requires holding
- * {@code android.permission.OBSERVE_ROLE_HOLDERS} and if the user id is not the current user
- * {@code android.permission.INTERACT_ACROSS_USERS_FULL}.
- *
- * @param executor the {@code Executor} to call the listener on.
- * @param listener the listener to be added
- * @param user the user to add the listener for
- *
- * @see #removeOnRoleHoldersChangedListenerAsUser(OnRoleHoldersChangedListener, UserHandle)
- *
- * @hide
- */
- @RequiresPermission(Manifest.permission.OBSERVE_ROLE_HOLDERS)
- @SystemApi
- public void addOnRoleHoldersChangedListenerAsUser(@CallbackExecutor @NonNull Executor executor,
- @NonNull OnRoleHoldersChangedListener listener, @NonNull UserHandle user) {
- Objects.requireNonNull(executor, "executor cannot be null");
- Objects.requireNonNull(listener, "listener cannot be null");
- Objects.requireNonNull(user, "user cannot be null");
- int userId = user.getIdentifier();
- synchronized (mListenersLock) {
- ArrayMap<OnRoleHoldersChangedListener, OnRoleHoldersChangedListenerDelegate> listeners =
- mListeners.get(userId);
- if (listeners == null) {
- listeners = new ArrayMap<>();
- mListeners.put(userId, listeners);
- } else {
- if (listeners.containsKey(listener)) {
- return;
- }
- }
- OnRoleHoldersChangedListenerDelegate listenerDelegate =
- new OnRoleHoldersChangedListenerDelegate(executor, listener);
- try {
- mService.addOnRoleHoldersChangedListenerAsUser(listenerDelegate, userId);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- listeners.put(listener, listenerDelegate);
- }
- }
-
- /**
- * Remove a listener observing role holder changes
- * <p>
- * <strong>Note:</strong> Using this API requires holding
- * {@code android.permission.OBSERVE_ROLE_HOLDERS} and if the user id is not the current user
- * {@code android.permission.INTERACT_ACROSS_USERS_FULL}.
- *
- * @param listener the listener to be removed
- * @param user the user to remove the listener for
- *
- * @see #addOnRoleHoldersChangedListenerAsUser(Executor, OnRoleHoldersChangedListener,
- * UserHandle)
- *
- * @hide
- */
- @RequiresPermission(Manifest.permission.OBSERVE_ROLE_HOLDERS)
- @SystemApi
- public void removeOnRoleHoldersChangedListenerAsUser(
- @NonNull OnRoleHoldersChangedListener listener, @NonNull UserHandle user) {
- Objects.requireNonNull(listener, "listener cannot be null");
- Objects.requireNonNull(user, "user cannot be null");
- int userId = user.getIdentifier();
- synchronized (mListenersLock) {
- ArrayMap<OnRoleHoldersChangedListener, OnRoleHoldersChangedListenerDelegate> listeners =
- mListeners.get(userId);
- if (listeners == null) {
- return;
- }
- OnRoleHoldersChangedListenerDelegate listenerDelegate = listeners.get(listener);
- if (listenerDelegate == null) {
- return;
- }
- try {
- mService.removeOnRoleHoldersChangedListenerAsUser(listenerDelegate,
- user.getIdentifier());
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- listeners.remove(listener);
- if (listeners.isEmpty()) {
- mListeners.remove(userId);
- }
- }
- }
-
- /**
- * Set the names of all the available roles. Should only be called from
- * {@link android.app.role.RoleControllerService}.
- * <p>
- * <strong>Note:</strong> Using this API requires holding
- * {@link #PERMISSION_MANAGE_ROLES_FROM_CONTROLLER}.
- *
- * @param roleNames the names of all the available roles
- *
- * @deprecated This is only usable by the role controller service, which is an internal
- * implementation detail inside role.
- *
- * @hide
- */
- @Deprecated
- @RequiresPermission(PERMISSION_MANAGE_ROLES_FROM_CONTROLLER)
- @SystemApi
- public void setRoleNamesFromController(@NonNull List<String> roleNames) {
- Objects.requireNonNull(roleNames, "roleNames cannot be null");
- try {
- mService.setRoleNamesFromController(roleNames);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Add a specific application to the holders of a role, only modifying records inside
- * {@link RoleManager}. Should only be called from
- * {@link android.app.role.RoleControllerService}.
- * <p>
- * <strong>Note:</strong> Using this API requires holding
- * {@link #PERMISSION_MANAGE_ROLES_FROM_CONTROLLER}.
- *
- * @param roleName the name of the role to add the role holder for
- * @param packageName the package name of the application to add to the role holders
- *
- * @return whether the operation was successful, and will also be {@code true} if a matching
- * role holder is already found.
- *
- * @see #getRoleHolders(String)
- * @see #removeRoleHolderFromController(String, String)
- *
- * @deprecated This is only usable by the role controller service, which is an internal
- * implementation detail inside role.
- *
- * @hide
- */
- @Deprecated
- @RequiresPermission(PERMISSION_MANAGE_ROLES_FROM_CONTROLLER)
- @SystemApi
- public boolean addRoleHolderFromController(@NonNull String roleName,
- @NonNull String packageName) {
- Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
- Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
- try {
- return mService.addRoleHolderFromController(roleName, packageName);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Remove a specific application from the holders of a role, only modifying records inside
- * {@link RoleManager}. Should only be called from
- * {@link android.app.role.RoleControllerService}.
- * <p>
- * <strong>Note:</strong> Using this API requires holding
- * {@link #PERMISSION_MANAGE_ROLES_FROM_CONTROLLER}.
- *
- * @param roleName the name of the role to remove the role holder for
- * @param packageName the package name of the application to remove from the role holders
- *
- * @return whether the operation was successful, and will also be {@code true} if no matching
- * role holder was found to remove.
- *
- * @see #getRoleHolders(String)
- * @see #addRoleHolderFromController(String, String)
- *
- * @deprecated This is only usable by the role controller service, which is an internal
- * implementation detail inside role.
- *
- * @hide
- */
- @Deprecated
- @RequiresPermission(PERMISSION_MANAGE_ROLES_FROM_CONTROLLER)
- @SystemApi
- public boolean removeRoleHolderFromController(@NonNull String roleName,
- @NonNull String packageName) {
- Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
- Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
- try {
- return mService.removeRoleHolderFromController(roleName, packageName);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Returns the list of all roles that the given package is currently holding
- *
- * @param packageName the package name
- * @return the list of role names
- *
- * @deprecated This is only usable by the role controller service, which is an internal
- * implementation detail inside role.
- *
- * @hide
- */
- @Deprecated
- @NonNull
- @RequiresPermission(PERMISSION_MANAGE_ROLES_FROM_CONTROLLER)
- @SystemApi
- public List<String> getHeldRolesFromController(@NonNull String packageName) {
- Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
- try {
- return mService.getHeldRolesFromController(packageName);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Get the role holder of {@link #ROLE_BROWSER} without requiring
- * {@link Manifest.permission#OBSERVE_ROLE_HOLDERS}, as in
- * {@link android.content.pm.PackageManager#getDefaultBrowserPackageNameAsUser(int)}
- *
- * @param userId the user ID
- * @return the package name of the default browser, or {@code null} if none
- *
- * @hide
- */
- @Nullable
- @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
- public String getBrowserRoleHolder(@UserIdInt int userId) {
- try {
- return mService.getBrowserRoleHolder(userId);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Set the role holder of {@link #ROLE_BROWSER} requiring
- * {@link Manifest.permission.SET_PREFERRED_APPLICATIONS} instead of
- * {@link Manifest.permission#MANAGE_ROLE_HOLDERS}, as in
- * {@link android.content.pm.PackageManager#setDefaultBrowserPackageNameAsUser(String, int)}
- *
- * @param packageName the package name of the default browser, or {@code null} if none
- * @param userId the user ID
- * @return whether the default browser was set successfully
- *
- * @hide
- */
- @Nullable
- @RequiresPermission(Manifest.permission.SET_PREFERRED_APPLICATIONS)
- @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
- public boolean setBrowserRoleHolder(@Nullable String packageName, @UserIdInt int userId) {
- try {
- return mService.setBrowserRoleHolder(packageName, userId);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Allows getting the role holder for {@link #ROLE_SMS} without requiring
- * {@link Manifest.permission#OBSERVE_ROLE_HOLDERS}, as in
- * {@link android.provider.Telephony.Sms#getDefaultSmsPackage(Context)}.
- *
- * @param userId the user ID to get the default SMS package for
- * @return the package name of the default SMS app, or {@code null} if none
- *
- * @hide
- */
- @Nullable
- @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
- public String getSmsRoleHolder(@UserIdInt int userId) {
- try {
- return mService.getSmsRoleHolder(userId);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Check whether a role should be visible to user.
- *
- * @param roleName name of the role to check for
- * @param executor the executor to execute callback on
- * @param callback the callback to receive whether the role should be visible to user
- *
- * @hide
- */
- @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
- @SystemApi
- public void isRoleVisible(@NonNull String roleName,
- @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) {
- getRoleControllerManager().isRoleVisible(roleName, executor, callback);
- }
-
- /**
- * Check whether an application is visible for a role.
- *
- * While an application can be qualified for a role, it can still stay hidden from user (thus
- * not visible). If an application is visible for a role, we may show things related to the role
- * for it, e.g. showing an entry pointing to the role settings in its application info page.
- *
- * @param roleName the name of the role to check for
- * @param packageName the package name of the application to check for
- * @param executor the executor to execute callback on
- * @param callback the callback to receive whether the application is visible for the role
- *
- * @hide
- */
- @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
- @SystemApi
- public void isApplicationVisibleForRole(@NonNull String roleName, @NonNull String packageName,
- @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) {
- getRoleControllerManager().isApplicationVisibleForRole(roleName, packageName, executor,
- callback);
- }
-
- @NonNull
- private RoleControllerManager getRoleControllerManager() {
- synchronized (mRoleControllerManagerLock) {
- if (mRoleControllerManager == null) {
- mRoleControllerManager = new RoleControllerManager(mContext);
- }
- return mRoleControllerManager;
- }
- }
-
- private static class OnRoleHoldersChangedListenerDelegate
- extends IOnRoleHoldersChangedListener.Stub {
-
- @NonNull
- private final Executor mExecutor;
- @NonNull
- private final OnRoleHoldersChangedListener mListener;
-
- OnRoleHoldersChangedListenerDelegate(@NonNull Executor executor,
- @NonNull OnRoleHoldersChangedListener listener) {
- mExecutor = executor;
- mListener = listener;
- }
-
- @Override
- public void onRoleHoldersChanged(@NonNull String roleName, @UserIdInt int userId) {
- final long token = Binder.clearCallingIdentity();
- try {
- mExecutor.execute(() ->
- mListener.onRoleHoldersChanged(roleName, UserHandle.of(userId)));
- } finally {
- Binder.restoreCallingIdentity(token);
- }
- }
- }
-}
diff --git a/apex/permission/framework-s/java/android/app/role/TEST_MAPPING b/apex/permission/framework-s/java/android/app/role/TEST_MAPPING
deleted file mode 100644
index f8f140d..0000000
--- a/apex/permission/framework-s/java/android/app/role/TEST_MAPPING
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "presubmit": [
- {
- "name": "CtsRoleTestCases",
- "options": [
- {
- "include-filter": "android.app.role.cts.RoleManagerTest"
- }
- ]
- }
- ]
-}
diff --git a/apex/permission/framework/Android.bp b/apex/permission/framework/Android.bp
deleted file mode 100644
index 52a6167..0000000
--- a/apex/permission/framework/Android.bp
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright (C) 2020 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-filegroup {
- name: "framework-permission-sources",
- srcs: [
- "java/**/*.java",
- "java/**/*.aidl",
- ],
- path: "java",
- visibility: ["//frameworks/base"],
-}
-
-java_sdk_library {
- name: "framework-permission",
- defaults: ["framework-module-defaults"],
-
- // Restrict access to implementation library.
- impl_library_visibility: [
- "//frameworks/base/apex/permission:__subpackages__",
- "//packages/modules/Permission:__subpackages__",
- ],
-
- srcs: [
- ":framework-permission-sources",
- ],
-
- apex_available: [
- "com.android.permission",
- "test_com.android.permission",
- ],
- min_sdk_version: "30",
- permitted_packages: [
- "android.permission",
- "android.app.role",
- ],
- hostdex: true,
- installable: true,
-}
diff --git a/apex/permission/framework/api/current.txt b/apex/permission/framework/api/current.txt
deleted file mode 100644
index d802177..0000000
--- a/apex/permission/framework/api/current.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/apex/permission/framework/api/module-lib-current.txt b/apex/permission/framework/api/module-lib-current.txt
deleted file mode 100644
index d802177..0000000
--- a/apex/permission/framework/api/module-lib-current.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/apex/permission/framework/api/module-lib-removed.txt b/apex/permission/framework/api/module-lib-removed.txt
deleted file mode 100644
index d802177..0000000
--- a/apex/permission/framework/api/module-lib-removed.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/apex/permission/framework/api/removed.txt b/apex/permission/framework/api/removed.txt
deleted file mode 100644
index d802177..0000000
--- a/apex/permission/framework/api/removed.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/apex/permission/framework/api/system-current.txt b/apex/permission/framework/api/system-current.txt
deleted file mode 100644
index d802177..0000000
--- a/apex/permission/framework/api/system-current.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/apex/permission/framework/api/system-removed.txt b/apex/permission/framework/api/system-removed.txt
deleted file mode 100644
index d802177..0000000
--- a/apex/permission/framework/api/system-removed.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/apex/permission/framework/java/android/permission/PermissionState.java b/apex/permission/framework/java/android/permission/PermissionState.java
deleted file mode 100644
index e810db8..0000000
--- a/apex/permission/framework/java/android/permission/PermissionState.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.permission;
-
-/**
- * @hide
- */
-public class PermissionState {}
diff --git a/apex/permission/jarjar-rules.txt b/apex/permission/jarjar-rules.txt
deleted file mode 100644
index 4729ed1..0000000
--- a/apex/permission/jarjar-rules.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-rule android.os.HandlerExecutor com.android.permission.jarjar.@0
-rule android.util.IndentingPrintWriter com.android.permission.jarjar.@0
-rule com.android.internal.** com.android.permission.jarjar.@0
-rule com.android.modules.** com.android.permission.jarjar.@0
-rule com.android.role.*Proto com.android.permission.jarjar.@0
diff --git a/apex/permission/service/Android.bp b/apex/permission/service/Android.bp
deleted file mode 100644
index d0fc5b9..0000000
--- a/apex/permission/service/Android.bp
+++ /dev/null
@@ -1,106 +0,0 @@
-// Copyright (C) 2020 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-filegroup {
- name: "service-permission-sources",
- srcs: [
- "java/**/*.java",
- ],
- path: "java",
- visibility: ["//frameworks/base/services"],
-}
-
-filegroup {
- name: "service-permission-protos",
- srcs: [
- "proto/**/*.proto",
- ],
- visibility: ["//frameworks/base"],
-}
-
-gensrcs {
- name: "service-permission-javastream-protos",
- depfile: true,
-
- tools: [
- "aprotoc",
- "protoc-gen-javastream",
- "soong_zip",
- ],
-
- cmd: "mkdir -p $(genDir)/$(in) " +
- "&& $(location aprotoc) " +
- " --plugin=$(location protoc-gen-javastream) " +
- " --dependency_out=$(depfile) " +
- " --javastream_out=$(genDir)/$(in) " +
- " -Iexternal/protobuf/src " +
- " -I . " +
- " $(in) " +
- "&& $(location soong_zip) -jar -o $(out) -C $(genDir)/$(in) -D $(genDir)/$(in)",
-
- srcs: [
- ":service-permission-protos",
- ],
- output_extension: "srcjar",
-}
-
-java_library {
- name: "service-permission-shared",
- srcs: [":service-permission-shared-srcs"],
- libs: [
- "framework-annotations-lib",
- "framework-permission-s-shared",
- ],
- apex_available: [
- "com.android.permission",
- "test_com.android.permission",
- ],
- installable: false,
- min_sdk_version: "30",
- sdk_version: "system_server_current",
-}
-
-java_sdk_library {
- name: "service-permission",
- defaults: ["framework-system-server-module-defaults"],
- impl_library_visibility: [
- "//frameworks/base/apex/permission/tests",
- "//frameworks/base/services/tests/mockingservicestests",
- "//frameworks/base/services/tests/servicestests",
- "//packages/modules/Permission/tests",
- ],
- srcs: [
- ":service-permission-sources",
- ":service-permission-javastream-protos",
- ],
- libs: [
- "framework-permission",
- "framework-permission-s.impl",
- "framework-permission-s-shared",
- ],
- static_libs: [
- "modules-utils-os",
- "service-permission-shared",
- ],
- jarjar_rules: ":permission-jarjar-rules",
- min_sdk_version: "30",
- sdk_version: "system_server_current",
- apex_available: [
- "com.android.permission",
- "test_com.android.permission",
- ],
- installable: true,
- // We don't have last-api tracking files for the public part of this jar's API.
- unsafe_ignore_missing_latest_api: true,
-}
diff --git a/apex/permission/service/api/current.txt b/apex/permission/service/api/current.txt
deleted file mode 100644
index d802177..0000000
--- a/apex/permission/service/api/current.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/apex/permission/service/api/removed.txt b/apex/permission/service/api/removed.txt
deleted file mode 100644
index d802177..0000000
--- a/apex/permission/service/api/removed.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/apex/permission/service/api/system-server-current.txt b/apex/permission/service/api/system-server-current.txt
deleted file mode 100644
index b1869c2c..0000000
--- a/apex/permission/service/api/system-server-current.txt
+++ /dev/null
@@ -1,54 +0,0 @@
-// Signature format: 2.0
-package com.android.permission.persistence {
-
- public interface RuntimePermissionsPersistence {
- method @NonNull public static com.android.permission.persistence.RuntimePermissionsPersistence createInstance();
- method public void deleteForUser(@NonNull android.os.UserHandle);
- method @Nullable public com.android.permission.persistence.RuntimePermissionsState readForUser(@NonNull android.os.UserHandle);
- method public void writeForUser(@NonNull com.android.permission.persistence.RuntimePermissionsState, @NonNull android.os.UserHandle);
- }
-
- public final class RuntimePermissionsState {
- ctor public RuntimePermissionsState(int, @Nullable String, @NonNull java.util.Map<java.lang.String,java.util.List<com.android.permission.persistence.RuntimePermissionsState.PermissionState>>, @NonNull java.util.Map<java.lang.String,java.util.List<com.android.permission.persistence.RuntimePermissionsState.PermissionState>>);
- method @Nullable public String getFingerprint();
- method @NonNull public java.util.Map<java.lang.String,java.util.List<com.android.permission.persistence.RuntimePermissionsState.PermissionState>> getPackagePermissions();
- method @NonNull public java.util.Map<java.lang.String,java.util.List<com.android.permission.persistence.RuntimePermissionsState.PermissionState>> getSharedUserPermissions();
- method public int getVersion();
- field public static final int NO_VERSION = -1; // 0xffffffff
- }
-
- public static final class RuntimePermissionsState.PermissionState {
- ctor public RuntimePermissionsState.PermissionState(@NonNull String, boolean, int);
- method public int getFlags();
- method @NonNull public String getName();
- method public boolean isGranted();
- }
-
-}
-
-package com.android.role {
-
- public interface RoleManagerLocal {
- method @NonNull public java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getRolesAndHolders(int);
- }
-
-}
-
-package com.android.role.persistence {
-
- public interface RolesPersistence {
- method @NonNull public static com.android.role.persistence.RolesPersistence createInstance();
- method public void deleteForUser(@NonNull android.os.UserHandle);
- method @Nullable public com.android.role.persistence.RolesState readForUser(@NonNull android.os.UserHandle);
- method public void writeForUser(@NonNull com.android.role.persistence.RolesState, @NonNull android.os.UserHandle);
- }
-
- public final class RolesState {
- ctor public RolesState(int, @Nullable String, @NonNull java.util.Map<java.lang.String,java.util.Set<java.lang.String>>);
- method @Nullable public String getPackagesHash();
- method @NonNull public java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getRoles();
- method public int getVersion();
- }
-
-}
-
diff --git a/apex/permission/service/api/system-server-removed.txt b/apex/permission/service/api/system-server-removed.txt
deleted file mode 100644
index d802177..0000000
--- a/apex/permission/service/api/system-server-removed.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/apex/permission/service/java/com/android/permission/compat/UserHandleCompat.java b/apex/permission/service/java/com/android/permission/compat/UserHandleCompat.java
deleted file mode 100644
index 7c711d3..0000000
--- a/apex/permission/service/java/com/android/permission/compat/UserHandleCompat.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.permission.compat;
-
-import android.annotation.UserIdInt;
-import android.os.UserHandle;
-
-/**
- * Helper for accessing features in {@link UserHandle}.
- */
-public final class UserHandleCompat {
- /**
- * A user ID to indicate all users on the device.
- */
- public static final int USER_ALL = UserHandle.ALL.getIdentifier();
-
- /**
- * A user ID to indicate the "system" user of the device.
- */
- public static final int USER_SYSTEM = UserHandle.SYSTEM.getIdentifier();
-
- private UserHandleCompat() {}
-
- /**
- * Get the user ID of a given UID.
- *
- * @param uid the UID
- * @return the user ID
- */
- @UserIdInt
- public static int getUserId(int uid) {
- return UserHandle.getUserHandleForUid(uid).getIdentifier();
- }
-}
diff --git a/apex/permission/service/java/com/android/permission/compat/package-info.java b/apex/permission/service/java/com/android/permission/compat/package-info.java
deleted file mode 100644
index c89cc8e..0000000
--- a/apex/permission/service/java/com/android/permission/compat/package-info.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * @hide
- * TODO(b/146466118) remove this javadoc tag
- */
-@android.annotation.Hide
-package com.android.permission.compat;
diff --git a/apex/permission/service/java/com/android/permission/persistence/IoUtils.java b/apex/permission/service/java/com/android/permission/persistence/IoUtils.java
deleted file mode 100644
index 569a78c..0000000
--- a/apex/permission/service/java/com/android/permission/persistence/IoUtils.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.permission.persistence;
-
-import android.annotation.NonNull;
-
-/**
- * Utility class for IO.
- *
- * @hide
- */
-public class IoUtils {
-
- private IoUtils() {}
-
- /**
- * Close 'closeable' ignoring any exceptions.
- */
- public static void closeQuietly(@NonNull AutoCloseable closeable) {
- try {
- closeable.close();
- } catch (Exception ignored) {
- // Ignored.
- }
- }
-}
diff --git a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistence.java b/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistence.java
deleted file mode 100644
index aedba29..0000000
--- a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistence.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.permission.persistence;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.annotation.SystemApi.Client;
-import android.os.UserHandle;
-
-/**
- * Persistence for runtime permissions.
- *
- * TODO(b/147914847): Remove @hide when it becomes the default.
- * @hide
- */
-@SystemApi(client = Client.SYSTEM_SERVER)
-public interface RuntimePermissionsPersistence {
-
- /**
- * Read the runtime permissions from persistence.
- *
- * This will perform I/O operations synchronously.
- *
- * @param user the user to read for
- * @return the runtime permissions read
- */
- @Nullable
- RuntimePermissionsState readForUser(@NonNull UserHandle user);
-
- /**
- * Write the runtime permissions to persistence.
- *
- * This will perform I/O operations synchronously.
- *
- * @param runtimePermissions the runtime permissions to write
- * @param user the user to write for
- */
- void writeForUser(@NonNull RuntimePermissionsState runtimePermissions,
- @NonNull UserHandle user);
-
- /**
- * Delete the runtime permissions from persistence.
- *
- * This will perform I/O operations synchronously.
- *
- * @param user the user to delete for
- */
- void deleteForUser(@NonNull UserHandle user);
-
- /**
- * Create a new instance of {@link RuntimePermissionsPersistence} implementation.
- *
- * @return the new instance.
- */
- @NonNull
- static RuntimePermissionsPersistence createInstance() {
- return new RuntimePermissionsPersistenceImpl();
- }
-}
diff --git a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java b/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java
deleted file mode 100644
index e43f59a..0000000
--- a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsPersistenceImpl.java
+++ /dev/null
@@ -1,265 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.permission.persistence;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.ApexEnvironment;
-import android.content.pm.PackageManager;
-import android.os.UserHandle;
-import android.util.ArrayMap;
-import android.util.AtomicFile;
-import android.util.Log;
-import android.util.Xml;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
-/**
- * Persistence implementation for runtime permissions.
- *
- * TODO(b/147914847): Remove @hide when it becomes the default.
- * @hide
- */
-public class RuntimePermissionsPersistenceImpl implements RuntimePermissionsPersistence {
-
- private static final String LOG_TAG = RuntimePermissionsPersistenceImpl.class.getSimpleName();
-
- private static final String APEX_MODULE_NAME = "com.android.permission";
-
- private static final String RUNTIME_PERMISSIONS_FILE_NAME = "runtime-permissions.xml";
-
- private static final String TAG_PACKAGE = "package";
- private static final String TAG_PERMISSION = "permission";
- private static final String TAG_RUNTIME_PERMISSIONS = "runtime-permissions";
- private static final String TAG_SHARED_USER = "shared-user";
-
- private static final String ATTRIBUTE_FINGERPRINT = "fingerprint";
- private static final String ATTRIBUTE_FLAGS = "flags";
- private static final String ATTRIBUTE_GRANTED = "granted";
- private static final String ATTRIBUTE_NAME = "name";
- private static final String ATTRIBUTE_VERSION = "version";
-
- @Nullable
- @Override
- public RuntimePermissionsState readForUser(@NonNull UserHandle user) {
- File file = getFile(user);
- try (FileInputStream inputStream = new AtomicFile(file).openRead()) {
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(inputStream, null);
- return parseXml(parser);
- } catch (FileNotFoundException e) {
- Log.i(LOG_TAG, "runtime-permissions.xml not found");
- return null;
- } catch (XmlPullParserException | IOException e) {
- throw new IllegalStateException("Failed to read runtime-permissions.xml: " + file , e);
- }
- }
-
- @NonNull
- private static RuntimePermissionsState parseXml(@NonNull XmlPullParser parser)
- throws IOException, XmlPullParserException {
- int type;
- int depth;
- int innerDepth = parser.getDepth() + 1;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) {
- if (depth > innerDepth || type != XmlPullParser.START_TAG) {
- continue;
- }
-
- if (parser.getName().equals(TAG_RUNTIME_PERMISSIONS)) {
- return parseRuntimePermissions(parser);
- }
- }
- throw new IllegalStateException("Missing <" + TAG_RUNTIME_PERMISSIONS
- + "> in runtime-permissions.xml");
- }
-
- @NonNull
- private static RuntimePermissionsState parseRuntimePermissions(@NonNull XmlPullParser parser)
- throws IOException, XmlPullParserException {
- String versionValue = parser.getAttributeValue(null, ATTRIBUTE_VERSION);
- int version = versionValue != null ? Integer.parseInt(versionValue)
- : RuntimePermissionsState.NO_VERSION;
- String fingerprint = parser.getAttributeValue(null, ATTRIBUTE_FINGERPRINT);
-
- Map<String, List<RuntimePermissionsState.PermissionState>> packagePermissions =
- new ArrayMap<>();
- Map<String, List<RuntimePermissionsState.PermissionState>> sharedUserPermissions =
- new ArrayMap<>();
- int type;
- int depth;
- int innerDepth = parser.getDepth() + 1;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) {
- if (depth > innerDepth || type != XmlPullParser.START_TAG) {
- continue;
- }
-
- switch (parser.getName()) {
- case TAG_PACKAGE: {
- String packageName = parser.getAttributeValue(null, ATTRIBUTE_NAME);
- List<RuntimePermissionsState.PermissionState> permissions = parsePermissions(
- parser);
- packagePermissions.put(packageName, permissions);
- break;
- }
- case TAG_SHARED_USER: {
- String sharedUserName = parser.getAttributeValue(null, ATTRIBUTE_NAME);
- List<RuntimePermissionsState.PermissionState> permissions = parsePermissions(
- parser);
- sharedUserPermissions.put(sharedUserName, permissions);
- break;
- }
- }
- }
-
- return new RuntimePermissionsState(version, fingerprint, packagePermissions,
- sharedUserPermissions);
- }
-
- @NonNull
- private static List<RuntimePermissionsState.PermissionState> parsePermissions(
- @NonNull XmlPullParser parser) throws IOException, XmlPullParserException {
- List<RuntimePermissionsState.PermissionState> permissions = new ArrayList<>();
- int type;
- int depth;
- int innerDepth = parser.getDepth() + 1;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) {
- if (depth > innerDepth || type != XmlPullParser.START_TAG) {
- continue;
- }
-
- if (parser.getName().equals(TAG_PERMISSION)) {
- String name = parser.getAttributeValue(null, ATTRIBUTE_NAME);
- boolean granted = Boolean.parseBoolean(parser.getAttributeValue(null,
- ATTRIBUTE_GRANTED));
- int flags = Integer.parseInt(parser.getAttributeValue(null,
- ATTRIBUTE_FLAGS), 16);
- RuntimePermissionsState.PermissionState permission =
- new RuntimePermissionsState.PermissionState(name, granted, flags);
- permissions.add(permission);
- }
- }
- return permissions;
- }
-
- @Override
- public void writeForUser(@NonNull RuntimePermissionsState runtimePermissions,
- @NonNull UserHandle user) {
- File file = getFile(user);
- AtomicFile atomicFile = new AtomicFile(file);
- FileOutputStream outputStream = null;
- try {
- outputStream = atomicFile.startWrite();
-
- XmlSerializer serializer = Xml.newSerializer();
- serializer.setOutput(outputStream, StandardCharsets.UTF_8.name());
- serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
- serializer.startDocument(null, true);
-
- serializeRuntimePermissions(serializer, runtimePermissions);
-
- serializer.endDocument();
- atomicFile.finishWrite(outputStream);
- } catch (Exception e) {
- Log.wtf(LOG_TAG, "Failed to write runtime-permissions.xml, restoring backup: " + file,
- e);
- atomicFile.failWrite(outputStream);
- } finally {
- IoUtils.closeQuietly(outputStream);
- }
- }
-
- private static void serializeRuntimePermissions(@NonNull XmlSerializer serializer,
- @NonNull RuntimePermissionsState runtimePermissions) throws IOException {
- serializer.startTag(null, TAG_RUNTIME_PERMISSIONS);
-
- int version = runtimePermissions.getVersion();
- serializer.attribute(null, ATTRIBUTE_VERSION, Integer.toString(version));
- String fingerprint = runtimePermissions.getFingerprint();
- if (fingerprint != null) {
- serializer.attribute(null, ATTRIBUTE_FINGERPRINT, fingerprint);
- }
-
- for (Map.Entry<String, List<RuntimePermissionsState.PermissionState>> entry
- : runtimePermissions.getPackagePermissions().entrySet()) {
- String packageName = entry.getKey();
- List<RuntimePermissionsState.PermissionState> permissions = entry.getValue();
-
- serializer.startTag(null, TAG_PACKAGE);
- serializer.attribute(null, ATTRIBUTE_NAME, packageName);
- serializePermissions(serializer, permissions);
- serializer.endTag(null, TAG_PACKAGE);
- }
-
- for (Map.Entry<String, List<RuntimePermissionsState.PermissionState>> entry
- : runtimePermissions.getSharedUserPermissions().entrySet()) {
- String sharedUserName = entry.getKey();
- List<RuntimePermissionsState.PermissionState> permissions = entry.getValue();
-
- serializer.startTag(null, TAG_SHARED_USER);
- serializer.attribute(null, ATTRIBUTE_NAME, sharedUserName);
- serializePermissions(serializer, permissions);
- serializer.endTag(null, TAG_SHARED_USER);
- }
-
- serializer.endTag(null, TAG_RUNTIME_PERMISSIONS);
- }
-
- private static void serializePermissions(@NonNull XmlSerializer serializer,
- @NonNull List<RuntimePermissionsState.PermissionState> permissions) throws IOException {
- int permissionsSize = permissions.size();
- for (int i = 0; i < permissionsSize; i++) {
- RuntimePermissionsState.PermissionState permissionState = permissions.get(i);
-
- serializer.startTag(null, TAG_PERMISSION);
- serializer.attribute(null, ATTRIBUTE_NAME, permissionState.getName());
- serializer.attribute(null, ATTRIBUTE_GRANTED, Boolean.toString(
- permissionState.isGranted() && (permissionState.getFlags()
- & PackageManager.FLAG_PERMISSION_ONE_TIME) == 0));
- serializer.attribute(null, ATTRIBUTE_FLAGS, Integer.toHexString(
- permissionState.getFlags()));
- serializer.endTag(null, TAG_PERMISSION);
- }
- }
-
- @Override
- public void deleteForUser(@NonNull UserHandle user) {
- getFile(user).delete();
- }
-
- @NonNull
- private static File getFile(@NonNull UserHandle user) {
- ApexEnvironment apexEnvironment = ApexEnvironment.getApexEnvironment(APEX_MODULE_NAME);
- File dataDirectory = apexEnvironment.getDeviceProtectedDataDirForUser(user);
- return new File(dataDirectory, RUNTIME_PERMISSIONS_FILE_NAME);
- }
-}
diff --git a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsState.java b/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsState.java
deleted file mode 100644
index c6bfc6d..0000000
--- a/apex/permission/service/java/com/android/permission/persistence/RuntimePermissionsState.java
+++ /dev/null
@@ -1,222 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.permission.persistence;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.annotation.SystemApi.Client;
-
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-
-/**
- * State of all runtime permissions.
- *
- * TODO(b/147914847): Remove @hide when it becomes the default.
- * @hide
- */
-@SystemApi(client = Client.SYSTEM_SERVER)
-public final class RuntimePermissionsState {
-
- /**
- * Special value for {@link #mVersion} to indicate that no version was read.
- */
- public static final int NO_VERSION = -1;
-
- /**
- * The version of the runtime permissions.
- */
- private final int mVersion;
-
- /**
- * The fingerprint of the runtime permissions.
- */
- @Nullable
- private final String mFingerprint;
-
- /**
- * The runtime permissions by packages.
- */
- @NonNull
- private final Map<String, List<PermissionState>> mPackagePermissions;
-
- /**
- * The runtime permissions by shared users.
- */
- @NonNull
- private final Map<String, List<PermissionState>> mSharedUserPermissions;
-
- /**
- * Create a new instance of this class.
- *
- * @param version the version of the runtime permissions
- * @param fingerprint the fingerprint of the runtime permissions
- * @param packagePermissions the runtime permissions by packages
- * @param sharedUserPermissions the runtime permissions by shared users
- */
- public RuntimePermissionsState(int version, @Nullable String fingerprint,
- @NonNull Map<String, List<PermissionState>> packagePermissions,
- @NonNull Map<String, List<PermissionState>> sharedUserPermissions) {
- mVersion = version;
- mFingerprint = fingerprint;
- mPackagePermissions = packagePermissions;
- mSharedUserPermissions = sharedUserPermissions;
- }
-
- /**
- * Get the version of the runtime permissions.
- *
- * @return the version of the runtime permissions
- */
- public int getVersion() {
- return mVersion;
- }
-
- /**
- * Get the fingerprint of the runtime permissions.
- *
- * @return the fingerprint of the runtime permissions
- */
- @Nullable
- public String getFingerprint() {
- return mFingerprint;
- }
-
- /**
- * Get the runtime permissions by packages.
- *
- * @return the runtime permissions by packages
- */
- @NonNull
- public Map<String, List<PermissionState>> getPackagePermissions() {
- return mPackagePermissions;
- }
-
- /**
- * Get the runtime permissions by shared users.
- *
- * @return the runtime permissions by shared users
- */
- @NonNull
- public Map<String, List<PermissionState>> getSharedUserPermissions() {
- return mSharedUserPermissions;
- }
-
- @Override
- public boolean equals(Object object) {
- if (this == object) {
- return true;
- }
- if (object == null || getClass() != object.getClass()) {
- return false;
- }
- RuntimePermissionsState that = (RuntimePermissionsState) object;
- return mVersion == that.mVersion
- && Objects.equals(mFingerprint, that.mFingerprint)
- && Objects.equals(mPackagePermissions, that.mPackagePermissions)
- && Objects.equals(mSharedUserPermissions, that.mSharedUserPermissions);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mVersion, mFingerprint, mPackagePermissions, mSharedUserPermissions);
- }
-
- /**
- * State of a single permission.
- */
- public static final class PermissionState {
-
- /**
- * The name of the permission.
- */
- @NonNull
- private final String mName;
-
- /**
- * Whether the permission is granted.
- */
- private final boolean mGranted;
-
- /**
- * The flags of the permission.
- */
- private final int mFlags;
-
- /**
- * Create a new instance of this class.
- *
- * @param name the name of the permission
- * @param granted whether the permission is granted
- * @param flags the flags of the permission
- */
- public PermissionState(@NonNull String name, boolean granted, int flags) {
- mName = name;
- mGranted = granted;
- mFlags = flags;
- }
-
- /**
- * Get the name of the permission.
- *
- * @return the name of the permission
- */
- @NonNull
- public String getName() {
- return mName;
- }
-
- /**
- * Get whether the permission is granted.
- *
- * @return whether the permission is granted
- */
- public boolean isGranted() {
- return mGranted;
- }
-
- /**
- * Get the flags of the permission.
- *
- * @return the flags of the permission
- */
- public int getFlags() {
- return mFlags;
- }
-
- @Override
- public boolean equals(Object object) {
- if (this == object) {
- return true;
- }
- if (object == null || getClass() != object.getClass()) {
- return false;
- }
- PermissionState that = (PermissionState) object;
- return mGranted == that.mGranted
- && mFlags == that.mFlags
- && Objects.equals(mName, that.mName);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mName, mGranted, mFlags);
- }
- }
-}
diff --git a/apex/permission/service/java/com/android/permission/util/ArrayUtils.java b/apex/permission/service/java/com/android/permission/util/ArrayUtils.java
deleted file mode 100644
index 5d5cd78..0000000
--- a/apex/permission/service/java/com/android/permission/util/ArrayUtils.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.permission.util;
-
-import android.annotation.Nullable;
-
-import java.util.Objects;
-
-/**
- * Array utilities.
- */
-public final class ArrayUtils {
- private ArrayUtils() {}
-
- /**
- * @see java.util.List#contains(Object)
- */
- public static <T> boolean contains(@Nullable T[] array, T value) {
- return indexOf(array, value) != -1;
- }
-
- /**
- * Get the first element of an array, or {@code null} if none.
- *
- * @param array the array
- * @param <T> the type of the elements of the array
- * @return first element of an array, or {@code null} if none
- */
- public static <T> T firstOrNull(@Nullable T[] array) {
- return !isEmpty(array) ? array[0] : null;
- }
-
- /**
- * @see java.util.List#indexOf(Object)
- */
- public static <T> int indexOf(@Nullable T[] array, T value) {
- if (array == null) {
- return -1;
- }
- final int length = array.length;
- for (int i = 0; i < length; i++) {
- final T element = array[i];
- if (Objects.equals(element, value)) {
- return i;
- }
- }
- return -1;
- }
-
- /**
- * @see java.util.List#isEmpty()
- */
- public static <T> boolean isEmpty(@Nullable T[] array) {
- return array == null || array.length == 0;
- }
-}
diff --git a/apex/permission/service/java/com/android/permission/util/BackgroundThread.java b/apex/permission/service/java/com/android/permission/util/BackgroundThread.java
deleted file mode 100644
index 7308eec..0000000
--- a/apex/permission/service/java/com/android/permission/util/BackgroundThread.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.permission.util;
-
-import android.annotation.NonNull;
-import android.os.Handler;
-import android.os.HandlerExecutor;
-import android.os.HandlerThread;
-
-import com.android.internal.annotations.GuardedBy;
-
-import java.util.concurrent.Executor;
-
-/**
- * Shared singleton background thread.
- */
-public class BackgroundThread extends HandlerThread {
- private static final Object sLock = new Object();
-
- @GuardedBy("sLock")
- private static BackgroundThread sInstance;
- @GuardedBy("sLock")
- private static Handler sHandler;
- @GuardedBy("sLock")
- private static Executor sExecutor;
-
- private BackgroundThread() {
- super(BackgroundThread.class.getName());
- }
-
- @GuardedBy("sLock")
- private static void ensureInstanceLocked() {
- if (sInstance == null) {
- sInstance = new BackgroundThread();
- sInstance.start();
- sHandler = new Handler(sInstance.getLooper());
- sExecutor = new HandlerExecutor(sHandler);
- }
- }
-
- /**
- * Get the singleton instance of thi class.
- *
- * @return the singleton instance of thi class
- */
- @NonNull
- public static BackgroundThread get() {
- synchronized (sLock) {
- ensureInstanceLocked();
- return sInstance;
- }
- }
-
- /**
- * Get the {@link Handler} for this thread.
- *
- * @return the {@link Handler} for this thread.
- */
- @NonNull
- public static Handler getHandler() {
- synchronized (sLock) {
- ensureInstanceLocked();
- return sHandler;
- }
- }
-
- /**
- * Get the {@link Executor} for this thread.
- *
- * @return the {@link Executor} for this thread.
- */
- @NonNull
- public static Executor getExecutor() {
- synchronized (sLock) {
- ensureInstanceLocked();
- return sExecutor;
- }
- }
-}
diff --git a/apex/permission/service/java/com/android/permission/util/CollectionUtils.java b/apex/permission/service/java/com/android/permission/util/CollectionUtils.java
deleted file mode 100644
index ea49524..0000000
--- a/apex/permission/service/java/com/android/permission/util/CollectionUtils.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.permission.util;
-
-import android.annotation.Nullable;
-
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-
-/**
- * {@link Collection} utilities.
- */
-public class CollectionUtils {
- private CollectionUtils() {}
-
- /**
- * Get the first element of a {@link List}, or {@code null} if none.
- *
- * @param list the {@link List}, or {@code null}
- * @param <E> the element type of the {@link List}
- * @return the first element of the {@link List}, or {@code 0} if none
- */
- @Nullable
- public static <E> E firstOrNull(@Nullable List<E> list) {
- return !isEmpty(list) ? list.get(0) : null;
- }
-
- /**
- * Check whether a {@link Collection} is empty or {@code null}.
- *
- * @param collection the {@link Collection}, or {@code null}
- * @return whether the {@link Collection} is empty or {@code null}
- */
- public static boolean isEmpty(@Nullable Collection<?> collection) {
- return collection == null || collection.isEmpty();
- }
-
- /**
- * Get the size of a {@link Collection}, or {@code 0} if {@code null}.
- *
- * @param collection the {@link Collection}, or {@code null}
- * @return the size of the {@link Collection}, or {@code 0} if {@code null}
- */
- public static int size(@Nullable Collection<?> collection) {
- return collection != null ? collection.size() : 0;
- }
-
- /**
- * Get the size of a {@link Map}, or {@code 0} if {@code null}.
- *
- * @param collection the {@link Map}, or {@code null}
- * @return the size of the {@link Map}, or {@code 0} if {@code null}
- */
- public static int size(@Nullable Map<?, ?> collection) {
- return collection != null ? collection.size() : 0;
- }
-}
diff --git a/apex/permission/service/java/com/android/permission/util/ForegroundThread.java b/apex/permission/service/java/com/android/permission/util/ForegroundThread.java
deleted file mode 100644
index cd6f605..0000000
--- a/apex/permission/service/java/com/android/permission/util/ForegroundThread.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.permission.util;
-
-import android.annotation.NonNull;
-import android.os.Handler;
-import android.os.HandlerExecutor;
-import android.os.HandlerThread;
-
-import com.android.internal.annotations.GuardedBy;
-
-import java.util.concurrent.Executor;
-
-/**
- * Shared singleton foreground thread.
- */
-public class ForegroundThread extends HandlerThread {
- private static final Object sLock = new Object();
-
- @GuardedBy("sLock")
- private static ForegroundThread sInstance;
- @GuardedBy("sLock")
- private static Handler sHandler;
- @GuardedBy("sLock")
- private static Executor sExecutor;
-
- private ForegroundThread() {
- super(ForegroundThread.class.getName());
- }
-
- @GuardedBy("sLock")
- private static void ensureInstanceLocked() {
- if (sInstance == null) {
- sInstance = new ForegroundThread();
- sInstance.start();
- sHandler = new Handler(sInstance.getLooper());
- sExecutor = new HandlerExecutor(sHandler);
- }
- }
-
- /**
- * Get the singleton instance of thi class.
- *
- * @return the singleton instance of thi class
- */
- @NonNull
- public static ForegroundThread get() {
- synchronized (sLock) {
- ensureInstanceLocked();
- return sInstance;
- }
- }
-
- /**
- * Get the {@link Handler} for this thread.
- *
- * @return the {@link Handler} for this thread.
- */
- @NonNull
- public static Handler getHandler() {
- synchronized (sLock) {
- ensureInstanceLocked();
- return sHandler;
- }
- }
-
- /**
- * Get the {@link Executor} for this thread.
- *
- * @return the {@link Executor} for this thread.
- */
- @NonNull
- public static Executor getExecutor() {
- synchronized (sLock) {
- ensureInstanceLocked();
- return sExecutor;
- }
- }
-}
diff --git a/apex/permission/service/java/com/android/permission/util/ThrottledRunnable.java b/apex/permission/service/java/com/android/permission/util/ThrottledRunnable.java
deleted file mode 100644
index ba1c393..0000000
--- a/apex/permission/service/java/com/android/permission/util/ThrottledRunnable.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.permission.util;
-
-import android.annotation.NonNull;
-import android.os.Handler;
-import android.os.SystemClock;
-
-import com.android.internal.annotations.GuardedBy;
-
-/**
- * A throttled runnable that can wrap around a runnable and throttle calls to its run().
- *
- * The throttling logic makes sure that the original runnable will be called only after the
- * specified interval passes since the last actual call. The first call in a while (after the
- * specified interval passes since the last actual call) will always result in the original runnable
- * being called immediately, and then subsequent calls will start to be throttled. It is guaranteed
- * that any call to this throttled runnable will always result in the original runnable being called
- * afterwards, within the specified interval.
- */
-public class ThrottledRunnable implements Runnable {
-
- @NonNull
- private final Handler mHandler;
- private final long mIntervalMillis;
- @NonNull
- private final Runnable mRunnable;
-
- @NonNull
- private final Object mLock = new Object();
-
- @GuardedBy("mLock")
- private long mScheduledUptimeMillis;
-
- public ThrottledRunnable(@NonNull Handler handler, long intervalMillis,
- @NonNull Runnable runnable) {
- mHandler = handler;
- mIntervalMillis = intervalMillis;
- mRunnable = runnable;
- }
-
- @Override
- public void run() {
- synchronized (mLock) {
- if (mHandler.hasCallbacks(mRunnable)) {
- // We have a scheduled runnable.
- return;
- }
- long currentUptimeMillis = SystemClock.uptimeMillis();
- if (mScheduledUptimeMillis == 0
- || currentUptimeMillis > mScheduledUptimeMillis + mIntervalMillis) {
- // First time in a while, schedule immediately.
- mScheduledUptimeMillis = currentUptimeMillis;
- } else {
- // We were scheduled not long ago, so schedule with delay for throttling.
- mScheduledUptimeMillis = mScheduledUptimeMillis + mIntervalMillis;
- }
- mHandler.postAtTime(mRunnable, mScheduledUptimeMillis);
- }
- }
-}
diff --git a/apex/permission/service/java/com/android/role/RoleManagerLocal.java b/apex/permission/service/java/com/android/role/RoleManagerLocal.java
deleted file mode 100644
index e243e2e..0000000
--- a/apex/permission/service/java/com/android/role/RoleManagerLocal.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.role;
-
-import android.annotation.NonNull;
-import android.annotation.SystemApi;
-import android.annotation.UserIdInt;
-
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Internal calls into {@link RoleService}.
- *
- * @hide
- */
-@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
-public interface RoleManagerLocal {
- /**
- * Get all roles and their holders.
- *
- * @param userId The user to query to roles for
- *
- * @return The roles and their holders
- */
- @NonNull
- Map<String, Set<String>> getRolesAndHolders(@UserIdInt int userId);
-}
diff --git a/apex/permission/service/java/com/android/role/RoleService.java b/apex/permission/service/java/com/android/role/RoleService.java
deleted file mode 100644
index 5f7eb22..0000000
--- a/apex/permission/service/java/com/android/role/RoleService.java
+++ /dev/null
@@ -1,736 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.role;
-
-import android.Manifest;
-import android.annotation.AnyThread;
-import android.annotation.MainThread;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.UserIdInt;
-import android.annotation.WorkerThread;
-import android.app.AppOpsManager;
-import android.app.role.IOnRoleHoldersChangedListener;
-import android.app.role.IRoleManager;
-import android.app.role.RoleControllerManager;
-import android.app.role.RoleManager;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.PackageManager;
-import android.os.Binder;
-import android.os.Handler;
-import android.os.ParcelFileDescriptor;
-import android.os.Process;
-import android.os.RemoteCallback;
-import android.os.RemoteCallbackList;
-import android.os.RemoteException;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.text.TextUtils;
-import android.util.ArraySet;
-import android.util.IndentingPrintWriter;
-import android.util.Log;
-import android.util.SparseArray;
-import android.util.proto.ProtoOutputStream;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.infra.AndroidFuture;
-import com.android.internal.util.Preconditions;
-import com.android.internal.util.dump.DualDumpOutputStream;
-import com.android.permission.compat.UserHandleCompat;
-import com.android.permission.util.ArrayUtils;
-import com.android.permission.util.CollectionUtils;
-import com.android.permission.util.ForegroundThread;
-import com.android.permission.util.ThrottledRunnable;
-import com.android.server.LocalManagerRegistry;
-import com.android.server.SystemService;
-import com.android.server.role.RoleServicePlatformHelper;
-
-import java.io.FileDescriptor;
-import java.io.FileOutputStream;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-/**
- * Service for role management.
- *
- * @see RoleManager
- */
-public class RoleService extends SystemService implements RoleUserState.Callback {
- private static final String LOG_TAG = RoleService.class.getSimpleName();
-
- private static final boolean DEBUG = false;
-
- private static final long GRANT_DEFAULT_ROLES_INTERVAL_MILLIS = 1000;
-
- @NonNull
- private final AppOpsManager mAppOpsManager;
- @NonNull
- private final UserManager mUserManager;
-
- @NonNull
- private final Object mLock = new Object();
-
- @NonNull
- private final RoleServicePlatformHelper mPlatformHelper;
-
- /**
- * Maps user id to its state.
- */
- @GuardedBy("mLock")
- @NonNull
- private final SparseArray<RoleUserState> mUserStates = new SparseArray<>();
-
- /**
- * Maps user id to its controller.
- */
- @GuardedBy("mLock")
- @NonNull
- private final SparseArray<RoleControllerManager> mControllers = new SparseArray<>();
-
- /**
- * Maps user id to its list of listeners.
- */
- @GuardedBy("mLock")
- @NonNull
- private final SparseArray<RemoteCallbackList<IOnRoleHoldersChangedListener>> mListeners =
- new SparseArray<>();
-
- @NonNull
- private final Handler mListenerHandler = ForegroundThread.getHandler();
-
- /**
- * Maps user id to its throttled runnable for granting default roles.
- */
- @GuardedBy("mLock")
- @NonNull
- private final SparseArray<ThrottledRunnable> mGrantDefaultRolesThrottledRunnables =
- new SparseArray<>();
-
- public RoleService(@NonNull Context context) {
- super(context);
-
- mPlatformHelper = LocalManagerRegistry.getManager(RoleServicePlatformHelper.class);
-
- RoleControllerManager.initializeRemoteServiceComponentName(context);
-
- mAppOpsManager = context.getSystemService(AppOpsManager.class);
- mUserManager = context.getSystemService(UserManager.class);
-
- LocalManagerRegistry.addManager(RoleManagerLocal.class, new Local());
-
- registerUserRemovedReceiver();
- }
-
- private void registerUserRemovedReceiver() {
- IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(Intent.ACTION_USER_REMOVED);
- getContext().registerReceiverForAllUsers(new BroadcastReceiver() {
- @Override
- public void onReceive(@NonNull Context context, @NonNull Intent intent) {
- if (TextUtils.equals(intent.getAction(), Intent.ACTION_USER_REMOVED)) {
- int userId = intent.<UserHandle>getParcelableExtra(Intent.EXTRA_USER)
- .getIdentifier();
- onRemoveUser(userId);
- }
- }
- }, intentFilter, null, null);
- }
-
- @Override
- public void onStart() {
- publishBinderService(Context.ROLE_SERVICE, new Stub());
-
- IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
- intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
- intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
- intentFilter.addDataScheme("package");
- intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
- getContext().registerReceiverForAllUsers(new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- int userId = UserHandleCompat.getUserId(intent.getIntExtra(Intent.EXTRA_UID, -1));
- if (DEBUG) {
- Log.i(LOG_TAG, "Packages changed - re-running initial grants for user "
- + userId);
- }
- if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())
- && intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
- // Package is being upgraded - we're about to get ACTION_PACKAGE_ADDED
- return;
- }
- maybeGrantDefaultRolesAsync(userId);
- }
- }, intentFilter, null, null);
- }
-
- @Override
- public void onUserStarting(@NonNull TargetUser user) {
- maybeGrantDefaultRolesSync(user.getUserHandle().getIdentifier());
- }
-
- @MainThread
- private void maybeGrantDefaultRolesSync(@UserIdInt int userId) {
- AndroidFuture<Void> future = maybeGrantDefaultRolesInternal(userId);
- try {
- future.get(30, TimeUnit.SECONDS);
- } catch (InterruptedException | ExecutionException | TimeoutException e) {
- Log.e(LOG_TAG, "Failed to grant default roles for user " + userId, e);
- }
- }
-
- private void maybeGrantDefaultRolesAsync(@UserIdInt int userId) {
- ThrottledRunnable runnable;
- synchronized (mLock) {
- runnable = mGrantDefaultRolesThrottledRunnables.get(userId);
- if (runnable == null) {
- runnable = new ThrottledRunnable(ForegroundThread.getHandler(),
- GRANT_DEFAULT_ROLES_INTERVAL_MILLIS,
- () -> maybeGrantDefaultRolesInternal(userId));
- mGrantDefaultRolesThrottledRunnables.put(userId, runnable);
- }
- }
- runnable.run();
- }
-
- @AnyThread
- @NonNull
- private AndroidFuture<Void> maybeGrantDefaultRolesInternal(@UserIdInt int userId) {
- RoleUserState userState = getOrCreateUserState(userId);
- String oldPackagesHash = userState.getPackagesHash();
- String newPackagesHash = mPlatformHelper.computePackageStateHash(userId);
- if (Objects.equals(oldPackagesHash, newPackagesHash)) {
- if (DEBUG) {
- Log.i(LOG_TAG, "Already granted default roles for packages hash "
- + newPackagesHash);
- }
- return AndroidFuture.completedFuture(null);
- }
-
- // Some package state has changed, so grant default roles again.
- Log.i(LOG_TAG, "Granting default roles...");
- AndroidFuture<Void> future = new AndroidFuture<>();
- getOrCreateController(userId).grantDefaultRoles(ForegroundThread.getExecutor(),
- successful -> {
- if (successful) {
- userState.setPackagesHash(newPackagesHash);
- future.complete(null);
- } else {
- future.completeExceptionally(new RuntimeException());
- }
- });
- return future;
- }
-
- @NonNull
- private RoleUserState getOrCreateUserState(@UserIdInt int userId) {
- synchronized (mLock) {
- RoleUserState userState = mUserStates.get(userId);
- if (userState == null) {
- userState = new RoleUserState(userId, mPlatformHelper, this);
- mUserStates.put(userId, userState);
- }
- return userState;
- }
- }
-
- @NonNull
- private RoleControllerManager getOrCreateController(@UserIdInt int userId) {
- synchronized (mLock) {
- RoleControllerManager controller = mControllers.get(userId);
- if (controller == null) {
- Context systemContext = getContext();
- Context context;
- try {
- context = systemContext.createPackageContextAsUser(
- systemContext.getPackageName(), 0, UserHandle.of(userId));
- } catch (PackageManager.NameNotFoundException e) {
- throw new RuntimeException(e);
- }
- controller = RoleControllerManager.createWithInitializedRemoteServiceComponentName(
- ForegroundThread.getHandler(), context);
- mControllers.put(userId, controller);
- }
- return controller;
- }
- }
-
- @Nullable
- private RemoteCallbackList<IOnRoleHoldersChangedListener> getListeners(@UserIdInt int userId) {
- synchronized (mLock) {
- return mListeners.get(userId);
- }
- }
-
- @NonNull
- private RemoteCallbackList<IOnRoleHoldersChangedListener> getOrCreateListeners(
- @UserIdInt int userId) {
- synchronized (mLock) {
- RemoteCallbackList<IOnRoleHoldersChangedListener> listeners = mListeners.get(userId);
- if (listeners == null) {
- listeners = new RemoteCallbackList<>();
- mListeners.put(userId, listeners);
- }
- return listeners;
- }
- }
-
- private void onRemoveUser(@UserIdInt int userId) {
- RemoteCallbackList<IOnRoleHoldersChangedListener> listeners;
- RoleUserState userState;
- synchronized (mLock) {
- mGrantDefaultRolesThrottledRunnables.remove(userId);
- listeners = mListeners.get(userId);
- mListeners.remove(userId);
- mControllers.remove(userId);
- userState = mUserStates.get(userId);
- mUserStates.remove(userId);
- }
- if (listeners != null) {
- listeners.kill();
- }
- if (userState != null) {
- userState.destroy();
- }
- }
-
- @Override
- public void onRoleHoldersChanged(@NonNull String roleName, @UserIdInt int userId) {
- mListenerHandler.post(() -> notifyRoleHoldersChanged(roleName, userId));
- }
-
- @WorkerThread
- private void notifyRoleHoldersChanged(@NonNull String roleName, @UserIdInt int userId) {
- RemoteCallbackList<IOnRoleHoldersChangedListener> listeners = getListeners(userId);
- if (listeners != null) {
- notifyRoleHoldersChangedForListeners(listeners, roleName, userId);
- }
-
- RemoteCallbackList<IOnRoleHoldersChangedListener> allUsersListeners = getListeners(
- UserHandleCompat.USER_ALL);
- if (allUsersListeners != null) {
- notifyRoleHoldersChangedForListeners(allUsersListeners, roleName, userId);
- }
- }
-
- @WorkerThread
- private void notifyRoleHoldersChangedForListeners(
- @NonNull RemoteCallbackList<IOnRoleHoldersChangedListener> listeners,
- @NonNull String roleName, @UserIdInt int userId) {
- int broadcastCount = listeners.beginBroadcast();
- try {
- for (int i = 0; i < broadcastCount; i++) {
- IOnRoleHoldersChangedListener listener = listeners.getBroadcastItem(i);
- try {
- listener.onRoleHoldersChanged(roleName, userId);
- } catch (RemoteException e) {
- Log.e(LOG_TAG, "Error calling OnRoleHoldersChangedListener", e);
- }
- }
- } finally {
- listeners.finishBroadcast();
- }
- }
-
- private class Stub extends IRoleManager.Stub {
-
- @Override
- public boolean isRoleAvailable(@NonNull String roleName) {
- Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
-
- int userId = UserHandleCompat.getUserId(getCallingUid());
- return getOrCreateUserState(userId).isRoleAvailable(roleName);
- }
-
- @Override
- public boolean isRoleHeld(@NonNull String roleName, @NonNull String packageName) {
- int callingUid = getCallingUid();
- mAppOpsManager.checkPackage(callingUid, packageName);
-
- Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
- Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
-
- int userId = UserHandleCompat.getUserId(callingUid);
- ArraySet<String> roleHolders = getOrCreateUserState(userId).getRoleHolders(roleName);
- if (roleHolders == null) {
- return false;
- }
- return roleHolders.contains(packageName);
- }
-
- @NonNull
- @Override
- public List<String> getRoleHoldersAsUser(@NonNull String roleName, @UserIdInt int userId) {
- if (!isUserExistent(userId)) {
- Log.e(LOG_TAG, "user " + userId + " does not exist");
- return Collections.emptyList();
- }
- enforceCrossUserPermission(userId, false, "getRoleHoldersAsUser");
- getContext().enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ROLE_HOLDERS,
- "getRoleHoldersAsUser");
-
- Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
-
- ArraySet<String> roleHolders = getOrCreateUserState(userId).getRoleHolders(roleName);
- if (roleHolders == null) {
- return Collections.emptyList();
- }
- return new ArrayList<>(roleHolders);
- }
-
- @Override
- public void addRoleHolderAsUser(@NonNull String roleName, @NonNull String packageName,
- @RoleManager.ManageHoldersFlags int flags, @UserIdInt int userId,
- @NonNull RemoteCallback callback) {
- if (!isUserExistent(userId)) {
- Log.e(LOG_TAG, "user " + userId + " does not exist");
- return;
- }
- enforceCrossUserPermission(userId, false, "addRoleHolderAsUser");
- getContext().enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ROLE_HOLDERS,
- "addRoleHolderAsUser");
-
- Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
- Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
- Objects.requireNonNull(callback, "callback cannot be null");
-
- getOrCreateController(userId).onAddRoleHolder(roleName, packageName, flags,
- callback);
- }
-
- @Override
- public void removeRoleHolderAsUser(@NonNull String roleName, @NonNull String packageName,
- @RoleManager.ManageHoldersFlags int flags, @UserIdInt int userId,
- @NonNull RemoteCallback callback) {
- if (!isUserExistent(userId)) {
- Log.e(LOG_TAG, "user " + userId + " does not exist");
- return;
- }
- enforceCrossUserPermission(userId, false, "removeRoleHolderAsUser");
- getContext().enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ROLE_HOLDERS,
- "removeRoleHolderAsUser");
-
- Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
- Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
- Objects.requireNonNull(callback, "callback cannot be null");
-
- getOrCreateController(userId).onRemoveRoleHolder(roleName, packageName, flags,
- callback);
- }
-
- @Override
- public void clearRoleHoldersAsUser(@NonNull String roleName,
- @RoleManager.ManageHoldersFlags int flags, @UserIdInt int userId,
- @NonNull RemoteCallback callback) {
- if (!isUserExistent(userId)) {
- Log.e(LOG_TAG, "user " + userId + " does not exist");
- return;
- }
- enforceCrossUserPermission(userId, false, "clearRoleHoldersAsUser");
- getContext().enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ROLE_HOLDERS,
- "clearRoleHoldersAsUser");
-
- Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
- Objects.requireNonNull(callback, "callback cannot be null");
-
- getOrCreateController(userId).onClearRoleHolders(roleName, flags, callback);
- }
-
- @Override
- public void addOnRoleHoldersChangedListenerAsUser(
- @NonNull IOnRoleHoldersChangedListener listener, @UserIdInt int userId) {
- if (userId != UserHandleCompat.USER_ALL && !isUserExistent(userId)) {
- Log.e(LOG_TAG, "user " + userId + " does not exist");
- return;
- }
- enforceCrossUserPermission(userId, true, "addOnRoleHoldersChangedListenerAsUser");
- getContext().enforceCallingOrSelfPermission(Manifest.permission.OBSERVE_ROLE_HOLDERS,
- "addOnRoleHoldersChangedListenerAsUser");
-
- Objects.requireNonNull(listener, "listener cannot be null");
-
- RemoteCallbackList<IOnRoleHoldersChangedListener> listeners = getOrCreateListeners(
- userId);
- listeners.register(listener);
- }
-
- @Override
- public void removeOnRoleHoldersChangedListenerAsUser(
- @NonNull IOnRoleHoldersChangedListener listener, @UserIdInt int userId) {
- if (userId != UserHandleCompat.USER_ALL && !isUserExistent(userId)) {
- Log.e(LOG_TAG, "user " + userId + " does not exist");
- return;
- }
- enforceCrossUserPermission(userId, true, "removeOnRoleHoldersChangedListenerAsUser");
- getContext().enforceCallingOrSelfPermission(Manifest.permission.OBSERVE_ROLE_HOLDERS,
- "removeOnRoleHoldersChangedListenerAsUser");
-
- Objects.requireNonNull(listener, "listener cannot be null");
-
- RemoteCallbackList<IOnRoleHoldersChangedListener> listeners = getListeners(userId);
- if (listener == null) {
- return;
- }
- listeners.unregister(listener);
- }
-
- @Override
- public void setRoleNamesFromController(@NonNull List<String> roleNames) {
- getContext().enforceCallingOrSelfPermission(
- RoleManager.PERMISSION_MANAGE_ROLES_FROM_CONTROLLER,
- "setRoleNamesFromController");
-
- Objects.requireNonNull(roleNames, "roleNames cannot be null");
-
- int userId = UserHandleCompat.getUserId(Binder.getCallingUid());
- getOrCreateUserState(userId).setRoleNames(roleNames);
- }
-
- @Override
- public boolean addRoleHolderFromController(@NonNull String roleName,
- @NonNull String packageName) {
- getContext().enforceCallingOrSelfPermission(
- RoleManager.PERMISSION_MANAGE_ROLES_FROM_CONTROLLER,
- "addRoleHolderFromController");
-
- Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
- Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
-
- int userId = UserHandleCompat.getUserId(Binder.getCallingUid());
- return getOrCreateUserState(userId).addRoleHolder(roleName, packageName);
- }
-
- @Override
- public boolean removeRoleHolderFromController(@NonNull String roleName,
- @NonNull String packageName) {
- getContext().enforceCallingOrSelfPermission(
- RoleManager.PERMISSION_MANAGE_ROLES_FROM_CONTROLLER,
- "removeRoleHolderFromController");
-
- Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
- Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
-
- int userId = UserHandleCompat.getUserId(Binder.getCallingUid());
- return getOrCreateUserState(userId).removeRoleHolder(roleName, packageName);
- }
-
- @Override
- public List<String> getHeldRolesFromController(@NonNull String packageName) {
- getContext().enforceCallingOrSelfPermission(
- RoleManager.PERMISSION_MANAGE_ROLES_FROM_CONTROLLER,
- "getRolesHeldFromController");
-
- Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
-
- int userId = UserHandleCompat.getUserId(Binder.getCallingUid());
- return getOrCreateUserState(userId).getHeldRoles(packageName);
- }
-
- private boolean isUserExistent(@UserIdInt int userId) {
- // FIXME: This checks whether the user is alive, but we should check for whether the
- // user is existent.
- return mUserManager.getUserHandles(true).contains(UserHandle.of(userId));
- }
-
- private void enforceCrossUserPermission(@UserIdInt int userId, boolean allowAll,
- @NonNull String message) {
- final int callingUid = Binder.getCallingUid();
- final int callingUserId = UserHandleCompat.getUserId(callingUid);
- if (userId == callingUserId) {
- return;
- }
- Preconditions.checkArgument(userId >= UserHandleCompat.USER_SYSTEM
- || (allowAll && userId == UserHandleCompat.USER_ALL), "Invalid user " + userId);
- getContext().enforceCallingOrSelfPermission(
- android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
- if (callingUid == Process.SHELL_UID && userId >= UserHandleCompat.USER_SYSTEM) {
- if (mUserManager.hasUserRestrictionForUser(UserManager.DISALLOW_DEBUGGING_FEATURES,
- UserHandle.of(userId))) {
- throw new SecurityException("Shell does not have permission to access user "
- + userId);
- }
- }
- }
-
- @Override
- public int handleShellCommand(@NonNull ParcelFileDescriptor in,
- @NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err,
- @NonNull String[] args) {
- return new RoleShellCommand(this).exec(this, in.getFileDescriptor(),
- out.getFileDescriptor(), err.getFileDescriptor(), args);
- }
-
- @Nullable
- @Override
- public String getBrowserRoleHolder(@UserIdInt int userId) {
- final int callingUid = Binder.getCallingUid();
- if (UserHandleCompat.getUserId(callingUid) != userId) {
- getContext().enforceCallingOrSelfPermission(
- android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
- }
- if (isInstantApp(callingUid)) {
- return null;
- }
-
- final long identity = Binder.clearCallingIdentity();
- try {
- return CollectionUtils.firstOrNull(getRoleHoldersAsUser(RoleManager.ROLE_BROWSER,
- userId));
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- private boolean isInstantApp(int uid) {
- final long identity = Binder.clearCallingIdentity();
- try {
- final UserHandle user = UserHandle.getUserHandleForUid(uid);
- final Context userContext = getContext().createContextAsUser(user, 0);
- final PackageManager userPackageManager = userContext.getPackageManager();
- // Instant apps can not have shared UID, so it's safe to check only the first
- // package name here.
- final String packageName = ArrayUtils.firstOrNull(
- userPackageManager.getPackagesForUid(uid));
- if (packageName == null) {
- return false;
- }
- return userPackageManager.isInstantApp(packageName);
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- @Override
- public boolean setBrowserRoleHolder(@Nullable String packageName, @UserIdInt int userId) {
- final Context context = getContext();
- context.enforceCallingOrSelfPermission(
- android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null);
- if (UserHandleCompat.getUserId(Binder.getCallingUid()) != userId) {
- context.enforceCallingOrSelfPermission(
- android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
- }
-
- if (!isUserExistent(userId)) {
- return false;
- }
-
- final AndroidFuture<Void> future = new AndroidFuture<>();
- final RemoteCallback callback = new RemoteCallback(result -> {
- boolean successful = result != null;
- if (successful) {
- future.complete(null);
- } else {
- future.completeExceptionally(new RuntimeException());
- }
- });
- final long identity = Binder.clearCallingIdentity();
- try {
- if (packageName != null) {
- addRoleHolderAsUser(RoleManager.ROLE_BROWSER, packageName, 0, userId, callback);
- } else {
- clearRoleHoldersAsUser(RoleManager.ROLE_BROWSER, 0, userId, callback);
- }
- try {
- future.get(5, TimeUnit.SECONDS);
- } catch (InterruptedException | ExecutionException | TimeoutException e) {
- Log.e(LOG_TAG, "Exception while setting default browser: " + packageName, e);
- return false;
- }
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
-
- return true;
- }
-
- @Override
- public String getSmsRoleHolder(int userId) {
- final long identity = Binder.clearCallingIdentity();
- try {
- return CollectionUtils.firstOrNull(getRoleHoldersAsUser(RoleManager.ROLE_SMS,
- userId));
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
-
- @Override
- protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter fout,
- @Nullable String[] args) {
- if (!checkDumpPermission("role", fout)) {
- return;
- }
-
- boolean dumpAsProto = args != null && ArrayUtils.contains(args, "--proto");
- DualDumpOutputStream dumpOutputStream;
- if (dumpAsProto) {
- dumpOutputStream = new DualDumpOutputStream(new ProtoOutputStream(
- new FileOutputStream(fd)));
- } else {
- fout.println("ROLE STATE (dumpsys role):");
- dumpOutputStream = new DualDumpOutputStream(new IndentingPrintWriter(fout, " "));
- }
-
- synchronized (mLock) {
- final int userStatesSize = mUserStates.size();
- for (int i = 0; i < userStatesSize; i++) {
- final RoleUserState userState = mUserStates.valueAt(i);
-
- userState.dump(dumpOutputStream, "user_states",
- RoleServiceDumpProto.USER_STATES);
- }
- }
-
- dumpOutputStream.flush();
- }
-
- private boolean checkDumpPermission(@NonNull String serviceName,
- @NonNull PrintWriter writer) {
- if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
- != PackageManager.PERMISSION_GRANTED) {
- writer.println("Permission Denial: can't dump " + serviceName + " from from pid="
- + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
- + " due to missing " + android.Manifest.permission.DUMP + " permission");
- return false;
- } else {
- return true;
- }
- }
- }
-
- private class Local implements RoleManagerLocal {
- @NonNull
- @Override
- public Map<String, Set<String>> getRolesAndHolders(@UserIdInt int userId) {
- // Convert ArrayMap<String, ArraySet<String>> to Map<String, Set<String>> for the API.
- //noinspection unchecked
- return (Map<String, Set<String>>) (Map<String, ?>)
- getOrCreateUserState(userId).getRolesAndHolders();
- }
- }
-}
diff --git a/apex/permission/service/java/com/android/role/RoleShellCommand.java b/apex/permission/service/java/com/android/role/RoleShellCommand.java
deleted file mode 100644
index 03b7c76..0000000
--- a/apex/permission/service/java/com/android/role/RoleShellCommand.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.role;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.app.role.IRoleManager;
-import android.os.RemoteCallback;
-import android.os.RemoteException;
-
-import com.android.modules.utils.BasicShellCommandHandler;
-import com.android.permission.compat.UserHandleCompat;
-
-import java.io.PrintWriter;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.TimeUnit;
-
-class RoleShellCommand extends BasicShellCommandHandler {
- @NonNull
- private final IRoleManager mRoleManager;
-
- RoleShellCommand(@NonNull IRoleManager roleManager) {
- mRoleManager = roleManager;
- }
-
- private class CallbackFuture extends CompletableFuture<Void> {
- @NonNull
- public RemoteCallback createCallback() {
- return new RemoteCallback(result -> {
- boolean successful = result != null;
- if (successful) {
- complete(null);
- } else {
- completeExceptionally(new RuntimeException("Failed"));
- }
- });
- }
-
- public int waitForResult() {
- try {
- get(5, TimeUnit.SECONDS);
- return 0;
- } catch (Exception e) {
- getErrPrintWriter().println("Error: see logcat for details.\n" + e);
- return -1;
- }
- }
- }
-
- @Override
- public int onCommand(@Nullable String cmd) {
- if (cmd == null) {
- return handleDefaultCommands(cmd);
- }
-
- PrintWriter pw = getOutPrintWriter();
- try {
- switch (cmd) {
- case "add-role-holder":
- return runAddRoleHolder();
- case "remove-role-holder":
- return runRemoveRoleHolder();
- case "clear-role-holders":
- return runClearRoleHolders();
- default:
- return handleDefaultCommands(cmd);
- }
- } catch (RemoteException e) {
- pw.println("Remote exception: " + e);
- }
- return -1;
- }
-
- private int getUserIdMaybe() {
- int userId = UserHandleCompat.USER_SYSTEM;
- String option = getNextOption();
- if (option != null && option.equals("--user")) {
- userId = Integer.parseInt(getNextArgRequired());
- }
- return userId;
- }
-
- private int getFlagsMaybe() {
- String flags = getNextArg();
- if (flags == null) {
- return 0;
- }
- return Integer.parseInt(flags);
- }
-
- private int runAddRoleHolder() throws RemoteException {
- int userId = getUserIdMaybe();
- String roleName = getNextArgRequired();
- String packageName = getNextArgRequired();
- int flags = getFlagsMaybe();
-
- CallbackFuture future = new CallbackFuture();
- mRoleManager.addRoleHolderAsUser(roleName, packageName, flags, userId,
- future.createCallback());
- return future.waitForResult();
- }
-
- private int runRemoveRoleHolder() throws RemoteException {
- int userId = getUserIdMaybe();
- String roleName = getNextArgRequired();
- String packageName = getNextArgRequired();
- int flags = getFlagsMaybe();
-
- CallbackFuture future = new CallbackFuture();
- mRoleManager.removeRoleHolderAsUser(roleName, packageName, flags, userId,
- future.createCallback());
- return future.waitForResult();
- }
-
- private int runClearRoleHolders() throws RemoteException {
- int userId = getUserIdMaybe();
- String roleName = getNextArgRequired();
- int flags = getFlagsMaybe();
-
- CallbackFuture future = new CallbackFuture();
- mRoleManager.clearRoleHoldersAsUser(roleName, flags, userId, future.createCallback());
- return future.waitForResult();
- }
-
- @Override
- public void onHelp() {
- PrintWriter pw = getOutPrintWriter();
- pw.println("Role (role) commands:");
- pw.println(" help or -h");
- pw.println(" Print this help text.");
- pw.println();
- pw.println(" add-role-holder [--user USER_ID] ROLE PACKAGE [FLAGS]");
- pw.println(" remove-role-holder [--user USER_ID] ROLE PACKAGE [FLAGS]");
- pw.println(" clear-role-holders [--user USER_ID] ROLE [FLAGS]");
- pw.println();
- }
-}
diff --git a/apex/permission/service/java/com/android/role/RoleUserState.java b/apex/permission/service/java/com/android/role/RoleUserState.java
deleted file mode 100644
index 78d8d15..0000000
--- a/apex/permission/service/java/com/android/role/RoleUserState.java
+++ /dev/null
@@ -1,476 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.role;
-
-import android.annotation.CheckResult;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.UserIdInt;
-import android.annotation.WorkerThread;
-import android.os.Handler;
-import android.os.UserHandle;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.Log;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.util.dump.DualDumpOutputStream;
-import com.android.permission.util.BackgroundThread;
-import com.android.permission.util.CollectionUtils;
-import com.android.role.persistence.RolesPersistence;
-import com.android.role.persistence.RolesState;
-import com.android.server.role.RoleServicePlatformHelper;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-
-/**
- * Stores the state of roles for a user.
- */
-class RoleUserState {
- private static final String LOG_TAG = RoleUserState.class.getSimpleName();
-
- public static final int VERSION_UNDEFINED = -1;
-
- private static final long WRITE_DELAY_MILLIS = 200;
-
- private final RolesPersistence mPersistence = RolesPersistence.createInstance();
-
- @UserIdInt
- private final int mUserId;
-
- @NonNull
- private final RoleServicePlatformHelper mPlatformHelper;
-
- @NonNull
- private final Callback mCallback;
-
- @NonNull
- private final Object mLock = new Object();
-
- @GuardedBy("mLock")
- private int mVersion = VERSION_UNDEFINED;
-
- @GuardedBy("mLock")
- @Nullable
- private String mPackagesHash;
-
- /**
- * Maps role names to its holders' package names. The values should never be null.
- */
- @GuardedBy("mLock")
- @NonNull
- private ArrayMap<String, ArraySet<String>> mRoles = new ArrayMap<>();
-
- @GuardedBy("mLock")
- private boolean mWriteScheduled;
-
- @GuardedBy("mLock")
- private boolean mDestroyed;
-
- @NonNull
- private final Handler mWriteHandler = new Handler(BackgroundThread.get().getLooper());
-
- /**
- * Create a new user state, and read its state from disk if previously persisted.
- *
- * @param userId the user id for this user state
- * @param platformHelper the platform helper
- * @param callback the callback for this user state
- */
- public RoleUserState(@UserIdInt int userId, @NonNull RoleServicePlatformHelper platformHelper,
- @NonNull Callback callback) {
- mUserId = userId;
- mPlatformHelper = platformHelper;
- mCallback = callback;
-
- readFile();
- }
-
- /**
- * Get the version of this user state.
- */
- public int getVersion() {
- synchronized (mLock) {
- return mVersion;
- }
- }
-
- /**
- * Set the version of this user state.
- *
- * @param version the version to set
- */
- public void setVersion(int version) {
- synchronized (mLock) {
- if (mVersion == version) {
- return;
- }
- mVersion = version;
- scheduleWriteFileLocked();
- }
- }
-
- /**
- * Get the hash representing the state of packages during the last time initial grants was run.
- *
- * @return the hash representing the state of packages
- */
- @Nullable
- public String getPackagesHash() {
- synchronized (mLock) {
- return mPackagesHash;
- }
- }
-
- /**
- * Set the hash representing the state of packages during the last time initial grants was run.
- *
- * @param packagesHash the hash representing the state of packages
- */
- public void setPackagesHash(@Nullable String packagesHash) {
- synchronized (mLock) {
- if (Objects.equals(mPackagesHash, packagesHash)) {
- return;
- }
- mPackagesHash = packagesHash;
- scheduleWriteFileLocked();
- }
- }
-
- /**
- * Get whether the role is available.
- *
- * @param roleName the name of the role to get the holders for
- *
- * @return whether the role is available
- */
- public boolean isRoleAvailable(@NonNull String roleName) {
- synchronized (mLock) {
- return mRoles.containsKey(roleName);
- }
- }
-
- /**
- * Get the holders of a role.
- *
- * @param roleName the name of the role to query for
- *
- * @return the set of role holders, or {@code null} if and only if the role is not found
- */
- @Nullable
- public ArraySet<String> getRoleHolders(@NonNull String roleName) {
- synchronized (mLock) {
- ArraySet<String> packageNames = mRoles.get(roleName);
- if (packageNames == null) {
- return null;
- }
- return new ArraySet<>(packageNames);
- }
- }
-
- /**
- * Adds the given role, effectively marking it as {@link #isRoleAvailable available}
- *
- * @param roleName the name of the role
- *
- * @return whether any changes were made
- */
- public boolean addRoleName(@NonNull String roleName) {
- synchronized (mLock) {
- if (!mRoles.containsKey(roleName)) {
- mRoles.put(roleName, new ArraySet<>());
- Log.i(LOG_TAG, "Added new role: " + roleName);
- scheduleWriteFileLocked();
- return true;
- } else {
- return false;
- }
- }
- }
-
- /**
- * Set the names of all available roles.
- *
- * @param roleNames the names of all the available roles
- */
- public void setRoleNames(@NonNull List<String> roleNames) {
- synchronized (mLock) {
- boolean changed = false;
-
- for (int i = mRoles.size() - 1; i >= 0; i--) {
- String roleName = mRoles.keyAt(i);
-
- if (!roleNames.contains(roleName)) {
- ArraySet<String> packageNames = mRoles.valueAt(i);
- if (!packageNames.isEmpty()) {
- Log.e(LOG_TAG, "Holders of a removed role should have been cleaned up,"
- + " role: " + roleName + ", holders: " + packageNames);
- }
- mRoles.removeAt(i);
- changed = true;
- }
- }
-
- int roleNamesSize = roleNames.size();
- for (int i = 0; i < roleNamesSize; i++) {
- changed |= addRoleName(roleNames.get(i));
- }
-
- if (changed) {
- scheduleWriteFileLocked();
- }
- }
- }
-
- /**
- * Add a holder to a role.
- *
- * @param roleName the name of the role to add the holder to
- * @param packageName the package name of the new holder
- *
- * @return {@code false} if and only if the role is not found
- */
- @CheckResult
- public boolean addRoleHolder(@NonNull String roleName, @NonNull String packageName) {
- boolean changed;
-
- synchronized (mLock) {
- ArraySet<String> roleHolders = mRoles.get(roleName);
- if (roleHolders == null) {
- Log.e(LOG_TAG, "Cannot add role holder for unknown role, role: " + roleName
- + ", package: " + packageName);
- return false;
- }
- changed = roleHolders.add(packageName);
- if (changed) {
- scheduleWriteFileLocked();
- }
- }
-
- if (changed) {
- mCallback.onRoleHoldersChanged(roleName, mUserId);
- }
- return true;
- }
-
- /**
- * Remove a holder from a role.
- *
- * @param roleName the name of the role to remove the holder from
- * @param packageName the package name of the holder to remove
- *
- * @return {@code false} if and only if the role is not found
- */
- @CheckResult
- public boolean removeRoleHolder(@NonNull String roleName, @NonNull String packageName) {
- boolean changed;
-
- synchronized (mLock) {
- ArraySet<String> roleHolders = mRoles.get(roleName);
- if (roleHolders == null) {
- Log.e(LOG_TAG, "Cannot remove role holder for unknown role, role: " + roleName
- + ", package: " + packageName);
- return false;
- }
-
- changed = roleHolders.remove(packageName);
- if (changed) {
- scheduleWriteFileLocked();
- }
- }
-
- if (changed) {
- mCallback.onRoleHoldersChanged(roleName, mUserId);
- }
- return true;
- }
-
- /**
- * @see android.app.role.RoleManager#getHeldRolesFromController
- */
- @NonNull
- public List<String> getHeldRoles(@NonNull String packageName) {
- synchronized (mLock) {
- List<String> roleNames = new ArrayList<>();
- int size = mRoles.size();
- for (int i = 0; i < size; i++) {
- if (mRoles.valueAt(i).contains(packageName)) {
- roleNames.add(mRoles.keyAt(i));
- }
- }
- return roleNames;
- }
- }
-
- /**
- * Schedule writing the state to file.
- */
- @GuardedBy("mLock")
- private void scheduleWriteFileLocked() {
- if (mDestroyed) {
- return;
- }
-
- if (!mWriteScheduled) {
- mWriteHandler.postDelayed(this::writeFile, WRITE_DELAY_MILLIS);
- mWriteScheduled = true;
- }
- }
-
- @WorkerThread
- private void writeFile() {
- RolesState roles;
- synchronized (mLock) {
- if (mDestroyed) {
- return;
- }
-
- mWriteScheduled = false;
-
- roles = new RolesState(mVersion, mPackagesHash,
- (Map<String, Set<String>>) (Map<String, ?>) snapshotRolesLocked());
- }
-
- mPersistence.writeForUser(roles, UserHandle.of(mUserId));
- }
-
- private void readFile() {
- synchronized (mLock) {
- RolesState roleState = mPersistence.readForUser(UserHandle.of(mUserId));
-
- Map<String, Set<String>> roles;
- if (roleState != null) {
- mVersion = roleState.getVersion();
- mPackagesHash = roleState.getPackagesHash();
- roles = roleState.getRoles();
- } else {
- roles = mPlatformHelper.getLegacyRoleState(mUserId);
- }
- mRoles.clear();
- for (Map.Entry<String, Set<String>> entry : roles.entrySet()) {
- String roleName = entry.getKey();
- ArraySet<String> roleHolders = new ArraySet<>(entry.getValue());
- mRoles.put(roleName, roleHolders);
- }
-
- if (roleState == null) {
- scheduleWriteFileLocked();
- }
- }
- }
-
- /**
- * Dump this user state.
- *
- * @param dumpOutputStream the output stream to dump to
- */
- public void dump(@NonNull DualDumpOutputStream dumpOutputStream, @NonNull String fieldName,
- long fieldId) {
- int version;
- String packagesHash;
- ArrayMap<String, ArraySet<String>> roles;
- synchronized (mLock) {
- version = mVersion;
- packagesHash = mPackagesHash;
- roles = snapshotRolesLocked();
- }
-
- long fieldToken = dumpOutputStream.start(fieldName, fieldId);
- dumpOutputStream.write("user_id", RoleUserStateProto.USER_ID, mUserId);
- dumpOutputStream.write("version", RoleUserStateProto.VERSION, version);
- dumpOutputStream.write("packages_hash", RoleUserStateProto.PACKAGES_HASH, packagesHash);
-
- int rolesSize = roles.size();
- for (int rolesIndex = 0; rolesIndex < rolesSize; rolesIndex++) {
- String roleName = roles.keyAt(rolesIndex);
- ArraySet<String> roleHolders = roles.valueAt(rolesIndex);
-
- long rolesToken = dumpOutputStream.start("roles", RoleUserStateProto.ROLES);
- dumpOutputStream.write("name", RoleProto.NAME, roleName);
-
- int roleHoldersSize = roleHolders.size();
- for (int roleHoldersIndex = 0; roleHoldersIndex < roleHoldersSize; roleHoldersIndex++) {
- String roleHolder = roleHolders.valueAt(roleHoldersIndex);
-
- dumpOutputStream.write("holders", RoleProto.HOLDERS, roleHolder);
- }
-
- dumpOutputStream.end(rolesToken);
- }
-
- dumpOutputStream.end(fieldToken);
- }
-
- /**
- * Get the roles and their holders.
- *
- * @return A copy of the roles and their holders
- */
- @NonNull
- public ArrayMap<String, ArraySet<String>> getRolesAndHolders() {
- synchronized (mLock) {
- return snapshotRolesLocked();
- }
- }
-
- @GuardedBy("mLock")
- @NonNull
- private ArrayMap<String, ArraySet<String>> snapshotRolesLocked() {
- ArrayMap<String, ArraySet<String>> roles = new ArrayMap<>();
- for (int i = 0, size = CollectionUtils.size(mRoles); i < size; ++i) {
- String roleName = mRoles.keyAt(i);
- ArraySet<String> roleHolders = mRoles.valueAt(i);
-
- roleHolders = new ArraySet<>(roleHolders);
- roles.put(roleName, roleHolders);
- }
- return roles;
- }
-
- /**
- * Destroy this user state and delete the corresponding file. Any pending writes to the file
- * will be cancelled, and any future interaction with this state will throw an exception.
- */
- public void destroy() {
- synchronized (mLock) {
- if (mDestroyed) {
- throw new IllegalStateException("This RoleUserState has already been destroyed");
- }
- mWriteHandler.removeCallbacksAndMessages(null);
- mPersistence.deleteForUser(UserHandle.of(mUserId));
- mDestroyed = true;
- }
- }
-
- /**
- * Callback for a user state.
- */
- public interface Callback {
-
- /**
- * Called when the holders of roles are changed.
- *
- * @param roleName the name of the role whose holders are changed
- * @param userId the user id for this role holder change
- */
- void onRoleHoldersChanged(@NonNull String roleName, @UserIdInt int userId);
- }
-}
diff --git a/apex/permission/service/java/com/android/role/TEST_MAPPING b/apex/permission/service/java/com/android/role/TEST_MAPPING
deleted file mode 100644
index 0d7bc14..0000000
--- a/apex/permission/service/java/com/android/role/TEST_MAPPING
+++ /dev/null
@@ -1,20 +0,0 @@
-{
- "presubmit": [
- {
- "name": "CtsStatsdHostTestCases",
- "options": [
- {
- "include-filter": "android.cts.statsd.atom.UidAtomTests#testRoleHolder"
- }
- ]
- },
- {
- "name": "CtsRoleTestCases",
- "options": [
- {
- "exclude-annotation": "androidx.test.filters.FlakyTest"
- }
- ]
- }
- ]
-}
diff --git a/apex/permission/service/java/com/android/role/package-info.java b/apex/permission/service/java/com/android/role/package-info.java
deleted file mode 100644
index 8b5b251..0000000
--- a/apex/permission/service/java/com/android/role/package-info.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/**
- * @hide
- * TODO(b/146466118) remove this javadoc tag
- */
-@android.annotation.Hide
-package com.android.role;
diff --git a/apex/permission/service/java/com/android/role/persistence/RolesPersistence.java b/apex/permission/service/java/com/android/role/persistence/RolesPersistence.java
deleted file mode 100644
index 2e5a28a..0000000
--- a/apex/permission/service/java/com/android/role/persistence/RolesPersistence.java
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.role.persistence;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.annotation.SystemApi.Client;
-import android.os.UserHandle;
-
-/**
- * Persistence for roles.
- *
- * TODO(b/147914847): Remove @hide when it becomes the default.
- * @hide
- */
-@SystemApi(client = Client.SYSTEM_SERVER)
-public interface RolesPersistence {
-
- /**
- * Read the roles from persistence.
- *
- * This will perform I/O operations synchronously.
- *
- * @param user the user to read for
- * @return the roles read
- */
- @Nullable
- RolesState readForUser(@NonNull UserHandle user);
-
- /**
- * Write the roles to persistence.
- *
- * This will perform I/O operations synchronously.
- *
- * @param roles the roles to write
- * @param user the user to write for
- */
- void writeForUser(@NonNull RolesState roles, @NonNull UserHandle user);
-
- /**
- * Delete the roles from persistence.
- *
- * This will perform I/O operations synchronously.
- *
- * @param user the user to delete for
- */
- void deleteForUser(@NonNull UserHandle user);
-
- /**
- * Create a new instance of {@link RolesPersistence} implementation.
- *
- * @return the new instance.
- */
- @NonNull
- static RolesPersistence createInstance() {
- return new RolesPersistenceImpl();
- }
-}
diff --git a/apex/permission/service/java/com/android/role/persistence/RolesPersistenceImpl.java b/apex/permission/service/java/com/android/role/persistence/RolesPersistenceImpl.java
deleted file mode 100644
index f66257f..0000000
--- a/apex/permission/service/java/com/android/role/persistence/RolesPersistenceImpl.java
+++ /dev/null
@@ -1,218 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.role.persistence;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.ApexEnvironment;
-import android.os.UserHandle;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.AtomicFile;
-import android.util.Log;
-import android.util.Xml;
-
-import com.android.permission.persistence.IoUtils;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Persistence implementation for roles.
- *
- * TODO(b/147914847): Remove @hide when it becomes the default.
- * @hide
- */
-public class RolesPersistenceImpl implements RolesPersistence {
-
- private static final String LOG_TAG = RolesPersistenceImpl.class.getSimpleName();
-
- private static final String APEX_MODULE_NAME = "com.android.permission";
-
- private static final String ROLES_FILE_NAME = "roles.xml";
-
- private static final String TAG_ROLES = "roles";
- private static final String TAG_ROLE = "role";
- private static final String TAG_HOLDER = "holder";
-
- private static final String ATTRIBUTE_VERSION = "version";
- private static final String ATTRIBUTE_NAME = "name";
- private static final String ATTRIBUTE_PACKAGES_HASH = "packagesHash";
-
- @Nullable
- @Override
- public RolesState readForUser(@NonNull UserHandle user) {
- File file = getFile(user);
- try (FileInputStream inputStream = new AtomicFile(file).openRead()) {
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(inputStream, null);
- return parseXml(parser);
- } catch (FileNotFoundException e) {
- Log.i(LOG_TAG, "roles.xml not found");
- return null;
- } catch (XmlPullParserException | IOException e) {
- throw new IllegalStateException("Failed to read roles.xml: " + file , e);
- }
- }
-
- @NonNull
- private static RolesState parseXml(@NonNull XmlPullParser parser)
- throws IOException, XmlPullParserException {
- int type;
- int depth;
- int innerDepth = parser.getDepth() + 1;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) {
- if (depth > innerDepth || type != XmlPullParser.START_TAG) {
- continue;
- }
-
- if (parser.getName().equals(TAG_ROLES)) {
- return parseRoles(parser);
- }
- }
- throw new IllegalStateException("Missing <" + TAG_ROLES + "> in roles.xml");
- }
-
- @NonNull
- private static RolesState parseRoles(@NonNull XmlPullParser parser)
- throws IOException, XmlPullParserException {
- int version = Integer.parseInt(parser.getAttributeValue(null, ATTRIBUTE_VERSION));
- String packagesHash = parser.getAttributeValue(null, ATTRIBUTE_PACKAGES_HASH);
-
- Map<String, Set<String>> roles = new ArrayMap<>();
- int type;
- int depth;
- int innerDepth = parser.getDepth() + 1;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) {
- if (depth > innerDepth || type != XmlPullParser.START_TAG) {
- continue;
- }
-
- if (parser.getName().equals(TAG_ROLE)) {
- String roleName = parser.getAttributeValue(null, ATTRIBUTE_NAME);
- Set<String> roleHolders = parseRoleHolders(parser);
- roles.put(roleName, roleHolders);
- }
- }
-
- return new RolesState(version, packagesHash, roles);
- }
-
- @NonNull
- private static Set<String> parseRoleHolders(@NonNull XmlPullParser parser)
- throws IOException, XmlPullParserException {
- Set<String> roleHolders = new ArraySet<>();
- int type;
- int depth;
- int innerDepth = parser.getDepth() + 1;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) {
- if (depth > innerDepth || type != XmlPullParser.START_TAG) {
- continue;
- }
-
- if (parser.getName().equals(TAG_HOLDER)) {
- String roleHolder = parser.getAttributeValue(null, ATTRIBUTE_NAME);
- roleHolders.add(roleHolder);
- }
- }
- return roleHolders;
- }
-
- @Override
- public void writeForUser(@NonNull RolesState roles, @NonNull UserHandle user) {
- File file = getFile(user);
- AtomicFile atomicFile = new AtomicFile(file);
- FileOutputStream outputStream = null;
- try {
- outputStream = atomicFile.startWrite();
-
- XmlSerializer serializer = Xml.newSerializer();
- serializer.setOutput(outputStream, StandardCharsets.UTF_8.name());
- serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
- serializer.startDocument(null, true);
-
- serializeRoles(serializer, roles);
-
- serializer.endDocument();
- atomicFile.finishWrite(outputStream);
- } catch (Exception e) {
- Log.wtf(LOG_TAG, "Failed to write roles.xml, restoring backup: " + file,
- e);
- atomicFile.failWrite(outputStream);
- } finally {
- IoUtils.closeQuietly(outputStream);
- }
- }
-
- private static void serializeRoles(@NonNull XmlSerializer serializer,
- @NonNull RolesState roles) throws IOException {
- serializer.startTag(null, TAG_ROLES);
-
- int version = roles.getVersion();
- serializer.attribute(null, ATTRIBUTE_VERSION, Integer.toString(version));
- String packagesHash = roles.getPackagesHash();
- if (packagesHash != null) {
- serializer.attribute(null, ATTRIBUTE_PACKAGES_HASH, packagesHash);
- }
-
- for (Map.Entry<String, Set<String>> entry : roles.getRoles().entrySet()) {
- String roleName = entry.getKey();
- Set<String> roleHolders = entry.getValue();
-
- serializer.startTag(null, TAG_ROLE);
- serializer.attribute(null, ATTRIBUTE_NAME, roleName);
- serializeRoleHolders(serializer, roleHolders);
- serializer.endTag(null, TAG_ROLE);
- }
-
- serializer.endTag(null, TAG_ROLES);
- }
-
- private static void serializeRoleHolders(@NonNull XmlSerializer serializer,
- @NonNull Set<String> roleHolders) throws IOException {
- for (String roleHolder : roleHolders) {
- serializer.startTag(null, TAG_HOLDER);
- serializer.attribute(null, ATTRIBUTE_NAME, roleHolder);
- serializer.endTag(null, TAG_HOLDER);
- }
- }
-
- @Override
- public void deleteForUser(@NonNull UserHandle user) {
- getFile(user).delete();
- }
-
- @NonNull
- private static File getFile(@NonNull UserHandle user) {
- ApexEnvironment apexEnvironment = ApexEnvironment.getApexEnvironment(APEX_MODULE_NAME);
- File dataDirectory = apexEnvironment.getDeviceProtectedDataDirForUser(user);
- return new File(dataDirectory, ROLES_FILE_NAME);
- }
-}
diff --git a/apex/permission/service/java/com/android/role/persistence/RolesState.java b/apex/permission/service/java/com/android/role/persistence/RolesState.java
deleted file mode 100644
index f61efa0..0000000
--- a/apex/permission/service/java/com/android/role/persistence/RolesState.java
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.role.persistence;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SystemApi;
-import android.annotation.SystemApi.Client;
-
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-
-/**
- * State of all roles.
- *
- * TODO(b/147914847): Remove @hide when it becomes the default.
- * @hide
- */
-@SystemApi(client = Client.SYSTEM_SERVER)
-public final class RolesState {
-
- /**
- * The version of the roles.
- */
- private final int mVersion;
-
- /**
- * The hash of all packages in the system.
- */
- @Nullable
- private final String mPackagesHash;
-
- /**
- * The roles.
- */
- @NonNull
- private final Map<String, Set<String>> mRoles;
-
- /**
- * Create a new instance of this class.
- *
- * @param version the version of the roles
- * @param packagesHash the hash of all packages in the system
- * @param roles the roles
- */
- public RolesState(int version, @Nullable String packagesHash,
- @NonNull Map<String, Set<String>> roles) {
- mVersion = version;
- mPackagesHash = packagesHash;
- mRoles = roles;
- }
-
- /**
- * Get the version of the roles.
- *
- * @return the version of the roles
- */
- public int getVersion() {
- return mVersion;
- }
-
- /**
- * Get the hash of all packages in the system.
- *
- * @return the hash of all packages in the system
- */
- @Nullable
- public String getPackagesHash() {
- return mPackagesHash;
- }
-
- /**
- * Get the roles.
- *
- * @return the roles
- */
- @NonNull
- public Map<String, Set<String>> getRoles() {
- return mRoles;
- }
-
- @Override
- public boolean equals(Object object) {
- if (this == object) {
- return true;
- }
- if (object == null || getClass() != object.getClass()) {
- return false;
- }
- RolesState that = (RolesState) object;
- return mVersion == that.mVersion
- && Objects.equals(mPackagesHash, that.mPackagesHash)
- && Objects.equals(mRoles, that.mRoles);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mVersion, mPackagesHash, mRoles);
- }
-}
diff --git a/apex/permission/service/proto/com/android/role/roleservice.proto b/apex/permission/service/proto/com/android/role/roleservice.proto
deleted file mode 100644
index 79c4229..0000000
--- a/apex/permission/service/proto/com/android/role/roleservice.proto
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-syntax = "proto2";
-
-package com.android.role;
-
-option java_multiple_files = true;
-
-import "frameworks/base/core/proto/android/privacy.proto";
-
-message RoleServiceDumpProto {
- option (.android.msg_privacy).dest = DEST_AUTOMATIC;
-
- // List of per-user states for all users.
- repeated RoleUserStateProto user_states = 1;
-}
-
-message RoleUserStateProto {
- option (.android.msg_privacy).dest = DEST_AUTOMATIC;
-
- // The user id of this state.
- optional int32 user_id = 1;
-
- // The version of this state.
- optional int32 version = 2;
-
- // The hash of packages for this state.
- optional string packages_hash = 3;
-
- // The set of roles in this state.
- repeated RoleProto roles = 4;
-}
-
-message RoleProto {
- option (.android.msg_privacy).dest = DEST_AUTOMATIC;
-
- // The name of this role, e.g. "android.app.role.DIALER".
- optional string name = 1;
-
- // The package names of the holders of this role.
- repeated string holders = 2;
-}
diff --git a/apex/permission/testing/Android.bp b/apex/permission/testing/Android.bp
deleted file mode 100644
index 63bf0a0..0000000
--- a/apex/permission/testing/Android.bp
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (C) 2019 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-apex_test {
- name: "test_com.android.permission",
- visibility: [
- "//system/apex/tests",
- ],
- defaults: ["com.android.permission-defaults"],
- manifest: "test_manifest.json",
- file_contexts: ":com.android.permission-file_contexts",
- // Test APEX, should never be installed
- installable: false,
-}
diff --git a/apex/permission/testing/test_manifest.json b/apex/permission/testing/test_manifest.json
deleted file mode 100644
index bc19a9e..0000000
--- a/apex/permission/testing/test_manifest.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "name": "com.android.permission",
- "version": 2147483647
-}
diff --git a/apex/permission/tests/Android.bp b/apex/permission/tests/Android.bp
deleted file mode 100644
index 271e328..0000000
--- a/apex/permission/tests/Android.bp
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright (C) 2020 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-android_test {
- name: "PermissionApexTests",
- sdk_version: "test_current",
- srcs: [
- "java/**/*.kt",
- ],
- static_libs: [
- "service-permission.impl",
- "androidx.test.rules",
- "androidx.test.ext.junit",
- "androidx.test.ext.truth",
- "mockito-target-extended-minus-junit4",
- ],
- jni_libs: [
- "libdexmakerjvmtiagent",
- "libstaticjvmtiagent",
- ],
- compile_multilib: "both",
- test_suites: [
- "general-tests",
- "mts",
- ],
-}
diff --git a/apex/permission/tests/AndroidManifest.xml b/apex/permission/tests/AndroidManifest.xml
deleted file mode 100644
index 57ee641..0000000
--- a/apex/permission/tests/AndroidManifest.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
- ~ Copyright (C) 2020 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-
-<manifest
- xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.permission.test">
-
- <!-- The application has to be debuggable for static mocking to work. -->
- <application android:debuggable="true">
- <uses-library android:name="android.test.runner" />
- </application>
-
- <instrumentation
- android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.permission.test"
- android:label="Permission APEX Tests" />
-</manifest>
diff --git a/apex/permission/tests/java/com/android/permission/persistence/RuntimePermissionsPersistenceTest.kt b/apex/permission/tests/java/com/android/permission/persistence/RuntimePermissionsPersistenceTest.kt
deleted file mode 100644
index 2987da0..0000000
--- a/apex/permission/tests/java/com/android/permission/persistence/RuntimePermissionsPersistenceTest.kt
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.permission.persistence
-
-import android.content.ApexEnvironment
-import android.content.Context
-import android.os.Process
-import android.os.UserHandle
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
-import com.google.common.truth.Truth.assertThat
-import org.junit.After
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.any
-import org.mockito.ArgumentMatchers.eq
-import org.mockito.Mock
-import org.mockito.Mockito.`when`
-import org.mockito.MockitoAnnotations.initMocks
-import org.mockito.MockitoSession
-import org.mockito.quality.Strictness
-import java.io.File
-
-@RunWith(AndroidJUnit4::class)
-class RuntimePermissionsPersistenceTest {
- private val context = InstrumentationRegistry.getInstrumentation().context
-
- private lateinit var mockDataDirectory: File
-
- private lateinit var mockitoSession: MockitoSession
- @Mock
- lateinit var apexEnvironment: ApexEnvironment
-
- private val persistence = RuntimePermissionsPersistence.createInstance()
- private val permissionState = RuntimePermissionsState.PermissionState("permission", true, 3)
- private val state = RuntimePermissionsState(
- 1, "fingerprint", mapOf("package" to listOf(permissionState)),
- mapOf("sharedUser" to listOf(permissionState))
- )
- private val user = Process.myUserHandle()
-
- @Before
- fun createMockDataDirectory() {
- mockDataDirectory = context.getDir("mock_data", Context.MODE_PRIVATE)
- mockDataDirectory.listFiles()!!.forEach { assertThat(it.deleteRecursively()).isTrue() }
- }
-
- @Before
- fun mockApexEnvironment() {
- initMocks(this)
- mockitoSession = mockitoSession()
- .mockStatic(ApexEnvironment::class.java)
- .strictness(Strictness.LENIENT)
- .startMocking()
- `when`(ApexEnvironment.getApexEnvironment(eq(APEX_MODULE_NAME))).thenReturn(apexEnvironment)
- `when`(apexEnvironment.getDeviceProtectedDataDirForUser(any(UserHandle::class.java))).then {
- File(mockDataDirectory, it.arguments[0].toString()).also { it.mkdirs() }
- }
- }
-
- @After
- fun finishMockingApexEnvironment() {
- mockitoSession.finishMocking()
- }
-
- @Test
- fun testReadWrite() {
- persistence.writeForUser(state, user)
- val persistedState = persistence.readForUser(user)
-
- assertThat(persistedState).isEqualTo(state)
- assertThat(persistedState!!.version).isEqualTo(state.version)
- assertThat(persistedState.fingerprint).isEqualTo(state.fingerprint)
- assertThat(persistedState.packagePermissions).isEqualTo(state.packagePermissions)
- val persistedPermissionState = persistedState.packagePermissions.values.first().first()
- assertThat(persistedPermissionState.name).isEqualTo(permissionState.name)
- assertThat(persistedPermissionState.isGranted).isEqualTo(permissionState.isGranted)
- assertThat(persistedPermissionState.flags).isEqualTo(permissionState.flags)
- assertThat(persistedState.sharedUserPermissions).isEqualTo(state.sharedUserPermissions)
- }
-
- @Test
- fun testDelete() {
- persistence.writeForUser(state, user)
- persistence.deleteForUser(user)
- val persistedState = persistence.readForUser(user)
-
- assertThat(persistedState).isNull()
- }
-
- companion object {
- private const val APEX_MODULE_NAME = "com.android.permission"
- }
-}
diff --git a/apex/permission/tests/java/com/android/role/persistence/RolesPersistenceTest.kt b/apex/permission/tests/java/com/android/role/persistence/RolesPersistenceTest.kt
deleted file mode 100644
index f9d9d5a..0000000
--- a/apex/permission/tests/java/com/android/role/persistence/RolesPersistenceTest.kt
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.role.persistence
-
-import android.content.ApexEnvironment
-import android.content.Context
-import android.os.Process
-import android.os.UserHandle
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.platform.app.InstrumentationRegistry
-import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession
-import com.google.common.truth.Truth.assertThat
-import org.junit.After
-import org.junit.Before
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.mockito.ArgumentMatchers.any
-import org.mockito.ArgumentMatchers.eq
-import org.mockito.Mock
-import org.mockito.Mockito.`when`
-import org.mockito.MockitoAnnotations.initMocks
-import org.mockito.MockitoSession
-import org.mockito.quality.Strictness
-import java.io.File
-
-@RunWith(AndroidJUnit4::class)
-class RolesPersistenceTest {
- private val context = InstrumentationRegistry.getInstrumentation().context
-
- private lateinit var mockDataDirectory: File
-
- private lateinit var mockitoSession: MockitoSession
- @Mock
- lateinit var apexEnvironment: ApexEnvironment
-
- private val persistence = RolesPersistence.createInstance()
- private val state = RolesState(1, "packagesHash", mapOf("role" to setOf("holder1", "holder2")))
- private val user = Process.myUserHandle()
-
- @Before
- fun createMockDataDirectory() {
- mockDataDirectory = context.getDir("mock_data", Context.MODE_PRIVATE)
- mockDataDirectory.listFiles()!!.forEach { assertThat(it.deleteRecursively()).isTrue() }
- }
-
- @Before
- fun mockApexEnvironment() {
- initMocks(this)
- mockitoSession = mockitoSession()
- .mockStatic(ApexEnvironment::class.java)
- .strictness(Strictness.LENIENT)
- .startMocking()
- `when`(ApexEnvironment.getApexEnvironment(eq(APEX_MODULE_NAME))).thenReturn(apexEnvironment)
- `when`(apexEnvironment.getDeviceProtectedDataDirForUser(any(UserHandle::class.java))).then {
- File(mockDataDirectory, it.arguments[0].toString()).also { it.mkdirs() }
- }
- }
-
- @After
- fun finishMockingApexEnvironment() {
- mockitoSession.finishMocking()
- }
-
- @Test
- fun testReadWrite() {
- persistence.writeForUser(state, user)
- val persistedState = persistence.readForUser(user)
-
- assertThat(persistedState).isEqualTo(state)
- assertThat(persistedState!!.version).isEqualTo(state.version)
- assertThat(persistedState.packagesHash).isEqualTo(state.packagesHash)
- assertThat(persistedState.roles).isEqualTo(state.roles)
- }
-
- @Test
- fun testDelete() {
- persistence.writeForUser(state, user)
- persistence.deleteForUser(user)
- val persistedState = persistence.readForUser(user)
-
- assertThat(persistedState).isNull()
- }
-
- companion object {
- private const val APEX_MODULE_NAME = "com.android.permission"
- }
-}
diff --git a/boot/Android.bp b/boot/Android.bp
new file mode 100644
index 0000000..dd4066a
--- /dev/null
+++ b/boot/Android.bp
@@ -0,0 +1,18 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+boot_image {
+ name: "framework-boot-image",
+ image_name: "boot",
+}
diff --git a/core/api/current.txt b/core/api/current.txt
index f49ce1f..ec712d8 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -24225,7 +24225,8 @@
method public void setCallback(@Nullable android.media.session.MediaSession.Callback, @Nullable android.os.Handler);
method public void setExtras(@Nullable android.os.Bundle);
method public void setFlags(int);
- method public void setMediaButtonReceiver(@Nullable android.app.PendingIntent);
+ method public void setMediaButtonBroadcastReceiver(@Nullable android.content.ComponentName);
+ method @Deprecated public void setMediaButtonReceiver(@Nullable android.app.PendingIntent);
method public void setMetadata(@Nullable android.media.MediaMetadata);
method public void setPlaybackState(@Nullable android.media.session.PlaybackState);
method public void setPlaybackToLocal(android.media.AudioAttributes);
@@ -30191,6 +30192,8 @@
field @Deprecated public static final String RADIO;
field @Deprecated public static final String SERIAL;
field @NonNull public static final String SKU;
+ field @NonNull public static final String SOC_MANUFACTURER;
+ field @NonNull public static final String SOC_MODEL;
field public static final String[] SUPPORTED_32_BIT_ABIS;
field public static final String[] SUPPORTED_64_BIT_ABIS;
field public static final String[] SUPPORTED_ABIS;
@@ -31878,6 +31881,8 @@
}
public final class UnsafeIntentLaunchViolation extends android.os.strictmode.Violation {
+ ctor public UnsafeIntentLaunchViolation(@NonNull android.content.Intent);
+ method @Nullable public android.content.Intent getIntent();
}
public final class UntaggedSocketViolation extends android.os.strictmode.Violation {
@@ -36817,9 +36822,10 @@
package android.security.keystore {
public class BackendBusyException extends java.security.ProviderException {
- ctor public BackendBusyException();
- ctor public BackendBusyException(@NonNull String);
- ctor public BackendBusyException(@NonNull String, @NonNull Throwable);
+ ctor public BackendBusyException(long);
+ ctor public BackendBusyException(long, @NonNull String);
+ ctor public BackendBusyException(long, @NonNull String, @NonNull Throwable);
+ method public long getBackOffHintMillis();
}
public class KeyExpiredException extends java.security.InvalidKeyException {
@@ -36957,6 +36963,7 @@
field public static final int ORIGIN_IMPORTED = 2; // 0x2
field public static final int ORIGIN_SECURELY_IMPORTED = 8; // 0x8
field public static final int ORIGIN_UNKNOWN = 4; // 0x4
+ field public static final int PURPOSE_AGREE_KEY = 64; // 0x40
field public static final int PURPOSE_DECRYPT = 2; // 0x2
field public static final int PURPOSE_ENCRYPT = 1; // 0x1
field public static final int PURPOSE_SIGN = 4; // 0x4
@@ -38570,6 +38577,7 @@
public class SpeechRecognizer {
method public void cancel();
+ method @NonNull public static android.speech.SpeechRecognizer createOnDeviceSpeechRecognizer(@NonNull android.content.Context);
method public static android.speech.SpeechRecognizer createSpeechRecognizer(android.content.Context);
method public static android.speech.SpeechRecognizer createSpeechRecognizer(android.content.Context, android.content.ComponentName);
method public void destroy();
@@ -39707,7 +39715,6 @@
field public static final String EXTRA_HAS_PICTURE = "android.telecom.extra.HAS_PICTURE";
field public static final String EXTRA_INCOMING_CALL_ADDRESS = "android.telecom.extra.INCOMING_CALL_ADDRESS";
field public static final String EXTRA_INCOMING_CALL_EXTRAS = "android.telecom.extra.INCOMING_CALL_EXTRAS";
- field public static final String EXTRA_INCOMING_PICTURE = "android.telecom.extra.INCOMING_PICTURE";
field public static final String EXTRA_INCOMING_VIDEO_STATE = "android.telecom.extra.INCOMING_VIDEO_STATE";
field public static final String EXTRA_IS_DEFAULT_CALL_SCREENING_APP = "android.telecom.extra.IS_DEFAULT_CALL_SCREENING_APP";
field public static final String EXTRA_LOCATION = "android.telecom.extra.LOCATION";
@@ -39716,6 +39723,7 @@
field public static final String EXTRA_OUTGOING_CALL_EXTRAS = "android.telecom.extra.OUTGOING_CALL_EXTRAS";
field public static final String EXTRA_OUTGOING_PICTURE = "android.telecom.extra.OUTGOING_PICTURE";
field public static final String EXTRA_PHONE_ACCOUNT_HANDLE = "android.telecom.extra.PHONE_ACCOUNT_HANDLE";
+ field public static final String EXTRA_PICTURE_URI = "android.telecom.extra.PICTURE_URI";
field public static final String EXTRA_PRIORITY = "android.telecom.extra.PRIORITY";
field public static final String EXTRA_START_CALL_WITH_RTT = "android.telecom.extra.START_CALL_WITH_RTT";
field public static final String EXTRA_START_CALL_WITH_SPEAKERPHONE = "android.telecom.extra.START_CALL_WITH_SPEAKERPHONE";
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index b3e78ab..0f1296e 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -217,6 +217,7 @@
field public static final String REQUEST_NETWORK_SCORES = "android.permission.REQUEST_NETWORK_SCORES";
field public static final String REQUEST_NOTIFICATION_ASSISTANT_SERVICE = "android.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE";
field public static final String RESET_PASSWORD = "android.permission.RESET_PASSWORD";
+ field public static final String RESTART_WIFI_SUBSYSTEM = "android.permission.RESTART_WIFI_SUBSYSTEM";
field public static final String RESTORE_RUNTIME_PERMISSIONS = "android.permission.RESTORE_RUNTIME_PERMISSIONS";
field public static final String RESTRICTED_VR_ACCESS = "android.permission.RESTRICTED_VR_ACCESS";
field public static final String RETRIEVE_WINDOW_CONTENT = "android.permission.RETRIEVE_WINDOW_CONTENT";
@@ -2369,6 +2370,7 @@
field public static final int FLAG_PERMISSION_REVIEW_REQUIRED = 64; // 0x40
field public static final int FLAG_PERMISSION_REVOKED_COMPAT = 8; // 0x8
field @Deprecated public static final int FLAG_PERMISSION_REVOKE_ON_UPGRADE = 8; // 0x8
+ field public static final int FLAG_PERMISSION_SELECTED_LOCATION_ACCURACY = 524288; // 0x80000
field public static final int FLAG_PERMISSION_SYSTEM_FIXED = 16; // 0x10
field public static final int FLAG_PERMISSION_USER_FIXED = 2; // 0x2
field public static final int FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED = 512; // 0x200
@@ -5605,12 +5607,12 @@
public class Filter implements java.lang.AutoCloseable {
method public void close();
method public int configure(@NonNull android.media.tv.tuner.filter.FilterConfiguration);
- method public int configureMonitorEvent(int);
method public int flush();
method public int getId();
method public long getId64Bit();
method public int read(@NonNull byte[], long, long);
method public int setDataSource(@Nullable android.media.tv.tuner.filter.Filter);
+ method public int setMonitorEventMask(int);
method public int start();
method public int stop();
field public static final int MONITOR_EVENT_IP_CID_CHANGE = 2; // 0x2
@@ -12962,11 +12964,100 @@
field public static final String RCS_PROFILE_2_3 = "UP_2.3";
}
+ public final class RcsContactPresenceTuple implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public android.net.Uri getContactUri();
+ method @Nullable public android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities getServiceCapabilities();
+ method @Nullable public String getServiceDescription();
+ method @NonNull public String getServiceId();
+ method @NonNull public String getServiceVersion();
+ method @NonNull public String getStatus();
+ method @Nullable public String getTimestamp();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.RcsContactPresenceTuple> CREATOR;
+ field public static final String SERVICE_ID_CALL_COMPOSER = "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.gsma.callcomposer";
+ field public static final String SERVICE_ID_CHATBOT = "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.chatbot";
+ field public static final String SERVICE_ID_CHATBOT_ROLE = "org.gsma.rcs.isbot";
+ field public static final String SERVICE_ID_CHATBOT_STANDALONE = " org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.chatbot.sa";
+ field public static final String SERVICE_ID_CHAT_V1 = "org.openmobilealliance:IM-session";
+ field public static final String SERVICE_ID_CHAT_V2 = "org.openmobilealliance:ChatSession";
+ field public static final String SERVICE_ID_FT = "org.openmobilealliance:File-Transfer-HTTP";
+ field public static final String SERVICE_ID_FT_OVER_SMS = "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.ftsms";
+ field public static final String SERVICE_ID_GEO_PUSH = "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.geopush";
+ field public static final String SERVICE_ID_GEO_PUSH_VIA_SMS = "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.geosms";
+ field public static final String SERVICE_ID_MMTEL = "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.mmtel";
+ field public static final String SERVICE_ID_POST_CALL = "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.gsma.callunanswered";
+ field public static final String SERVICE_ID_SHARED_MAP = "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.gsma.sharedmap";
+ field public static final String SERVICE_ID_SHARED_SKETCH = "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.gsma.sharedsketch";
+ field public static final String TUPLE_BASIC_STATUS_CLOSED = "closed";
+ field public static final String TUPLE_BASIC_STATUS_OPEN = "open";
+ }
+
+ public static final class RcsContactPresenceTuple.Builder {
+ ctor public RcsContactPresenceTuple.Builder(@NonNull String, @NonNull String, @NonNull String);
+ method @NonNull public android.telephony.ims.RcsContactPresenceTuple build();
+ method @NonNull public android.telephony.ims.RcsContactPresenceTuple.Builder setContactUri(@NonNull android.net.Uri);
+ method @NonNull public android.telephony.ims.RcsContactPresenceTuple.Builder setServiceCapabilities(@NonNull android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities);
+ method @NonNull public android.telephony.ims.RcsContactPresenceTuple.Builder setServiceDescription(@NonNull String);
+ method @NonNull public android.telephony.ims.RcsContactPresenceTuple.Builder setTimestamp(@NonNull String);
+ }
+
+ public static final class RcsContactPresenceTuple.ServiceCapabilities implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public java.util.List<java.lang.String> getSupportedDuplexModes();
+ method @NonNull public java.util.List<java.lang.String> getUnsupportedDuplexModes();
+ method public boolean isAudioCapable();
+ method public boolean isVideoCapable();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities> CREATOR;
+ field public static final String DUPLEX_MODE_FULL = "full";
+ field public static final String DUPLEX_MODE_HALF = "half";
+ field public static final String DUPLEX_MODE_RECEIVE_ONLY = "receive-only";
+ field public static final String DUPLEX_MODE_SEND_ONLY = "send-only";
+ }
+
+ public static final class RcsContactPresenceTuple.ServiceCapabilities.Builder {
+ ctor public RcsContactPresenceTuple.ServiceCapabilities.Builder(boolean, boolean);
+ method @NonNull public android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities.Builder addSupportedDuplexMode(@NonNull String);
+ method @NonNull public android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities.Builder addUnsupportedDuplexMode(@NonNull String);
+ method @NonNull public android.telephony.ims.RcsContactPresenceTuple.ServiceCapabilities build();
+ }
+
+ public final class RcsContactUceCapability implements android.os.Parcelable {
+ method public int describeContents();
+ method public int getCapabilityMechanism();
+ method @Nullable public android.telephony.ims.RcsContactPresenceTuple getCapabilityTuple(@NonNull String);
+ method @NonNull public java.util.List<android.telephony.ims.RcsContactPresenceTuple> getCapabilityTuples();
+ method @NonNull public android.net.Uri getContactUri();
+ method public int getRequestResult();
+ method public int getSourceType();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field public static final int CAPABILITY_MECHANISM_OPTIONS = 2; // 0x2
+ field public static final int CAPABILITY_MECHANISM_PRESENCE = 1; // 0x1
+ field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.RcsContactUceCapability> CREATOR;
+ field public static final int REQUEST_RESULT_FOUND = 3; // 0x3
+ field public static final int REQUEST_RESULT_NOT_FOUND = 2; // 0x2
+ field public static final int REQUEST_RESULT_NOT_ONLINE = 1; // 0x1
+ field public static final int REQUEST_RESULT_UNKNOWN = 0; // 0x0
+ field public static final int SOURCE_TYPE_CACHED = 1; // 0x1
+ field public static final int SOURCE_TYPE_NETWORK = 0; // 0x0
+ }
+
+ public static final class RcsContactUceCapability.PresenceBuilder {
+ ctor public RcsContactUceCapability.PresenceBuilder(@NonNull android.net.Uri, int, int);
+ method @NonNull public android.telephony.ims.RcsContactUceCapability.PresenceBuilder addCapabilityTuple(@NonNull android.telephony.ims.RcsContactPresenceTuple);
+ method @NonNull public android.telephony.ims.RcsContactUceCapability.PresenceBuilder addCapabilityTuples(@NonNull java.util.List<android.telephony.ims.RcsContactPresenceTuple>);
+ method @NonNull public android.telephony.ims.RcsContactUceCapability build();
+ }
+
public class RcsUceAdapter {
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void addOnPublishStateChangedListener(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RcsUceAdapter.OnPublishStateChangedListener) throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getUcePublishState() throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void removeOnPublishStateChangedListener(@NonNull android.telephony.ims.RcsUceAdapter.OnPublishStateChangedListener) throws android.telephony.ims.ImsException;
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void requestAvailability(@NonNull android.net.Uri, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RcsUceAdapter.CapabilitiesCallback) throws android.telephony.ims.ImsException;
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void requestCapabilities(@NonNull java.util.List<android.net.Uri>, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.RcsUceAdapter.CapabilitiesCallback) throws android.telephony.ims.ImsException;
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setUceSettingEnabled(boolean) throws android.telephony.ims.ImsException;
+ field public static final int CAPABILITY_TYPE_PRESENCE_UCE = 2; // 0x2
field public static final int CAPABILITY_UPDATE_TRIGGER_ETAG_EXPIRED = 1; // 0x1
field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_2G = 7; // 0x7
field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_3G = 6; // 0x6
@@ -12979,6 +13070,18 @@
field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_NR5G_VOPS_ENABLED = 11; // 0xb
field public static final int CAPABILITY_UPDATE_TRIGGER_MOVE_TO_WLAN = 8; // 0x8
field public static final int CAPABILITY_UPDATE_TRIGGER_UNKNOWN = 0; // 0x0
+ field public static final int ERROR_FORBIDDEN = 6; // 0x6
+ field public static final int ERROR_GENERIC_FAILURE = 1; // 0x1
+ field public static final int ERROR_INSUFFICIENT_MEMORY = 10; // 0xa
+ field public static final int ERROR_LOST_NETWORK = 11; // 0xb
+ field public static final int ERROR_NOT_AUTHORIZED = 5; // 0x5
+ field public static final int ERROR_NOT_AVAILABLE = 3; // 0x3
+ field public static final int ERROR_NOT_ENABLED = 2; // 0x2
+ field public static final int ERROR_NOT_FOUND = 7; // 0x7
+ field public static final int ERROR_NOT_REGISTERED = 4; // 0x4
+ field public static final int ERROR_REQUEST_TIMEOUT = 9; // 0x9
+ field public static final int ERROR_REQUEST_TOO_LARGE = 8; // 0x8
+ field public static final int ERROR_SERVER_UNAVAILABLE = 12; // 0xc
field public static final int PUBLISH_STATE_NOT_PUBLISHED = 2; // 0x2
field public static final int PUBLISH_STATE_OK = 1; // 0x1
field public static final int PUBLISH_STATE_OTHER_ERROR = 6; // 0x6
@@ -12987,6 +13090,12 @@
field public static final int PUBLISH_STATE_VOICE_PROVISION_ERROR = 3; // 0x3
}
+ public static interface RcsUceAdapter.CapabilitiesCallback {
+ method public void onCapabilitiesReceived(@NonNull java.util.List<android.telephony.ims.RcsContactUceCapability>);
+ method public void onComplete();
+ method public void onError(int, long);
+ }
+
public static interface RcsUceAdapter.OnPublishStateChangedListener {
method public void onPublishStateChange(int);
}
@@ -13398,6 +13507,7 @@
public class RcsCapabilityExchangeImplBase {
ctor public RcsCapabilityExchangeImplBase(@NonNull java.util.concurrent.Executor);
method public void publishCapabilities(@NonNull String, @NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase.PublishResponseCallback);
+ method public void subscribeForCapabilities(@NonNull java.util.List<android.net.Uri>, @NonNull android.telephony.ims.stub.RcsCapabilityExchangeImplBase.SubscribeResponseCallback);
field public static final int COMMAND_CODE_FETCH_ERROR = 3; // 0x3
field public static final int COMMAND_CODE_GENERIC_FAILURE = 1; // 0x1
field public static final int COMMAND_CODE_INSUFFICIENT_MEMORY = 5; // 0x5
@@ -13416,6 +13526,14 @@
method public void onNetworkResponse(@IntRange(from=100, to=699) int, @NonNull String) throws android.telephony.ims.ImsException;
}
+ public static interface RcsCapabilityExchangeImplBase.SubscribeResponseCallback {
+ method public void onCommandError(int) throws android.telephony.ims.ImsException;
+ method public void onNetworkResponse(@IntRange(from=100, to=699) int, @NonNull String) throws android.telephony.ims.ImsException;
+ method public void onNotifyCapabilitiesUpdate(@NonNull java.util.List<java.lang.String>) throws android.telephony.ims.ImsException;
+ method public void onResourceTerminated(@NonNull java.util.List<android.util.Pair<android.net.Uri,java.lang.String>>) throws android.telephony.ims.ImsException;
+ method public void onTerminated(@NonNull String, long) throws android.telephony.ims.ImsException;
+ }
+
public interface SipDelegate {
method public void closeDialog(@NonNull String);
method public void notifyMessageReceiveError(@NonNull String, int);
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 415105f..bdd541a 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -2669,6 +2669,10 @@
dispatchActivityDestroyed();
notifyContentCaptureManagerIfNeeded(CONTENT_CAPTURE_STOP);
+
+ if (mUiTranslationController != null) {
+ mUiTranslationController.onActivityDestroyed();
+ }
}
/**
@@ -7877,6 +7881,10 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
final void performCreate(Bundle icicle, PersistableBundle persistentState) {
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) {
+ Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "performCreate:"
+ + mComponent.getClassName());
+ }
dispatchActivityPreCreated(icicle);
mCanEnterPictureInPicture = true;
// initialize mIsInMultiWindowMode and mIsInPictureInPictureMode before onCreate
@@ -7899,6 +7907,7 @@
mFragments.dispatchActivityCreated();
mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions());
dispatchActivityPostCreated(icicle);
+ Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
}
final void performNewIntent(@NonNull Intent intent) {
@@ -8004,6 +8013,10 @@
}
final void performResume(boolean followedByPause, String reason) {
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) {
+ Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "performResume:"
+ + mComponent.getClassName());
+ }
dispatchActivityPreResumed();
performRestart(true /* start */, reason);
@@ -8055,9 +8068,14 @@
" did not call through to super.onPostResume()");
}
dispatchActivityPostResumed();
+ Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
}
final void performPause() {
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) {
+ Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "performPause:"
+ + mComponent.getClassName());
+ }
dispatchActivityPrePaused();
mDoReportFullyDrawn = false;
mFragments.dispatchPause();
@@ -8073,6 +8091,7 @@
" did not call through to super.onPause()");
}
dispatchActivityPostPaused();
+ Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
}
final void performUserLeaving() {
@@ -8081,6 +8100,10 @@
}
final void performStop(boolean preserveWindow, String reason) {
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) {
+ Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "performStop:"
+ + mComponent.getClassName());
+ }
mDoReportFullyDrawn = false;
mFragments.doLoaderStop(mChangingConfigurations /*retain*/);
@@ -8126,9 +8149,14 @@
dispatchActivityPostStopped();
}
mResumed = false;
+ Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
}
final void performDestroy() {
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_WINDOW_MANAGER)) {
+ Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "performDestroy:"
+ + mComponent.getClassName());
+ }
dispatchActivityPreDestroyed();
mDestroyed = true;
mWindow.destroy();
@@ -8141,6 +8169,7 @@
mVoiceInteractor.detachActivity();
}
dispatchActivityPostDestroyed();
+ Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
}
final void dispatchMultiWindowModeChanged(boolean isInMultiWindowMode,
@@ -8450,9 +8479,8 @@
}
/** @hide */
- @Override
@Nullable
- public final View autofillClientFindViewByAutofillIdTraversal(AutofillId autofillId) {
+ public View findViewByAutofillIdTraversal(@NonNull AutofillId autofillId) {
final ArrayList<ViewRootImpl> roots =
WindowManagerGlobal.getInstance().getRootViews(getActivityToken());
for (int rootNum = 0; rootNum < roots.size(); rootNum++) {
@@ -8465,12 +8493,18 @@
}
}
}
-
return null;
}
/** @hide */
@Override
+ @Nullable
+ public final View autofillClientFindViewByAutofillIdTraversal(AutofillId autofillId) {
+ return findViewByAutofillIdTraversal(autofillId);
+ }
+
+ /** @hide */
+ @Override
public final @NonNull boolean[] autofillClientGetViewVisibility(
@NonNull AutofillId[] autofillIds) {
final int autofillIdCount = autofillIds.length;
diff --git a/core/java/android/app/ActivityClient.java b/core/java/android/app/ActivityClient.java
index d465b22..401f8cc 100644
--- a/core/java/android/app/ActivityClient.java
+++ b/core/java/android/app/ActivityClient.java
@@ -26,6 +26,8 @@
import android.util.Singleton;
import android.view.RemoteAnimationDefinition;
+import com.android.internal.policy.IKeyguardDismissCallback;
+
/**
* Provides the activity associated operations that communicate with system.
*
@@ -431,6 +433,37 @@
}
}
+ /**
+ * Restart the process and activity to adopt the latest configuration for size compat mode.
+ * This only takes effect for visible activity because invisible background activity can be
+ * restarted naturally when it becomes visible.
+ */
+ public void restartActivityProcessIfVisible(IBinder token) {
+ try {
+ getActivityClientController().restartActivityProcessIfVisible(token);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /** Removes the snapshot of home task. */
+ public void invalidateHomeTaskSnapshot(IBinder homeToken) {
+ try {
+ getActivityClientController().invalidateHomeTaskSnapshot(homeToken);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ void dismissKeyguard(IBinder token, IKeyguardDismissCallback callback,
+ CharSequence message) {
+ try {
+ getActivityClientController().dismissKeyguard(token, callback, message);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
void registerRemoteAnimations(IBinder token, RemoteAnimationDefinition definition) {
try {
getActivityClientController().registerRemoteAnimations(token, definition);
diff --git a/core/java/android/app/IActivityClientController.aidl b/core/java/android/app/IActivityClientController.aidl
index ebf1027..c2c62c1 100644
--- a/core/java/android/app/IActivityClientController.aidl
+++ b/core/java/android/app/IActivityClientController.aidl
@@ -25,6 +25,8 @@
import android.os.PersistableBundle;
import android.view.RemoteAnimationDefinition;
+import com.android.internal.policy.IKeyguardDismissCallback;
+
/**
* Interface for the callback and request from an activity to system.
*
@@ -34,7 +36,13 @@
oneway void activityIdle(in IBinder token, in Configuration config, in boolean stopProfiling);
oneway void activityResumed(in IBinder token);
oneway void activityTopResumedStateLost();
- oneway void activityPaused(in IBinder token);
+ /**
+ * Notifies that the activity has completed paused. This call is not one-way because it can make
+ * consecutive launch in the same process more coherent. About the order of binder call, it
+ * should be fine with other one-way calls because if pause hasn't completed on the server side,
+ * there won't be other lifecycle changes.
+ */
+ void activityPaused(in IBinder token);
oneway void activityStopped(in IBinder token, in Bundle state,
in PersistableBundle persistentState, in CharSequence description);
oneway void activityDestroyed(in IBinder token);
@@ -95,6 +103,27 @@
/** See {@link android.app.Activity#setDisablePreviewScreenshots}. */
oneway void setDisablePreviewScreenshots(in IBinder token, boolean disable);
+ /**
+ * Restarts the activity by killing its process if it is visible. If the activity is not
+ * visible, the activity will not be restarted immediately and just keep the activity record in
+ * the stack. It also resets the current override configuration so the activity will use the
+ * configuration according to the latest state.
+ *
+ * @param activityToken The token of the target activity to restart.
+ */
+ void restartActivityProcessIfVisible(in IBinder activityToken);
+
+ /**
+ * It should only be called from home activity to remove its outdated snapshot. The home
+ * snapshot is used to speed up entering home from screen off. If the content of home activity
+ * is significantly different from before taking the snapshot, then the home activity can use
+ * this method to avoid inconsistent transition.
+ */
+ void invalidateHomeTaskSnapshot(IBinder homeToken);
+
+ void dismissKeyguard(in IBinder token, in IKeyguardDismissCallback callback,
+ in CharSequence message);
+
/** Registers remote animations for a specific activity. */
void registerRemoteAnimations(in IBinder token, in RemoteAnimationDefinition definition);
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index b085340..38a3e70 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -72,7 +72,6 @@
import android.window.IWindowOrganizerController;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.os.IResultReceiver;
-import com.android.internal.policy.IKeyguardDismissCallback;
import java.util.List;
@@ -85,8 +84,6 @@
// TODO(b/174040395): Make this interface private to ActivityTaskManager.java and have external
// caller go through that call instead. This would help us better separate and control the API
// surface exposed.
-// TODO(b/174041144): Move callback methods from Activity (Things that take param 'IBinder token')
-// to a separate interface that is only available to the Activity.
// TODO(b/174041603): Create a builder interface for things like startActivityXXX(...) to reduce
// interface duplication.
// TODO(b/174040691): Clean-up/remove all obsolete or unused interfaces like things that should be
@@ -294,9 +291,6 @@
// Get device configuration
ConfigurationInfo getDeviceConfigurationInfo();
- void dismissKeyguard(in IBinder token, in IKeyguardDismissCallback callback,
- in CharSequence message);
-
/** Cancels the window transitions for the given task. */
void cancelTaskWindowTransition(int taskId);
@@ -309,14 +303,6 @@
android.window.TaskSnapshot getTaskSnapshot(int taskId, boolean isLowResolution);
/**
- * It should only be called from home activity to remove its outdated snapshot. The home
- * snapshot is used to speed up entering home from screen off. If the content of home activity
- * is significantly different from before taking the snapshot, then the home activity can use
- * this method to avoid inconsistent transition.
- */
- void invalidateHomeTaskSnapshot(IBinder homeToken);
-
- /**
* Return the user id of last resumed activity.
*/
int getLastResumedActivityUserId();
@@ -362,14 +348,4 @@
* Clears launch params for given packages.
*/
void clearLaunchParamsForPackages(in List<String> packageNames);
-
- /**
- * Restarts the activity by killing its process if it is visible. If the activity is not
- * visible, the activity will not be restarted immediately and just keep the activity record in
- * the stack. It also resets the current override configuration so the activity will use the
- * configuration according to the latest state.
- *
- * @param activityToken The token of the target activity to restart.
- */
- void restartActivityProcessIfVisible(in IBinder activityToken);
}
diff --git a/core/java/android/app/KeyguardManager.java b/core/java/android/app/KeyguardManager.java
index 545c3f7..b6d25cf 100644
--- a/core/java/android/app/KeyguardManager.java
+++ b/core/java/android/app/KeyguardManager.java
@@ -598,33 +598,29 @@
@SystemApi
public void requestDismissKeyguard(@NonNull Activity activity, @Nullable CharSequence message,
@Nullable KeyguardDismissCallback callback) {
- try {
- ActivityTaskManager.getService().dismissKeyguard(
- activity.getActivityToken(), new IKeyguardDismissCallback.Stub() {
- @Override
- public void onDismissError() throws RemoteException {
- if (callback != null && !activity.isDestroyed()) {
- activity.mHandler.post(callback::onDismissError);
- }
+ ActivityClient.getInstance().dismissKeyguard(
+ activity.getActivityToken(), new IKeyguardDismissCallback.Stub() {
+ @Override
+ public void onDismissError() throws RemoteException {
+ if (callback != null && !activity.isDestroyed()) {
+ activity.mHandler.post(callback::onDismissError);
}
+ }
- @Override
- public void onDismissSucceeded() throws RemoteException {
- if (callback != null && !activity.isDestroyed()) {
- activity.mHandler.post(callback::onDismissSucceeded);
- }
+ @Override
+ public void onDismissSucceeded() throws RemoteException {
+ if (callback != null && !activity.isDestroyed()) {
+ activity.mHandler.post(callback::onDismissSucceeded);
}
+ }
- @Override
- public void onDismissCancelled() throws RemoteException {
- if (callback != null && !activity.isDestroyed()) {
- activity.mHandler.post(callback::onDismissCancelled);
- }
+ @Override
+ public void onDismissCancelled() throws RemoteException {
+ if (callback != null && !activity.isDestroyed()) {
+ activity.mHandler.post(callback::onDismissCancelled);
}
- }, message);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ }
+ }, message);
}
/**
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index c5206d7..0c50446 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -64,7 +64,6 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.StatFs;
-import android.os.StrictMode;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.StorageManager;
@@ -3575,6 +3574,7 @@
LIGHTS_SERVICE,
//@hide: PEOPLE_SERVICE,
//@hide: DEVICE_STATE_SERVICE,
+ //@hide: SPEECH_RECOGNITION_SERVICE,
UWB_SERVICE,
MEDIA_METRICS_SERVICE,
})
@@ -5410,6 +5410,14 @@
public static final String MEDIA_METRICS_SERVICE = "media_metrics";
/**
+ * Use with {@link #getSystemService(String)} to access system speech recognition service.
+ *
+ * @see #getSystemService(String)
+ * @hide
+ */
+ public static final String SPEECH_RECOGNITION_SERVICE = "speech_recognition";
+
+ /**
* Determine whether the given permission is allowed for a particular
* process and user ID running in the system.
*
@@ -6469,7 +6477,7 @@
* {@link WindowManager}, {@link android.view.LayoutInflater LayoutInflater} or
* {@link android.app.WallpaperManager WallpaperManager}. Accessing UI components from non-UI
* contexts throws {@link android.os.strictmode.Violation} if
- * {@link StrictMode.VmPolicy.Builder#detectIncorrectContextUse()} is enabled.
+ * {@link android.os.StrictMode.VmPolicy.Builder#detectIncorrectContextUse()} is enabled.
* <p>
* Examples of UI contexts are
* an {@link android.app.Activity Activity}, a context created from
@@ -6479,7 +6487,7 @@
*
* @see #getDisplay()
* @see #getSystemService(String)
- * @see StrictMode.VmPolicy.Builder#detectIncorrectContextUse()
+ * @see android.os.StrictMode.VmPolicy.Builder#detectIncorrectContextUse()
*/
public static boolean isUiContext(@NonNull Context context) {
return context.isUiContext();
diff --git a/core/java/android/content/integrity/OWNERS b/core/java/android/content/integrity/OWNERS
new file mode 100644
index 0000000..20c758a
--- /dev/null
+++ b/core/java/android/content/integrity/OWNERS
@@ -0,0 +1,5 @@
+# Bug component: 722021
+
+toddke@android.com
+toddke@google.com
+patb@google.com
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index fcd573e..68792b2 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3971,6 +3971,15 @@
public static final int FLAG_PERMISSION_RESTRICTION_ROLE_EXEMPT = 1 << 18;
/**
+ * Permission flag: This location permission is selected as the level of granularity of
+ * location accuracy.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int FLAG_PERMISSION_SELECTED_LOCATION_ACCURACY = 1 << 19;
+
+ /**
* Permission flags: Reserved for use by the permission controller. The platform and any
* packages besides the permission controller should not assume any definition about these
* flags.
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 45f072a..fe8bf9d 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -30,6 +30,7 @@
import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE;
import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION;
import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE;
+import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
@@ -53,8 +54,6 @@
import android.content.ComponentName;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.split.DefaultSplitAssetLoader;
-import android.content.pm.split.SplitAssetDependencyLoader;
import android.content.pm.split.SplitAssetLoader;
import android.content.res.ApkAssets;
import android.content.res.AssetManager;
@@ -80,6 +79,7 @@
import android.util.AttributeSet;
import android.util.Base64;
import android.util.DisplayMetrics;
+import android.util.IntArray;
import android.util.Log;
import android.util.PackageUtils;
import android.util.Pair;
@@ -118,6 +118,7 @@
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.BitSet;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
@@ -8573,4 +8574,410 @@
this.error = error;
}
}
+
+ // Duplicate the SplitAsset related classes with PackageParser.Package/ApkLite here, and
+ // change the original one using new Package/ApkLite. The propose is that we don't want to
+ // have two branches of methods in SplitAsset related classes so we can keep real classes
+ // clean and move all the legacy code to one place.
+
+ /**
+ * A helper class that implements the dependency tree traversal for splits. Callbacks
+ * are implemented by subclasses to notify whether a split has already been constructed
+ * and is cached, and to actually create the split requested.
+ *
+ * This helper is meant to be subclassed so as to reduce the number of allocations
+ * needed to make use of it.
+ *
+ * All inputs and outputs are assumed to be indices into an array of splits.
+ *
+ * @hide
+ * @deprecated Do not use. New changes should use
+ * {@link android.content.pm.split.SplitDependencyLoader} instead.
+ */
+ @Deprecated
+ private abstract static class SplitDependencyLoader<E extends Exception> {
+ private final @NonNull SparseArray<int[]> mDependencies;
+
+ /**
+ * Construct a new SplitDependencyLoader. Meant to be called from the
+ * subclass constructor.
+ * @param dependencies The dependency tree of splits.
+ */
+ protected SplitDependencyLoader(@NonNull SparseArray<int[]> dependencies) {
+ mDependencies = dependencies;
+ }
+
+ /**
+ * Traverses the dependency tree and constructs any splits that are not already
+ * cached. This routine short-circuits and skips the creation of splits closer to the
+ * root if they are cached, as reported by the subclass implementation of
+ * {@link #isSplitCached(int)}. The construction of splits is delegated to the subclass
+ * implementation of {@link #constructSplit(int, int[], int)}.
+ * @param splitIdx The index of the split to load. 0 represents the base Application.
+ */
+ protected void loadDependenciesForSplit(@IntRange(from = 0) int splitIdx) throws E {
+ // Quick check before any allocations are done.
+ if (isSplitCached(splitIdx)) {
+ return;
+ }
+
+ // Special case the base, since it has no dependencies.
+ if (splitIdx == 0) {
+ final int[] configSplitIndices = collectConfigSplitIndices(0);
+ constructSplit(0, configSplitIndices, -1);
+ return;
+ }
+
+ // Build up the dependency hierarchy.
+ final IntArray linearDependencies = new IntArray();
+ linearDependencies.add(splitIdx);
+
+ // Collect all the dependencies that need to be constructed.
+ // They will be listed from leaf to root.
+ while (true) {
+ // Only follow the first index into the array. The others are config splits and
+ // get loaded with the split.
+ final int[] deps = mDependencies.get(splitIdx);
+ if (deps != null && deps.length > 0) {
+ splitIdx = deps[0];
+ } else {
+ splitIdx = -1;
+ }
+
+ if (splitIdx < 0 || isSplitCached(splitIdx)) {
+ break;
+ }
+
+ linearDependencies.add(splitIdx);
+ }
+
+ // Visit each index, from right to left (root to leaf).
+ int parentIdx = splitIdx;
+ for (int i = linearDependencies.size() - 1; i >= 0; i--) {
+ final int idx = linearDependencies.get(i);
+ final int[] configSplitIndices = collectConfigSplitIndices(idx);
+ constructSplit(idx, configSplitIndices, parentIdx);
+ parentIdx = idx;
+ }
+ }
+
+ private @NonNull int[] collectConfigSplitIndices(int splitIdx) {
+ // The config splits appear after the first element.
+ final int[] deps = mDependencies.get(splitIdx);
+ if (deps == null || deps.length <= 1) {
+ return EmptyArray.INT;
+ }
+ return Arrays.copyOfRange(deps, 1, deps.length);
+ }
+
+ /**
+ * Subclass to report whether the split at `splitIdx` is cached and need not be constructed.
+ * It is assumed that if `splitIdx` is cached, any parent of `splitIdx` is also cached.
+ * @param splitIdx The index of the split to check for in the cache.
+ * @return true if the split is cached and does not need to be constructed.
+ */
+ protected abstract boolean isSplitCached(@IntRange(from = 0) int splitIdx);
+
+ /**
+ * Subclass to construct a split at index `splitIdx` with parent split `parentSplitIdx`.
+ * The result is expected to be cached by the subclass in its own structures.
+ * @param splitIdx The index of the split to construct. 0 represents the base Application.
+ * @param configSplitIndices The array of configuration splits to load along with this
+ * split. May be empty (length == 0) but never null.
+ * @param parentSplitIdx The index of the parent split. -1 if there is no parent.
+ * @throws E Subclass defined exception representing failure to construct a split.
+ */
+ protected abstract void constructSplit(@IntRange(from = 0) int splitIdx,
+ @NonNull @IntRange(from = 1) int[] configSplitIndices,
+ @IntRange(from = -1) int parentSplitIdx) throws E;
+
+ public static class IllegalDependencyException extends Exception {
+ private IllegalDependencyException(String message) {
+ super(message);
+ }
+ }
+
+ private static int[] append(int[] src, int elem) {
+ if (src == null) {
+ return new int[] { elem };
+ }
+ int[] dst = Arrays.copyOf(src, src.length + 1);
+ dst[src.length] = elem;
+ return dst;
+ }
+
+ public static @NonNull SparseArray<int[]> createDependenciesFromPackage(
+ PackageLite pkg)
+ throws SplitDependencyLoader.IllegalDependencyException {
+ // The data structure that holds the dependencies. In PackageParser, splits are stored
+ // in their own array, separate from the base. We treat all paths as equals, so
+ // we need to insert the base as index 0, and shift all other splits.
+ final SparseArray<int[]> splitDependencies = new SparseArray<>();
+
+ // The base depends on nothing.
+ splitDependencies.put(0, new int[] {-1});
+
+ // First write out the <uses-split> dependencies. These must appear first in the
+ // array of ints, as is convention in this class.
+ for (int splitIdx = 0; splitIdx < pkg.splitNames.length; splitIdx++) {
+ if (!pkg.isFeatureSplits[splitIdx]) {
+ // Non-feature splits don't have dependencies.
+ continue;
+ }
+
+ // Implicit dependency on the base.
+ final int targetIdx;
+ final String splitDependency = pkg.usesSplitNames[splitIdx];
+ if (splitDependency != null) {
+ final int depIdx = Arrays.binarySearch(pkg.splitNames, splitDependency);
+ if (depIdx < 0) {
+ throw new SplitDependencyLoader.IllegalDependencyException(
+ "Split '" + pkg.splitNames[splitIdx] + "' requires split '"
+ + splitDependency + "', which is missing.");
+ }
+ targetIdx = depIdx + 1;
+ } else {
+ // Implicitly depend on the base.
+ targetIdx = 0;
+ }
+ splitDependencies.put(splitIdx + 1, new int[] {targetIdx});
+ }
+
+ // Write out the configForSplit reverse-dependencies. These appear after the
+ // <uses-split> dependencies and are considered leaves.
+ //
+ // At this point, all splits in splitDependencies have the first element in their
+ // array set.
+ for (int splitIdx = 0, size = pkg.splitNames.length; splitIdx < size; splitIdx++) {
+ if (pkg.isFeatureSplits[splitIdx]) {
+ // Feature splits are not configForSplits.
+ continue;
+ }
+
+ // Implicit feature for the base.
+ final int targetSplitIdx;
+ final String configForSplit = pkg.configForSplit[splitIdx];
+ if (configForSplit != null) {
+ final int depIdx = Arrays.binarySearch(pkg.splitNames, configForSplit);
+ if (depIdx < 0) {
+ throw new SplitDependencyLoader.IllegalDependencyException(
+ "Split '" + pkg.splitNames[splitIdx] + "' targets split '"
+ + configForSplit + "', which is missing.");
+ }
+
+ if (!pkg.isFeatureSplits[depIdx]) {
+ throw new SplitDependencyLoader.IllegalDependencyException(
+ "Split '" + pkg.splitNames[splitIdx] + "' declares itself as "
+ + "configuration split for a non-feature split '"
+ + pkg.splitNames[depIdx] + "'");
+ }
+ targetSplitIdx = depIdx + 1;
+ } else {
+ targetSplitIdx = 0;
+ }
+ splitDependencies.put(targetSplitIdx,
+ append(splitDependencies.get(targetSplitIdx), splitIdx + 1));
+ }
+
+ // Verify that there are no cycles.
+ final BitSet bitset = new BitSet();
+ for (int i = 0, size = splitDependencies.size(); i < size; i++) {
+ int splitIdx = splitDependencies.keyAt(i);
+
+ bitset.clear();
+ while (splitIdx != -1) {
+ // Check if this split has been visited yet.
+ if (bitset.get(splitIdx)) {
+ throw new SplitDependencyLoader.IllegalDependencyException(
+ "Cycle detected in split dependencies.");
+ }
+
+ // Mark the split so that if we visit it again, we no there is a cycle.
+ bitset.set(splitIdx);
+
+ // Follow the first dependency only, the others are leaves by definition.
+ final int[] deps = splitDependencies.get(splitIdx);
+ splitIdx = deps != null ? deps[0] : -1;
+ }
+ }
+ return splitDependencies;
+ }
+ }
+
+ /**
+ * Loads the base and split APKs into a single AssetManager.
+ * @hide
+ * @deprecated Do not use. New changes should use
+ * {@link android.content.pm.split.DefaultSplitAssetLoader} instead.
+ */
+ @Deprecated
+ private static class DefaultSplitAssetLoader implements SplitAssetLoader {
+ private final String mBaseCodePath;
+ private final String[] mSplitCodePaths;
+ private final @ParseFlags int mFlags;
+ private AssetManager mCachedAssetManager;
+
+ DefaultSplitAssetLoader(PackageLite pkg, @ParseFlags int flags) {
+ mBaseCodePath = pkg.baseCodePath;
+ mSplitCodePaths = pkg.splitCodePaths;
+ mFlags = flags;
+ }
+
+ private static ApkAssets loadApkAssets(String path, @ParseFlags int flags)
+ throws PackageParserException {
+ if ((flags & PackageParser.PARSE_MUST_BE_APK) != 0 && !PackageParser.isApkPath(path)) {
+ throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
+ "Invalid package file: " + path);
+ }
+
+ try {
+ return ApkAssets.loadFromPath(path);
+ } catch (IOException e) {
+ throw new PackageParserException(INSTALL_FAILED_INVALID_APK,
+ "Failed to load APK at path " + path, e);
+ }
+ }
+
+ @Override
+ public AssetManager getBaseAssetManager() throws PackageParserException {
+ if (mCachedAssetManager != null) {
+ return mCachedAssetManager;
+ }
+
+ ApkAssets[] apkAssets = new ApkAssets[(mSplitCodePaths != null
+ ? mSplitCodePaths.length : 0) + 1];
+
+ // Load the base.
+ int splitIdx = 0;
+ apkAssets[splitIdx++] = loadApkAssets(mBaseCodePath, mFlags);
+
+ // Load any splits.
+ if (!ArrayUtils.isEmpty(mSplitCodePaths)) {
+ for (String apkPath : mSplitCodePaths) {
+ apkAssets[splitIdx++] = loadApkAssets(apkPath, mFlags);
+ }
+ }
+
+ AssetManager assets = new AssetManager();
+ assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ Build.VERSION.RESOURCES_SDK_INT);
+ assets.setApkAssets(apkAssets, false /*invalidateCaches*/);
+
+ mCachedAssetManager = assets;
+ return mCachedAssetManager;
+ }
+
+ @Override
+ public AssetManager getSplitAssetManager(int splitIdx) throws PackageParserException {
+ return getBaseAssetManager();
+ }
+
+ @Override
+ public void close() throws Exception {
+ IoUtils.closeQuietly(mCachedAssetManager);
+ }
+ }
+
+ /**
+ * Loads AssetManagers for splits and their dependencies. This SplitAssetLoader implementation
+ * is to be used when an application opts-in to isolated split loading.
+ * @hide
+ * @deprecated Do not use. New changes should use
+ * {@link android.content.pm.split.SplitAssetDependencyLoader} instead.
+ */
+ @Deprecated
+ private static class SplitAssetDependencyLoader extends
+ SplitDependencyLoader<PackageParserException> implements SplitAssetLoader {
+ private final String[] mSplitPaths;
+ private final @ParseFlags int mFlags;
+ private final ApkAssets[][] mCachedSplitApks;
+ private final AssetManager[] mCachedAssetManagers;
+
+ SplitAssetDependencyLoader(PackageLite pkg,
+ SparseArray<int[]> dependencies, @ParseFlags int flags) {
+ super(dependencies);
+
+ // The base is inserted into index 0, so we need to shift all the splits by 1.
+ mSplitPaths = new String[pkg.splitCodePaths.length + 1];
+ mSplitPaths[0] = pkg.baseCodePath;
+ System.arraycopy(pkg.splitCodePaths, 0, mSplitPaths, 1, pkg.splitCodePaths.length);
+
+ mFlags = flags;
+ mCachedSplitApks = new ApkAssets[mSplitPaths.length][];
+ mCachedAssetManagers = new AssetManager[mSplitPaths.length];
+ }
+
+ @Override
+ protected boolean isSplitCached(int splitIdx) {
+ return mCachedAssetManagers[splitIdx] != null;
+ }
+
+ private static ApkAssets loadApkAssets(String path, @ParseFlags int flags)
+ throws PackageParserException {
+ if ((flags & PackageParser.PARSE_MUST_BE_APK) != 0 && !PackageParser.isApkPath(path)) {
+ throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
+ "Invalid package file: " + path);
+ }
+
+ try {
+ return ApkAssets.loadFromPath(path);
+ } catch (IOException e) {
+ throw new PackageParserException(PackageManager.INSTALL_FAILED_INVALID_APK,
+ "Failed to load APK at path " + path, e);
+ }
+ }
+
+ private static AssetManager createAssetManagerWithAssets(ApkAssets[] apkAssets) {
+ final AssetManager assets = new AssetManager();
+ assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ Build.VERSION.RESOURCES_SDK_INT);
+ assets.setApkAssets(apkAssets, false /*invalidateCaches*/);
+ return assets;
+ }
+
+ @Override
+ protected void constructSplit(int splitIdx, @NonNull int[] configSplitIndices,
+ int parentSplitIdx) throws PackageParserException {
+ final ArrayList<ApkAssets> assets = new ArrayList<>();
+
+ // Include parent ApkAssets.
+ if (parentSplitIdx >= 0) {
+ Collections.addAll(assets, mCachedSplitApks[parentSplitIdx]);
+ }
+
+ // Include this ApkAssets.
+ assets.add(loadApkAssets(mSplitPaths[splitIdx], mFlags));
+
+ // Load and include all config splits for this feature.
+ for (int configSplitIdx : configSplitIndices) {
+ assets.add(loadApkAssets(mSplitPaths[configSplitIdx], mFlags));
+ }
+
+ // Cache the results.
+ mCachedSplitApks[splitIdx] = assets.toArray(new ApkAssets[assets.size()]);
+ mCachedAssetManagers[splitIdx] = createAssetManagerWithAssets(
+ mCachedSplitApks[splitIdx]);
+ }
+
+ @Override
+ public AssetManager getBaseAssetManager() throws PackageParserException {
+ loadDependenciesForSplit(0);
+ return mCachedAssetManagers[0];
+ }
+
+ @Override
+ public AssetManager getSplitAssetManager(int idx) throws PackageParserException {
+ // Since we insert the base at position 0, and PackageParser keeps splits separate from
+ // the base, we need to adjust the index.
+ loadDependenciesForSplit(idx + 1);
+ return mCachedAssetManagers[idx + 1];
+ }
+
+ @Override
+ public void close() throws Exception {
+ for (AssetManager assets : mCachedAssetManagers) {
+ IoUtils.closeQuietly(assets);
+ }
+ }
+ }
}
diff --git a/core/java/android/content/pm/dex/DexMetadataHelper.java b/core/java/android/content/pm/dex/DexMetadataHelper.java
index 982fce9..bf35c4d 100644
--- a/core/java/android/content/pm/dex/DexMetadataHelper.java
+++ b/core/java/android/content/pm/dex/DexMetadataHelper.java
@@ -17,11 +17,11 @@
package android.content.pm.dex;
import static android.content.pm.PackageManager.INSTALL_FAILED_BAD_DEX_METADATA;
-import static android.content.pm.PackageParser.APK_FILE_EXTENSION;
+import static android.content.pm.parsing.ApkLiteParseUtils.APK_FILE_EXTENSION;
-import android.content.pm.PackageParser;
-import android.content.pm.PackageParser.PackageLite;
import android.content.pm.PackageParser.PackageParserException;
+import android.content.pm.parsing.ApkLiteParseUtils;
+import android.content.pm.parsing.PackageLite;
import android.util.ArrayMap;
import android.util.jar.StrictJarFile;
@@ -87,7 +87,7 @@
* NOTE: involves I/O checks.
*/
private static Map<String, String> getPackageDexMetadata(PackageLite pkg) {
- return buildPackageApkToDexMetadataMap(pkg.getAllCodePaths());
+ return buildPackageApkToDexMetadataMap(pkg.getAllApkPaths());
}
/**
@@ -125,7 +125,7 @@
* @throws IllegalArgumentException if the code path is not an .apk.
*/
public static String buildDexMetadataPathForApk(String codePath) {
- if (!PackageParser.isApkPath(codePath)) {
+ if (!ApkLiteParseUtils.isApkPath(codePath)) {
throw new IllegalStateException(
"Corrupted package. Code path is not an apk " + codePath);
}
@@ -140,7 +140,7 @@
* extension (e.g. 'foo.dm' will match 'foo' or 'foo.apk').
*/
private static String buildDexMetadataPathForFile(File targetFile) {
- return PackageParser.isApkFile(targetFile)
+ return ApkLiteParseUtils.isApkFile(targetFile)
? buildDexMetadataPathForApk(targetFile.getPath())
: targetFile.getPath() + DEX_METADATA_FILE_EXTENSION;
}
@@ -179,7 +179,7 @@
public static void validateDexPaths(String[] paths) {
ArrayList<String> apks = new ArrayList<>();
for (int i = 0; i < paths.length; i++) {
- if (PackageParser.isApkPath(paths[i])) {
+ if (ApkLiteParseUtils.isApkPath(paths[i])) {
apks.add(paths[i]);
}
}
diff --git a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
index 51b81b6..a3c2cbc 100644
--- a/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
+++ b/core/java/android/content/pm/parsing/ApkLiteParseUtils.java
@@ -66,6 +66,8 @@
private static final int PARSE_DEFAULT_INSTALL_LOCATION =
PackageInfo.INSTALL_LOCATION_UNSPECIFIED;
+ private static final Comparator<String> sSplitNameComparator = new SplitNameComparator();
+
public static final String APK_FILE_EXTENSION = ".apk";
/**
@@ -79,7 +81,7 @@
*
* @see PackageParser#parsePackage(File, int)
*/
- public static ParseResult<PackageParser.PackageLite> parsePackageLite(ParseInput input,
+ public static ParseResult<PackageLite> parsePackageLite(ParseInput input,
File packageFile, int flags) {
if (packageFile.isDirectory()) {
return parseClusterPackageLite(input, packageFile, flags);
@@ -88,26 +90,32 @@
}
}
- public static ParseResult<PackageParser.PackageLite> parseMonolithicPackageLite(
- ParseInput input, File packageFile, int flags) {
+ /**
+ * Parse lightweight details about a single APK files.
+ */
+ public static ParseResult<PackageLite> parseMonolithicPackageLite(ParseInput input,
+ File packageFile, int flags) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parseApkLite");
try {
- ParseResult<PackageParser.ApkLite> result = parseApkLite(input, packageFile, flags);
+ final ParseResult<ApkLite> result = parseApkLite(input, packageFile, flags);
if (result.isError()) {
return input.error(result);
}
- final PackageParser.ApkLite baseApk = result.getResult();
+ final ApkLite baseApk = result.getResult();
final String packagePath = packageFile.getAbsolutePath();
return input.success(
- new PackageParser.PackageLite(packagePath, baseApk.codePath, baseApk, null,
+ new PackageLite(packagePath, baseApk.getPath(), baseApk, null,
null, null, null, null, null));
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
- public static ParseResult<PackageParser.PackageLite> parseClusterPackageLite(ParseInput input,
+ /**
+ * Parse lightweight details about a directory of APKs.
+ */
+ public static ParseResult<PackageLite> parseClusterPackageLite(ParseInput input,
File packageDir, int flags) {
final File[] files = packageDir.listFiles();
if (ArrayUtils.isEmpty(files)) {
@@ -122,39 +130,39 @@
String packageName = null;
int versionCode = 0;
- final ArrayMap<String, PackageParser.ApkLite> apks = new ArrayMap<>();
+ final ArrayMap<String, ApkLite> apks = new ArrayMap<>();
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parseApkLite");
try {
for (File file : files) {
- if (PackageParser.isApkFile(file)) {
- ParseResult<PackageParser.ApkLite> result = parseApkLite(input, file, flags);
+ if (isApkFile(file)) {
+ final ParseResult<ApkLite> result = parseApkLite(input, file, flags);
if (result.isError()) {
return input.error(result);
}
- final PackageParser.ApkLite lite = result.getResult();
+ final ApkLite lite = result.getResult();
// Assert that all package names and version codes are
// consistent with the first one we encounter.
if (packageName == null) {
- packageName = lite.packageName;
- versionCode = lite.versionCode;
+ packageName = lite.getPackageName();
+ versionCode = lite.getVersionCode();
} else {
- if (!packageName.equals(lite.packageName)) {
+ if (!packageName.equals(lite.getPackageName())) {
return input.error(PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST,
- "Inconsistent package " + lite.packageName + " in " + file
+ "Inconsistent package " + lite.getPackageName() + " in " + file
+ "; expected " + packageName);
}
- if (versionCode != lite.versionCode) {
+ if (versionCode != lite.getVersionCode()) {
return input.error(PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST,
- "Inconsistent version " + lite.versionCode + " in " + file
+ "Inconsistent version " + lite.getVersionCode() + " in " + file
+ "; expected " + versionCode);
}
}
// Assert that each split is defined only oncuses-static-libe
- if (apks.put(lite.splitName, lite) != null) {
+ if (apks.put(lite.getSplitName(), lite) != null) {
return input.error(PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST,
- "Split name " + lite.splitName
+ "Split name " + lite.getSplitName()
+ " defined more than once; most recent was " + file);
}
}
@@ -163,7 +171,7 @@
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
- final PackageParser.ApkLite baseApk = apks.remove(null);
+ final ApkLite baseApk = apks.remove(null);
return composePackageLiteFromApks(input, packageDir, baseApk, apks);
}
@@ -176,9 +184,8 @@
* @param splitApks Parsed split APKs
* @return PackageLite
*/
- public static ParseResult<PackageParser.PackageLite> composePackageLiteFromApks(
- ParseInput input, File packageDir, PackageParser.ApkLite baseApk,
- ArrayMap<String, PackageParser.ApkLite> splitApks) {
+ public static ParseResult<PackageLite> composePackageLiteFromApks(ParseInput input,
+ File packageDir, ApkLite baseApk, ArrayMap<String, ApkLite> splitApks) {
return composePackageLiteFromApks(input, packageDir, baseApk, splitApks, false);
}
@@ -192,9 +199,9 @@
* @param apkRenamed Indicate whether the APKs are renamed after parsed.
* @return PackageLite
*/
- public static ParseResult<PackageParser.PackageLite> composePackageLiteFromApks(
- ParseInput input, File packageDir, PackageParser.ApkLite baseApk,
- ArrayMap<String, PackageParser.ApkLite> splitApks, boolean apkRenamed) {
+ public static ParseResult<PackageLite> composePackageLiteFromApks(
+ ParseInput input, File packageDir, ApkLite baseApk,
+ ArrayMap<String, ApkLite> splitApks, boolean apkRenamed) {
if (baseApk == null) {
return input.error(PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST,
"Missing base APK in " + packageDir);
@@ -217,26 +224,25 @@
splitRevisionCodes = new int[size];
splitNames = splitApks.keySet().toArray(splitNames);
- Arrays.sort(splitNames, PackageParser.sSplitNameComparator);
+ Arrays.sort(splitNames, sSplitNameComparator);
for (int i = 0; i < size; i++) {
- final PackageParser.ApkLite apk = splitApks.get(splitNames[i]);
- usesSplitNames[i] = apk.usesSplitName;
- isFeatureSplits[i] = apk.isFeatureSplit;
- configForSplits[i] = apk.configForSplit;
+ final ApkLite apk = splitApks.get(splitNames[i]);
+ usesSplitNames[i] = apk.getUsesSplitName();
+ isFeatureSplits[i] = apk.isFeatureSplit();
+ configForSplits[i] = apk.getConfigForSplit();
splitCodePaths[i] = apkRenamed ? new File(packageDir,
- splitNameToFileName(apk)).getAbsolutePath() : apk.codePath;
- splitRevisionCodes[i] = apk.revisionCode;
+ splitNameToFileName(apk)).getAbsolutePath() : apk.getPath();
+ splitRevisionCodes[i] = apk.getRevisionCode();
}
}
final String codePath = packageDir.getAbsolutePath();
final String baseCodePath = apkRenamed ? new File(packageDir,
- splitNameToFileName(baseApk)).getAbsolutePath() : baseApk.codePath;
+ splitNameToFileName(baseApk)).getAbsolutePath() : baseApk.getPath();
return input.success(
- new PackageParser.PackageLite(codePath, baseCodePath, baseApk, splitNames,
- isFeatureSplits, usesSplitNames, configForSplits, splitCodePaths,
- splitRevisionCodes));
+ new PackageLite(codePath, baseCodePath, baseApk, splitNames, isFeatureSplits,
+ usesSplitNames, configForSplits, splitCodePaths, splitRevisionCodes));
}
/**
@@ -245,9 +251,9 @@
* @param apk Parsed APK
* @return The canonical file name
*/
- public static String splitNameToFileName(@NonNull PackageParser.ApkLite apk) {
+ public static String splitNameToFileName(@NonNull ApkLite apk) {
Objects.requireNonNull(apk);
- final String fileName = apk.splitName == null ? "base" : "split_" + apk.splitName;
+ final String fileName = apk.getSplitName() == null ? "base" : "split_" + apk.getSplitName();
return fileName + APK_FILE_EXTENSION;
}
@@ -257,10 +263,9 @@
*
* @param apkFile path to a single APK
* @param flags optional parse flags, such as
- * {@link PackageParser#PARSE_COLLECT_CERTIFICATES}
+ * {@link ParsingPackageUtils#PARSE_COLLECT_CERTIFICATES}
*/
- public static ParseResult<PackageParser.ApkLite> parseApkLite(ParseInput input, File apkFile,
- int flags) {
+ public static ParseResult<ApkLite> parseApkLite(ParseInput input, File apkFile, int flags) {
return parseApkLiteInner(input, apkFile, null, null, flags);
}
@@ -271,14 +276,14 @@
* @param fd already open file descriptor of an apk file
* @param debugPathName arbitrary text name for this file, for debug output
* @param flags optional parse flags, such as
- * {@link PackageParser#PARSE_COLLECT_CERTIFICATES}
+ * {@link ParsingPackageUtils#PARSE_COLLECT_CERTIFICATES}
*/
- public static ParseResult<PackageParser.ApkLite> parseApkLite(ParseInput input,
+ public static ParseResult<ApkLite> parseApkLite(ParseInput input,
FileDescriptor fd, String debugPathName, int flags) {
return parseApkLiteInner(input, null, fd, debugPathName, flags);
}
- private static ParseResult<PackageParser.ApkLite> parseApkLiteInner(ParseInput input,
+ private static ParseResult<ApkLite> parseApkLiteInner(ParseInput input,
File apkFile, FileDescriptor fd, String debugPathName, int flags) {
final String apkPath = fd != null ? debugPathName : apkFile.getAbsolutePath();
@@ -294,11 +299,11 @@
"Failed to parse " + apkPath, e);
}
- parser = apkAssets.openXml(PackageParser.ANDROID_MANIFEST_FILENAME);
+ parser = apkAssets.openXml(ParsingPackageUtils.ANDROID_MANIFEST_FILENAME);
final PackageParser.SigningDetails signingDetails;
- if ((flags & PackageParser.PARSE_COLLECT_CERTIFICATES) != 0) {
- final boolean skipVerify = (flags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0;
+ if ((flags & ParsingPackageUtils.PARSE_COLLECT_CERTIFICATES) != 0) {
+ final boolean skipVerify = (flags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) != 0;
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
try {
ParseResult<PackageParser.SigningDetails> result =
@@ -335,9 +340,8 @@
}
}
- private static ParseResult<PackageParser.ApkLite> parseApkLite(ParseInput input,
- String codePath, XmlPullParser parser, AttributeSet attrs,
- PackageParser.SigningDetails signingDetails)
+ private static ParseResult<ApkLite> parseApkLite(ParseInput input, String codePath,
+ XmlPullParser parser, AttributeSet attrs, PackageParser.SigningDetails signingDetails)
throws IOException, XmlPullParserException {
ParseResult<Pair<String, String>> result = parsePackageSplitNames(input, parser, attrs);
if (result.isError()) {
@@ -421,12 +425,12 @@
continue;
}
- if (PackageParser.TAG_PACKAGE_VERIFIER.equals(parser.getName())) {
+ if (ParsingPackageUtils.TAG_PACKAGE_VERIFIER.equals(parser.getName())) {
final VerifierInfo verifier = parseVerifier(attrs);
if (verifier != null) {
verifiers.add(verifier);
}
- } else if (PackageParser.TAG_APPLICATION.equals(parser.getName())) {
+ } else if (ParsingPackageUtils.TAG_APPLICATION.equals(parser.getName())) {
for (int i = 0; i < attrs.getAttributeCount(); ++i) {
final String attr = attrs.getAttributeName(i);
switch (attr) {
@@ -464,7 +468,7 @@
continue;
}
- if (PackageParser.TAG_PROFILEABLE.equals(parser.getName())) {
+ if (ParsingPackageUtils.TAG_PROFILEABLE.equals(parser.getName())) {
for (int i = 0; i < attrs.getAttributeCount(); ++i) {
final String attr = attrs.getAttributeName(i);
if ("shell".equals(attr)) {
@@ -474,7 +478,7 @@
}
}
}
- } else if (PackageParser.TAG_OVERLAY.equals(parser.getName())) {
+ } else if (ParsingPackageUtils.TAG_OVERLAY.equals(parser.getName())) {
for (int i = 0; i < attrs.getAttributeCount(); ++i) {
final String attr = attrs.getAttributeName(i);
if ("requiredSystemPropertyName".equals(attr)) {
@@ -489,7 +493,7 @@
overlayPriority = attrs.getAttributeIntValue(i, 0);
}
}
- } else if (PackageParser.TAG_USES_SPLIT.equals(parser.getName())) {
+ } else if (ParsingPackageUtils.TAG_USES_SPLIT.equals(parser.getName())) {
if (usesSplitName != null) {
Slog.w(TAG, "Only one <uses-split> permitted. Ignoring others.");
continue;
@@ -500,7 +504,7 @@
return input.error(PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
"<uses-split> tag requires 'android:name' attribute");
}
- } else if (PackageParser.TAG_USES_SDK.equals(parser.getName())) {
+ } else if (ParsingPackageUtils.TAG_USES_SDK.equals(parser.getName())) {
for (int i = 0; i < attrs.getAttributeCount(); ++i) {
final String attr = attrs.getAttributeName(i);
if ("targetSdkVersion".equals(attr)) {
@@ -526,8 +530,8 @@
}
return input.success(
- new PackageParser.ApkLite(codePath, packageSplit.first, packageSplit.second,
- isFeatureSplit, configForSplit, usesSplitName, isSplitRequired, versionCode,
+ new ApkLite(codePath, packageSplit.first, packageSplit.second, isFeatureSplit,
+ configForSplit, usesSplitName, isSplitRequired, versionCode,
versionCodeMajor, revisionCode, installLocation, verifiers, signingDetails,
coreApp, debuggable, profilableByShell, multiArch, use32bitAbi,
useEmbeddedDex, extractNativeLibs, isolatedSplits, targetPackage,
@@ -546,7 +550,7 @@
return input.error(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
"No start tag found");
}
- if (!parser.getName().equals(PackageParser.TAG_MANIFEST)) {
+ if (!parser.getName().equals(ParsingPackageUtils.TAG_MANIFEST)) {
return input.error(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
"No <manifest> tag");
}
@@ -625,4 +629,24 @@
}
}
}
+
+ /**
+ * Check if the given file is an APK file.
+ *
+ * @param file the file to check.
+ * @return {@code true} if the given file is an APK file.
+ */
+ public static boolean isApkFile(File file) {
+ return isApkPath(file.getName());
+ }
+
+ /**
+ * Check if the given path ends with APK file extension.
+ *
+ * @param path the path to check.
+ * @return {@code true} if the given path ends with APK file extension.
+ */
+ public static boolean isApkPath(String path) {
+ return path.endsWith(APK_FILE_EXTENSION);
+ }
}
diff --git a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
index f8fd4a5..b7365b3 100644
--- a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
+++ b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
@@ -385,7 +385,7 @@
}
// CompatibilityMode is global state.
- if (!PackageParser.sCompatibilityModeEnabled) {
+ if (!ParsingPackageUtils.sCompatibilityModeEnabled) {
ai.disableCompatibilityMode();
}
diff --git a/core/java/android/content/pm/parsing/ParsingPackageImpl.java b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
index 38d3940..51ec297 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageImpl.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageImpl.java
@@ -335,7 +335,7 @@
private int fullBackupContent;
private int iconRes;
- private int installLocation = PackageParser.PARSE_DEFAULT_INSTALL_LOCATION;
+ private int installLocation = ParsingPackageUtils.PARSE_DEFAULT_INSTALL_LOCATION;
private int labelRes;
private int largestWidthLimitDp;
private int logo;
@@ -1013,7 +1013,8 @@
// TODO(b/135203078): See ParsingPackageImpl#getHiddenApiEnforcementPolicy
// appInfo.mHiddenApiPolicy
// appInfo.hiddenUntilInstalled
- appInfo.icon = (PackageParser.sUseRoundIcon && roundIconRes != 0) ? roundIconRes : iconRes;
+ appInfo.icon =
+ (ParsingPackageUtils.sUseRoundIcon && roundIconRes != 0) ? roundIconRes : iconRes;
appInfo.iconRes = iconRes;
appInfo.roundIconRes = roundIconRes;
appInfo.installLocation = installLocation;
diff --git a/core/java/android/content/pm/parsing/ParsingPackageRead.java b/core/java/android/content/pm/parsing/ParsingPackageRead.java
index 13ae7a2..a102e82 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageRead.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageRead.java
@@ -66,7 +66,7 @@
/**
* The names of packages to adopt ownership of permissions from, parsed under
- * {@link PackageParser#TAG_ADOPT_PERMISSIONS}.
+ * {@link ParsingPackageUtils#TAG_ADOPT_PERMISSIONS}.
* @see R.styleable#AndroidManifestOriginalPackage_name
*/
@NonNull
@@ -105,7 +105,7 @@
/**
* For use with {@link com.android.server.pm.KeySetManagerService}. Parsed in
- * {@link PackageParser#TAG_KEY_SETS}.
+ * {@link ParsingPackageUtils#TAG_KEY_SETS}.
* @see R.styleable#AndroidManifestKeySet
* @see R.styleable#AndroidManifestPublicKey
*/
@@ -816,7 +816,7 @@
/**
* For use with {@link com.android.server.pm.KeySetManagerService}. Parsed in
- * {@link PackageParser#TAG_KEY_SETS}.
+ * {@link ParsingPackageUtils#TAG_KEY_SETS}.
* @see R.styleable#AndroidManifestUpgradeKeySet
*/
@NonNull
diff --git a/core/java/android/content/pm/parsing/ParsingPackageUtils.java b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
index 494b3cc..b054304 100644
--- a/core/java/android/content/pm/parsing/ParsingPackageUtils.java
+++ b/core/java/android/content/pm/parsing/ParsingPackageUtils.java
@@ -189,6 +189,12 @@
PackageInfo.INSTALL_LOCATION_UNSPECIFIED;
public static final int PARSE_DEFAULT_TARGET_SANDBOX = 1;
+ /** If set to true, we will only allow package files that exactly match
+ * the DTD. Otherwise, we try to get as much from the package as we
+ * can without failing. This should normally be set to false, to
+ * support extensions to the DTD in future versions. */
+ public static final boolean RIGID_PARSER = false;
+
public static final int PARSE_MUST_BE_APK = 1 << 0;
public static final int PARSE_IGNORE_PROCESSES = 1 << 1;
public static final int PARSE_EXTERNAL_STORAGE = 1 << 3;
@@ -220,7 +226,7 @@
*/
@NonNull
public static ParseResult<ParsingPackage> parseDefaultOneTime(File file,
- @PackageParser.ParseFlags int parseFlags, boolean collectCertificates) {
+ @ParseFlags int parseFlags, boolean collectCertificates) {
ParseInput input = ParseTypeImpl.forDefaultParsing().reset();
return parseDefault(input, file, parseFlags, collectCertificates);
}
@@ -232,7 +238,7 @@
*/
@NonNull
public static ParseResult<ParsingPackage> parseDefault(ParseInput input, File file,
- @PackageParser.ParseFlags int parseFlags, boolean collectCertificates) {
+ @ParseFlags int parseFlags, boolean collectCertificates) {
ParseResult<ParsingPackage> result;
ParsingPackageUtils parser = new ParsingPackageUtils(false, null, null, new Callback() {
@@ -334,14 +340,14 @@
*/
private ParseResult<ParsingPackage> parseClusterPackage(ParseInput input, File packageDir,
int flags) {
- ParseResult<PackageParser.PackageLite> liteResult =
+ final ParseResult<PackageLite> liteResult =
ApkLiteParseUtils.parseClusterPackageLite(input, packageDir, 0);
if (liteResult.isError()) {
return input.error(liteResult);
}
- final PackageParser.PackageLite lite = liteResult.getResult();
- if (mOnlyCoreApps && !lite.coreApp) {
+ final PackageLite lite = liteResult.getResult();
+ if (mOnlyCoreApps && !lite.isCoreApp()) {
return input.error(INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED,
"Not a coreApp: " + packageDir);
}
@@ -349,7 +355,7 @@
// Build the split dependency tree.
SparseArray<int[]> splitDependencies = null;
final SplitAssetLoader assetLoader;
- if (lite.isolatedSplits && !ArrayUtils.isEmpty(lite.splitNames)) {
+ if (lite.isIsolatedSplits() && !ArrayUtils.isEmpty(lite.getSplitNames())) {
try {
splitDependencies = SplitAssetDependencyLoader.createDependenciesFromPackage(lite);
assetLoader = new SplitAssetDependencyLoader(lite, splitDependencies, flags);
@@ -362,22 +368,22 @@
try {
final AssetManager assets = assetLoader.getBaseAssetManager();
- final File baseApk = new File(lite.baseCodePath);
- ParseResult<ParsingPackage> result = parseBaseApk(input, baseApk,
- lite.codePath, assets, flags);
+ final File baseApk = new File(lite.getBaseApkPath());
+ final ParseResult<ParsingPackage> result = parseBaseApk(input, baseApk,
+ lite.getPath(), assets, flags);
if (result.isError()) {
return input.error(result);
}
ParsingPackage pkg = result.getResult();
- if (!ArrayUtils.isEmpty(lite.splitNames)) {
+ if (!ArrayUtils.isEmpty(lite.getSplitNames())) {
pkg.asSplit(
- lite.splitNames,
- lite.splitCodePaths,
- lite.splitRevisionCodes,
+ lite.getSplitNames(),
+ lite.getSplitApkPaths(),
+ lite.getSplitRevisionCodes(),
splitDependencies
);
- final int num = lite.splitNames.length;
+ final int num = lite.getSplitNames().length;
for (int i = 0; i < num; i++) {
final AssetManager splitAssets = assetLoader.getSplitAssetManager(i);
@@ -385,11 +391,11 @@
}
}
- pkg.setUse32BitAbi(lite.use32bitAbi);
+ pkg.setUse32BitAbi(lite.isUse32bitAbi());
return input.success(pkg);
} catch (PackageParserException e) {
return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
- "Failed to load assets: " + lite.baseCodePath, e);
+ "Failed to load assets: " + lite.getBaseApkPath(), e);
} finally {
IoUtils.closeQuietly(assetLoader);
}
@@ -403,21 +409,21 @@
*/
private ParseResult<ParsingPackage> parseMonolithicPackage(ParseInput input, File apkFile,
int flags) throws PackageParserException {
- ParseResult<PackageParser.PackageLite> liteResult =
+ final ParseResult<PackageLite> liteResult =
ApkLiteParseUtils.parseMonolithicPackageLite(input, apkFile, flags);
if (liteResult.isError()) {
return input.error(liteResult);
}
- final PackageParser.PackageLite lite = liteResult.getResult();
- if (mOnlyCoreApps && !lite.coreApp) {
+ final PackageLite lite = liteResult.getResult();
+ if (mOnlyCoreApps && !lite.isCoreApp()) {
return input.error(INSTALL_PARSE_FAILED_ONLY_COREAPP_ALLOWED,
"Not a coreApp: " + apkFile);
}
final SplitAssetLoader assetLoader = new DefaultSplitAssetLoader(lite, flags);
try {
- ParseResult<ParsingPackage> result = parseBaseApk(input,
+ final ParseResult<ParsingPackage> result = parseBaseApk(input,
apkFile,
apkFile.getCanonicalPath(),
assetLoader.getBaseAssetManager(), flags);
@@ -426,7 +432,7 @@
}
return input.success(result.getResult()
- .setUse32BitAbi(lite.use32bitAbi));
+ .setUse32BitAbi(lite.isUse32bitAbi()));
} catch (IOException e) {
return input.error(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
"Failed to get path: " + apkFile, e);
@@ -440,12 +446,12 @@
final String apkPath = apkFile.getAbsolutePath();
String volumeUuid = null;
- if (apkPath.startsWith(PackageParser.MNT_EXPAND)) {
- final int end = apkPath.indexOf('/', PackageParser.MNT_EXPAND.length());
- volumeUuid = apkPath.substring(PackageParser.MNT_EXPAND.length(), end);
+ if (apkPath.startsWith(MNT_EXPAND)) {
+ final int end = apkPath.indexOf('/', MNT_EXPAND.length());
+ volumeUuid = apkPath.substring(MNT_EXPAND.length(), end);
}
- if (PackageParser.DEBUG_JAR) Slog.d(TAG, "Scanning base APK: " + apkPath);
+ if (DEBUG_JAR) Slog.d(TAG, "Scanning base APK: " + apkPath);
final int cookie = assets.findCookieForPath(apkPath);
if (cookie == 0) {
@@ -454,7 +460,7 @@
}
try (XmlResourceParser parser = assets.openXmlResourceParser(cookie,
- PackageParser.ANDROID_MANIFEST_FILENAME)) {
+ ANDROID_MANIFEST_FILENAME)) {
final Resources res = new Resources(assets, mDisplayMetrics, null);
ParseResult<ParsingPackage> result = parseBaseApk(input, apkPath, codePath, res,
@@ -495,7 +501,7 @@
pkg.setVolumeUuid(volumeUuid);
- if ((flags & PackageParser.PARSE_COLLECT_CERTIFICATES) != 0) {
+ if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) {
pkg.setSigningDetails(getSigningDetails(pkg, false));
} else {
pkg.setSigningDetails(SigningDetails.UNKNOWN);
@@ -512,7 +518,7 @@
ParsingPackage pkg, int splitIndex, AssetManager assets, int flags) {
final String apkPath = pkg.getSplitCodePaths()[splitIndex];
- if (PackageParser.DEBUG_JAR) Slog.d(TAG, "Scanning split APK: " + apkPath);
+ if (DEBUG_JAR) Slog.d(TAG, "Scanning split APK: " + apkPath);
// This must always succeed, as the path has been added to the AssetManager before.
final int cookie = assets.findCookieForPath(apkPath);
@@ -521,7 +527,7 @@
"Failed adding asset path: " + apkPath);
}
try (XmlResourceParser parser = assets.openXmlResourceParser(cookie,
- PackageParser.ANDROID_MANIFEST_FILENAME)) {
+ ANDROID_MANIFEST_FILENAME)) {
Resources res = new Resources(assets, mDisplayMetrics, null);
ParseResult<ParsingPackage> parseResult = parseSplitApk(input, pkg, res,
parser, flags, splitIndex);
@@ -620,9 +626,9 @@
final ParseResult result;
String tagName = parser.getName();
- if (PackageParser.TAG_APPLICATION.equals(tagName)) {
+ if (TAG_APPLICATION.equals(tagName)) {
if (foundApp) {
- if (PackageParser.RIGID_PARSER) {
+ if (RIGID_PARSER) {
result = input.error("<manifest> has more than one <application>");
} else {
Slog.w(TAG, "<manifest> has more than one <application>");
@@ -701,7 +707,7 @@
ParseResult<ParsedActivity> activityResult =
ParsedActivityUtils.parseActivityOrReceiver(mSeparateProcesses, pkg,
res,
- parser, flags, PackageParser.sUseRoundIcon, input);
+ parser, flags, sUseRoundIcon, input);
if (activityResult.isSuccess()) {
ParsedActivity activity = activityResult.getResult();
if (isActivity) {
@@ -716,7 +722,7 @@
case "service":
ParseResult<ParsedService> serviceResult = ParsedServiceUtils.parseService(
mSeparateProcesses, pkg, res, parser, flags,
- PackageParser.sUseRoundIcon, input);
+ sUseRoundIcon, input);
if (serviceResult.isSuccess()) {
ParsedService service = serviceResult.getResult();
pkg.addService(service);
@@ -727,7 +733,7 @@
case "provider":
ParseResult<ParsedProvider> providerResult =
ParsedProviderUtils.parseProvider(mSeparateProcesses, pkg, res, parser,
- flags, PackageParser.sUseRoundIcon, input);
+ flags, sUseRoundIcon, input);
if (providerResult.isSuccess()) {
ParsedProvider provider = providerResult.getResult();
pkg.addProvider(provider);
@@ -737,7 +743,7 @@
break;
case "activity-alias":
activityResult = ParsedActivityUtils.parseActivityAlias(pkg, res, parser,
- PackageParser.sUseRoundIcon, input);
+ sUseRoundIcon, input);
if (activityResult.isSuccess()) {
ParsedActivity activity = activityResult.getResult();
pkg.addActivity(activity);
@@ -815,12 +821,12 @@
return sharedUserResult;
}
- pkg.setInstallLocation(anInteger(PackageParser.PARSE_DEFAULT_INSTALL_LOCATION,
+ pkg.setInstallLocation(anInteger(PARSE_DEFAULT_INSTALL_LOCATION,
R.styleable.AndroidManifest_installLocation, sa))
- .setTargetSandboxVersion(anInteger(PackageParser.PARSE_DEFAULT_TARGET_SANDBOX,
+ .setTargetSandboxVersion(anInteger(PARSE_DEFAULT_TARGET_SANDBOX,
R.styleable.AndroidManifest_targetSandboxVersion, sa))
/* Set the global "on SD card" flag */
- .setExternalStorage((flags & PackageParser.PARSE_EXTERNAL_STORAGE) != 0);
+ .setExternalStorage((flags & PARSE_EXTERNAL_STORAGE) != 0);
boolean foundApp = false;
final int depth = parser.getDepth();
@@ -836,9 +842,9 @@
final ParseResult result;
// <application> has special logic, so it's handled outside the general method
- if (PackageParser.TAG_APPLICATION.equals(tagName)) {
+ if (TAG_APPLICATION.equals(tagName)) {
if (foundApp) {
- if (PackageParser.RIGID_PARSER) {
+ if (RIGID_PARSER) {
result = input.error("<manifest> has more than one <application>");
} else {
Slog.w(TAG, "<manifest> has more than one <application>");
@@ -897,51 +903,51 @@
ParsingPackage pkg, Resources res, XmlResourceParser parser, int flags)
throws IOException, XmlPullParserException {
switch (tag) {
- case PackageParser.TAG_OVERLAY:
+ case TAG_OVERLAY:
return parseOverlay(input, pkg, res, parser);
- case PackageParser.TAG_KEY_SETS:
+ case TAG_KEY_SETS:
return parseKeySets(input, pkg, res, parser);
case "feature": // TODO moltmann: Remove
- case PackageParser.TAG_ATTRIBUTION:
+ case TAG_ATTRIBUTION:
return parseAttribution(input, pkg, res, parser);
- case PackageParser.TAG_PERMISSION_GROUP:
+ case TAG_PERMISSION_GROUP:
return parsePermissionGroup(input, pkg, res, parser);
- case PackageParser.TAG_PERMISSION:
+ case TAG_PERMISSION:
return parsePermission(input, pkg, res, parser);
- case PackageParser.TAG_PERMISSION_TREE:
+ case TAG_PERMISSION_TREE:
return parsePermissionTree(input, pkg, res, parser);
- case PackageParser.TAG_USES_PERMISSION:
- case PackageParser.TAG_USES_PERMISSION_SDK_M:
- case PackageParser.TAG_USES_PERMISSION_SDK_23:
+ case TAG_USES_PERMISSION:
+ case TAG_USES_PERMISSION_SDK_M:
+ case TAG_USES_PERMISSION_SDK_23:
return parseUsesPermission(input, pkg, res, parser);
- case PackageParser.TAG_USES_CONFIGURATION:
+ case TAG_USES_CONFIGURATION:
return parseUsesConfiguration(input, pkg, res, parser);
- case PackageParser.TAG_USES_FEATURE:
+ case TAG_USES_FEATURE:
return parseUsesFeature(input, pkg, res, parser);
- case PackageParser.TAG_FEATURE_GROUP:
+ case TAG_FEATURE_GROUP:
return parseFeatureGroup(input, pkg, res, parser);
- case PackageParser.TAG_USES_SDK:
+ case TAG_USES_SDK:
return parseUsesSdk(input, pkg, res, parser);
- case PackageParser.TAG_SUPPORT_SCREENS:
+ case TAG_SUPPORT_SCREENS:
return parseSupportScreens(input, pkg, res, parser);
- case PackageParser.TAG_PROTECTED_BROADCAST:
+ case TAG_PROTECTED_BROADCAST:
return parseProtectedBroadcast(input, pkg, res, parser);
- case PackageParser.TAG_INSTRUMENTATION:
+ case TAG_INSTRUMENTATION:
return parseInstrumentation(input, pkg, res, parser);
- case PackageParser.TAG_ORIGINAL_PACKAGE:
+ case TAG_ORIGINAL_PACKAGE:
return parseOriginalPackage(input, pkg, res, parser);
- case PackageParser.TAG_ADOPT_PERMISSIONS:
+ case TAG_ADOPT_PERMISSIONS:
return parseAdoptPermissions(input, pkg, res, parser);
- case PackageParser.TAG_USES_GL_TEXTURE:
- case PackageParser.TAG_COMPATIBLE_SCREENS:
- case PackageParser.TAG_SUPPORTS_INPUT:
- case PackageParser.TAG_EAT_COMMENT:
+ case TAG_USES_GL_TEXTURE:
+ case TAG_COMPATIBLE_SCREENS:
+ case TAG_SUPPORTS_INPUT:
+ case TAG_EAT_COMMENT:
// Just skip this tag
XmlUtils.skipCurrentTag(parser);
return input.success(pkg);
- case PackageParser.TAG_RESTRICT_UPDATE:
+ case TAG_RESTRICT_UPDATE:
return parseRestrictUpdateHash(flags, input, pkg, res, parser);
- case PackageParser.TAG_QUERIES:
+ case TAG_QUERIES:
return parseQueries(input, pkg, res, parser);
default:
return ParsingUtils.unknownTag("<manifest>", pkg, parser, input);
@@ -1125,7 +1131,7 @@
ParsingPackage pkg, Resources res, XmlResourceParser parser)
throws XmlPullParserException, IOException {
ParseResult<ParsedPermissionGroup> result = ParsedPermissionUtils.parsePermissionGroup(
- pkg, res, parser, PackageParser.sUseRoundIcon, input);
+ pkg, res, parser, sUseRoundIcon, input);
if (result.isError()) {
return input.error(result);
}
@@ -1136,7 +1142,7 @@
ParsingPackage pkg, Resources res, XmlResourceParser parser)
throws XmlPullParserException, IOException {
ParseResult<ParsedPermission> result = ParsedPermissionUtils.parsePermission(
- pkg, res, parser, PackageParser.sUseRoundIcon, input);
+ pkg, res, parser, sUseRoundIcon, input);
if (result.isError()) {
return input.error(result);
}
@@ -1147,7 +1153,7 @@
ParsingPackage pkg, Resources res, XmlResourceParser parser)
throws XmlPullParserException, IOException {
ParseResult<ParsedPermission> result = ParsedPermissionUtils.parsePermissionTree(
- pkg, res, parser, PackageParser.sUseRoundIcon, input);
+ pkg, res, parser, sUseRoundIcon, input);
if (result.isError()) {
return input.error(result);
}
@@ -1405,7 +1411,7 @@
private static ParseResult<ParsingPackage> parseUsesSdk(ParseInput input,
ParsingPackage pkg, Resources res, XmlResourceParser parser)
throws IOException, XmlPullParserException {
- if (PackageParser.SDK_VERSION > 0) {
+ if (SDK_VERSION > 0) {
TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestUsesSdk);
try {
int minVers = 1;
@@ -1440,7 +1446,7 @@
}
ParseResult<Integer> targetSdkVersionResult = computeTargetSdkVersion(
- targetVers, targetCode, PackageParser.SDK_CODENAMES, input);
+ targetVers, targetCode, SDK_CODENAMES, input);
if (targetSdkVersionResult.isError()) {
return input.error(targetSdkVersionResult);
}
@@ -1454,7 +1460,7 @@
}
ParseResult<Integer> minSdkVersionResult = computeMinSdkVersion(minVers, minCode,
- PackageParser.SDK_VERSION, PackageParser.SDK_CODENAMES, input);
+ SDK_VERSION, SDK_CODENAMES, input);
if (minSdkVersionResult.isError()) {
return input.error(minSdkVersionResult);
}
@@ -1637,7 +1643,7 @@
private static ParseResult<ParsingPackage> parseRestrictUpdateHash(int flags, ParseInput input,
ParsingPackage pkg, Resources res, XmlResourceParser parser) {
- if ((flags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0) {
+ if ((flags & PARSE_IS_SYSTEM_DIR) != 0) {
TypedArray sa = res.obtainAttributes(parser, R.styleable.AndroidManifestRestrictUpdate);
try {
final String hash = sa.getNonConfigurationString(
@@ -1846,7 +1852,7 @@
return input.error("Empty class name in package " + pkgName);
}
- if (PackageParser.DEBUG_BACKUP) {
+ if (DEBUG_BACKUP) {
Slog.v(TAG, "android:backupAgent = " + backupAgentName
+ " from " + pkgName + "+" + backupAgent);
}
@@ -1870,7 +1876,7 @@
fullBackupContent = v.resourceId;
if (v.resourceId == 0) {
- if (PackageParser.DEBUG_BACKUP) {
+ if (DEBUG_BACKUP) {
Slog.v(TAG, "fullBackupContent specified as boolean=" +
(v.data == 0 ? "false" : "true"));
}
@@ -1880,7 +1886,7 @@
pkg.setFullBackupContent(fullBackupContent);
}
- if (PackageParser.DEBUG_BACKUP) {
+ if (DEBUG_BACKUP) {
Slog.v(TAG, "fullBackupContent=" + fullBackupContent + " for " + pkgName);
}
}
@@ -1994,7 +2000,7 @@
case "receiver":
ParseResult<ParsedActivity> activityResult =
ParsedActivityUtils.parseActivityOrReceiver(mSeparateProcesses, pkg,
- res, parser, flags, PackageParser.sUseRoundIcon, input);
+ res, parser, flags, sUseRoundIcon, input);
if (activityResult.isSuccess()) {
ParsedActivity activity = activityResult.getResult();
@@ -2012,7 +2018,7 @@
case "service":
ParseResult<ParsedService> serviceResult =
ParsedServiceUtils.parseService(mSeparateProcesses, pkg, res, parser,
- flags, PackageParser.sUseRoundIcon, input);
+ flags, sUseRoundIcon, input);
if (serviceResult.isSuccess()) {
ParsedService service = serviceResult.getResult();
hasServiceOrder |= (service.getOrder() != 0);
@@ -2024,7 +2030,7 @@
case "provider":
ParseResult<ParsedProvider> providerResult =
ParsedProviderUtils.parseProvider(mSeparateProcesses, pkg, res, parser,
- flags, PackageParser.sUseRoundIcon, input);
+ flags, sUseRoundIcon, input);
if (providerResult.isSuccess()) {
pkg.addProvider(providerResult.getResult());
}
@@ -2033,7 +2039,7 @@
break;
case "activity-alias":
activityResult = ParsedActivityUtils.parseActivityAlias(pkg, res,
- parser, PackageParser.sUseRoundIcon, input);
+ parser, sUseRoundIcon, input);
if (activityResult.isSuccess()) {
ParsedActivity activity = activityResult.getResult();
hasActivityOrder |= (activity.getOrder() != 0);
@@ -2505,8 +2511,7 @@
private static void setMaxAspectRatio(ParsingPackage pkg) {
// Default to (1.86) 16.7:9 aspect ratio for pre-O apps and unset for O and greater.
// NOTE: 16.7:9 was the max aspect ratio Android devices can support pre-O per the CDD.
- float maxAspectRatio = pkg.getTargetSdkVersion() < O
- ? PackageParser.DEFAULT_PRE_O_MAX_ASPECT_RATIO : 0;
+ float maxAspectRatio = pkg.getTargetSdkVersion() < O ? DEFAULT_PRE_O_MAX_ASPECT_RATIO : 0;
float packageMaxAspectRatio = pkg.getMaxAspectRatio();
if (packageMaxAspectRatio != 0) {
@@ -2514,10 +2519,8 @@
maxAspectRatio = packageMaxAspectRatio;
} else {
Bundle appMetaData = pkg.getMetaData();
- if (appMetaData != null && appMetaData.containsKey(
- PackageParser.METADATA_MAX_ASPECT_RATIO)) {
- maxAspectRatio = appMetaData.getFloat(PackageParser.METADATA_MAX_ASPECT_RATIO,
- maxAspectRatio);
+ if (appMetaData != null && appMetaData.containsKey(METADATA_MAX_ASPECT_RATIO)) {
+ maxAspectRatio = appMetaData.getFloat(METADATA_MAX_ASPECT_RATIO, maxAspectRatio);
}
}
@@ -2536,8 +2539,7 @@
// process the meta data here since this method is called at the end of processing
// the application and all meta data is guaranteed.
final float activityAspectRatio = activity.getMetaData() != null
- ? activity.getMetaData().getFloat(PackageParser.METADATA_MAX_ASPECT_RATIO,
- maxAspectRatio)
+ ? activity.getMetaData().getFloat(METADATA_MAX_ASPECT_RATIO, maxAspectRatio)
: maxAspectRatio;
activity.setMaxAspectRatio(activity.getResizeMode(), activityAspectRatio);
@@ -2565,7 +2567,7 @@
private void setSupportsSizeChanges(ParsingPackage pkg) {
final Bundle appMetaData = pkg.getMetaData();
final boolean supportsSizeChanges = appMetaData != null
- && appMetaData.getBoolean(PackageParser.METADATA_SUPPORTS_SIZE_CHANGES, false);
+ && appMetaData.getBoolean(METADATA_SUPPORTS_SIZE_CHANGES, false);
List<ParsedActivity> activities = pkg.getActivities();
int activitiesSize = activities.size();
@@ -2573,7 +2575,7 @@
ParsedActivity activity = activities.get(index);
if (supportsSizeChanges || (activity.getMetaData() != null
&& activity.getMetaData().getBoolean(
- PackageParser.METADATA_SUPPORTS_SIZE_CHANGES, false))) {
+ METADATA_SUPPORTS_SIZE_CHANGES, false))) {
activity.setSupportsSizeChanges(true);
}
}
@@ -2674,7 +2676,7 @@
ParsingPackage pkg, Resources res, XmlResourceParser parser)
throws XmlPullParserException, IOException {
ParseResult<ParsedInstrumentation> result = ParsedInstrumentationUtils.parseInstrumentation(
- pkg, res, parser, PackageParser.sUseRoundIcon, input);
+ pkg, res, parser, sUseRoundIcon, input);
if (result.isError()) {
return input.error(result);
}
@@ -2860,7 +2862,7 @@
} else if (v.type == TypedValue.TYPE_FLOAT) {
property = new Property(name, v.getFloat(), packageName, className);
} else {
- if (!PackageParser.RIGID_PARSER) {
+ if (!RIGID_PARSER) {
Slog.w(TAG,
tagName + " only supports string, integer, float, color, "
+ "boolean, and resource reference types: "
diff --git a/core/java/android/content/pm/parsing/component/ComponentParseUtils.java b/core/java/android/content/pm/parsing/component/ComponentParseUtils.java
index d65f8ff..0403a25 100644
--- a/core/java/android/content/pm/parsing/component/ComponentParseUtils.java
+++ b/core/java/android/content/pm/parsing/component/ComponentParseUtils.java
@@ -22,9 +22,9 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Intent;
-import android.content.pm.PackageParser;
import android.content.pm.PackageUserState;
import android.content.pm.parsing.ParsingPackage;
+import android.content.pm.parsing.ParsingPackageUtils;
import android.content.pm.parsing.ParsingUtils;
import android.content.pm.parsing.result.ParseInput;
import android.content.pm.parsing.result.ParseResult;
@@ -77,7 +77,7 @@
@NonNull
public static ParseResult<String> buildProcessName(@NonNull String pkg, String defProc,
CharSequence procSeq, int flags, String[] separateProcesses, ParseInput input) {
- if ((flags & PackageParser.PARSE_IGNORE_PROCESSES) != 0 && !"system".contentEquals(
+ if ((flags & ParsingPackageUtils.PARSE_IGNORE_PROCESSES) != 0 && !"system".contentEquals(
procSeq)) {
return input.success(defProc != null ? defProc : pkg);
}
diff --git a/core/java/android/content/pm/parsing/component/ParsedActivity.java b/core/java/android/content/pm/parsing/component/ParsedActivity.java
index 1915028..2ea24f7 100644
--- a/core/java/android/content/pm/parsing/component/ParsedActivity.java
+++ b/core/java/android/content/pm/parsing/component/ParsedActivity.java
@@ -28,7 +28,6 @@
import android.content.ComponentName;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageParser;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
@@ -127,7 +126,7 @@
activity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
activity.documentLaunchMode = ActivityInfo.DOCUMENT_LAUNCH_NONE;
activity.maxRecents = ActivityTaskManager.getDefaultAppRecentsLimitStatic();
- activity.configChanges = PackageParser.getActivityConfigChanges(0, 0);
+ activity.configChanges = ParsedActivityUtils.getActivityConfigChanges(0, 0);
activity.softInputMode = 0;
activity.persistableMode = ActivityInfo.PERSIST_NEVER;
activity.screenOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
diff --git a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
index f96bd54..f821e08 100644
--- a/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedActivityUtils.java
@@ -23,8 +23,8 @@
import android.app.ActivityTaskManager;
import android.content.Intent;
import android.content.pm.ActivityInfo;
-import android.content.pm.PackageParser;
import android.content.pm.parsing.ParsingPackage;
+import android.content.pm.parsing.ParsingPackageUtils;
import android.content.pm.parsing.ParsingUtils;
import android.content.pm.parsing.result.ParseInput;
import android.content.pm.parsing.result.ParseInput.DeferredError;
@@ -67,6 +67,12 @@
SAFE_BROADCASTS.add(Intent.ACTION_BOOT_COMPLETED);
}
+ /**
+ * Bit mask of all the valid bits that can be set in recreateOnConfigChanges.
+ */
+ private static final int RECREATE_ON_CONFIG_CHANGES_MASK =
+ ActivityInfo.CONFIG_MCC | ActivityInfo.CONFIG_MNC;
+
@NonNull
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public static ParseResult<ParsedActivity> parseActivityOrReceiver(String[] separateProcesses,
@@ -153,7 +159,7 @@
activity.rotationAnimation = sa.getInt(R.styleable.AndroidManifestActivity_rotationAnimation, WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED);
activity.softInputMode = sa.getInt(R.styleable.AndroidManifestActivity_windowSoftInputMode, 0);
- activity.configChanges = PackageParser.getActivityConfigChanges(
+ activity.configChanges = getActivityConfigChanges(
sa.getInt(R.styleable.AndroidManifestActivity_configChanges, 0),
sa.getInt(R.styleable.AndroidManifestActivity_recreateOnConfigChanges, 0));
@@ -345,7 +351,7 @@
if (intent != null) {
activity.order = Math.max(intent.getOrder(), activity.order);
activity.addIntent(intent);
- if (PackageParser.LOG_UNSAFE_BROADCASTS && isReceiver
+ if (LOG_UNSAFE_BROADCASTS && isReceiver
&& pkg.getTargetSdkVersion() >= Build.VERSION_CODES.O) {
int actionCount = intent.countActions();
for (int i = 0; i < actionCount; i++) {
@@ -354,7 +360,7 @@
continue;
}
- if (!PackageParser.SAFE_BROADCASTS.contains(action)) {
+ if (!SAFE_BROADCASTS.contains(action)) {
Slog.w(TAG,
"Broadcast " + action + " may never be delivered to "
+ pkg.getPackageName() + " as requested at: "
@@ -532,7 +538,7 @@
ParsedActivity activity, ParseInput input) {
// There isn't a metadata for us to fall back. Whatever is in layout is correct.
if (activity.metaData == null || !activity.metaData.containsKey(
- PackageParser.METADATA_ACTIVITY_WINDOW_LAYOUT_AFFINITY)) {
+ ParsingPackageUtils.METADATA_ACTIVITY_WINDOW_LAYOUT_AFFINITY)) {
return input.success(activity.windowLayout);
}
@@ -542,7 +548,7 @@
}
String windowLayoutAffinity = activity.metaData.getString(
- PackageParser.METADATA_ACTIVITY_WINDOW_LAYOUT_AFFINITY);
+ ParsingPackageUtils.METADATA_ACTIVITY_WINDOW_LAYOUT_AFFINITY);
ActivityInfo.WindowLayout layout = activity.windowLayout;
if (layout == null) {
layout = new ActivityInfo.WindowLayout(-1 /* width */, -1 /* widthFraction */,
@@ -553,4 +559,14 @@
}
return input.success(layout);
}
+
+ /**
+ * @param configChanges The bit mask of configChanges fetched from AndroidManifest.xml.
+ * @param recreateOnConfigChanges The bit mask recreateOnConfigChanges fetched from
+ * AndroidManifest.xml.
+ * @hide
+ */
+ static int getActivityConfigChanges(int configChanges, int recreateOnConfigChanges) {
+ return configChanges | ((~recreateOnConfigChanges) & RECREATE_ON_CONFIG_CHANGES_MASK);
+ }
}
diff --git a/core/java/android/content/pm/parsing/component/ParsedIntentInfoUtils.java b/core/java/android/content/pm/parsing/component/ParsedIntentInfoUtils.java
index 368dcfd..939e77f 100644
--- a/core/java/android/content/pm/parsing/component/ParsedIntentInfoUtils.java
+++ b/core/java/android/content/pm/parsing/component/ParsedIntentInfoUtils.java
@@ -21,6 +21,7 @@
import android.content.IntentFilter;
import android.content.pm.PackageParser;
import android.content.pm.parsing.ParsingPackage;
+import android.content.pm.parsing.ParsingPackageUtils;
import android.content.pm.parsing.ParsingUtils;
import android.content.pm.parsing.result.ParseInput;
import android.content.pm.parsing.result.ParseResult;
@@ -65,7 +66,7 @@
}
}
- if (PackageParser.sUseRoundIcon) {
+ if (ParsingPackageUtils.sUseRoundIcon) {
intentInfo.icon = sa.getResourceId(
R.styleable.AndroidManifestIntentFilter_roundIcon, 0);
}
@@ -141,7 +142,7 @@
intentInfo.hasDefault = intentInfo.hasCategory(Intent.CATEGORY_DEFAULT);
- if (PackageParser.DEBUG_PARSER) {
+ if (DEBUG) {
final StringBuilder cats = new StringBuilder("Intent d=");
cats.append(intentInfo.isHasDefault());
cats.append(", cat=");
diff --git a/core/java/android/content/pm/split/DefaultSplitAssetLoader.java b/core/java/android/content/pm/split/DefaultSplitAssetLoader.java
index 9e3a8f4..f3caf60 100644
--- a/core/java/android/content/pm/split/DefaultSplitAssetLoader.java
+++ b/core/java/android/content/pm/split/DefaultSplitAssetLoader.java
@@ -18,9 +18,11 @@
import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK;
import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
-import android.content.pm.PackageParser;
import android.content.pm.PackageParser.PackageParserException;
-import android.content.pm.PackageParser.ParseFlags;
+import android.content.pm.parsing.ApkLiteParseUtils;
+import android.content.pm.parsing.PackageLite;
+import android.content.pm.parsing.ParsingPackageUtils;
+import android.content.pm.parsing.ParsingPackageUtils.ParseFlags;
import android.content.res.ApkAssets;
import android.content.res.AssetManager;
import android.os.Build;
@@ -36,20 +38,21 @@
* @hide
*/
public class DefaultSplitAssetLoader implements SplitAssetLoader {
- private final String mBaseCodePath;
- private final String[] mSplitCodePaths;
+ private final String mBaseApkPath;
+ private final String[] mSplitApkPaths;
private final @ParseFlags int mFlags;
private AssetManager mCachedAssetManager;
- public DefaultSplitAssetLoader(PackageParser.PackageLite pkg, @ParseFlags int flags) {
- mBaseCodePath = pkg.baseCodePath;
- mSplitCodePaths = pkg.splitCodePaths;
+ public DefaultSplitAssetLoader(PackageLite pkg, @ParseFlags int flags) {
+ mBaseApkPath = pkg.getBaseApkPath();
+ mSplitApkPaths = pkg.getSplitApkPaths();
mFlags = flags;
}
private static ApkAssets loadApkAssets(String path, @ParseFlags int flags)
throws PackageParserException {
- if ((flags & PackageParser.PARSE_MUST_BE_APK) != 0 && !PackageParser.isApkPath(path)) {
+ if ((flags & ParsingPackageUtils.PARSE_MUST_BE_APK) != 0
+ && !ApkLiteParseUtils.isApkPath(path)) {
throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
"Invalid package file: " + path);
}
@@ -68,16 +71,16 @@
return mCachedAssetManager;
}
- ApkAssets[] apkAssets = new ApkAssets[(mSplitCodePaths != null
- ? mSplitCodePaths.length : 0) + 1];
+ ApkAssets[] apkAssets = new ApkAssets[(mSplitApkPaths != null
+ ? mSplitApkPaths.length : 0) + 1];
// Load the base.
int splitIdx = 0;
- apkAssets[splitIdx++] = loadApkAssets(mBaseCodePath, mFlags);
+ apkAssets[splitIdx++] = loadApkAssets(mBaseApkPath, mFlags);
// Load any splits.
- if (!ArrayUtils.isEmpty(mSplitCodePaths)) {
- for (String apkPath : mSplitCodePaths) {
+ if (!ArrayUtils.isEmpty(mSplitApkPaths)) {
+ for (String apkPath : mSplitApkPaths) {
apkAssets[splitIdx++] = loadApkAssets(apkPath, mFlags);
}
}
diff --git a/core/java/android/content/pm/split/SplitAssetDependencyLoader.java b/core/java/android/content/pm/split/SplitAssetDependencyLoader.java
index 58eaabf..523ca40 100644
--- a/core/java/android/content/pm/split/SplitAssetDependencyLoader.java
+++ b/core/java/android/content/pm/split/SplitAssetDependencyLoader.java
@@ -19,9 +19,11 @@
import android.annotation.NonNull;
import android.content.pm.PackageManager;
-import android.content.pm.PackageParser;
import android.content.pm.PackageParser.PackageParserException;
-import android.content.pm.PackageParser.ParseFlags;
+import android.content.pm.parsing.ApkLiteParseUtils;
+import android.content.pm.parsing.PackageLite;
+import android.content.pm.parsing.ParsingPackageUtils;
+import android.content.pm.parsing.ParsingPackageUtils.ParseFlags;
import android.content.res.ApkAssets;
import android.content.res.AssetManager;
import android.os.Build;
@@ -45,14 +47,14 @@
private final ApkAssets[][] mCachedSplitApks;
private final AssetManager[] mCachedAssetManagers;
- public SplitAssetDependencyLoader(PackageParser.PackageLite pkg,
+ public SplitAssetDependencyLoader(PackageLite pkg,
SparseArray<int[]> dependencies, @ParseFlags int flags) {
super(dependencies);
// The base is inserted into index 0, so we need to shift all the splits by 1.
- mSplitPaths = new String[pkg.splitCodePaths.length + 1];
- mSplitPaths[0] = pkg.baseCodePath;
- System.arraycopy(pkg.splitCodePaths, 0, mSplitPaths, 1, pkg.splitCodePaths.length);
+ mSplitPaths = new String[pkg.getSplitApkPaths().length + 1];
+ mSplitPaths[0] = pkg.getBaseApkPath();
+ System.arraycopy(pkg.getSplitApkPaths(), 0, mSplitPaths, 1, pkg.getSplitApkPaths().length);
mFlags = flags;
mCachedSplitApks = new ApkAssets[mSplitPaths.length][];
@@ -66,7 +68,8 @@
private static ApkAssets loadApkAssets(String path, @ParseFlags int flags)
throws PackageParserException {
- if ((flags & PackageParser.PARSE_MUST_BE_APK) != 0 && !PackageParser.isApkPath(path)) {
+ if ((flags & ParsingPackageUtils.PARSE_MUST_BE_APK) != 0
+ && !ApkLiteParseUtils.isApkPath(path)) {
throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
"Invalid package file: " + path);
}
diff --git a/core/java/android/content/pm/split/SplitDependencyLoader.java b/core/java/android/content/pm/split/SplitDependencyLoader.java
index 3586546..3e68132 100644
--- a/core/java/android/content/pm/split/SplitDependencyLoader.java
+++ b/core/java/android/content/pm/split/SplitDependencyLoader.java
@@ -17,7 +17,7 @@
import android.annotation.IntRange;
import android.annotation.NonNull;
-import android.content.pm.PackageParser;
+import android.content.pm.parsing.PackageLite;
import android.util.IntArray;
import android.util.SparseArray;
@@ -149,10 +149,19 @@
return dst;
}
- public static @NonNull SparseArray<int[]> createDependenciesFromPackage(
- PackageParser.PackageLite pkg) throws IllegalDependencyException {
- // The data structure that holds the dependencies. In PackageParser, splits are stored
- // in their own array, separate from the base. We treat all paths as equals, so
+ /**
+ * Build the split dependency tree by the given package
+ *
+ * @param pkg The package to retrieve the dependency tree
+ * @return The dependency tree of splits
+ * @throws IllegalDependencyException if the requires split is missing, targets split is
+ * missing, it declares itself as configuration split for a non-feature split, or
+ * cycle detected in split dependencies.
+ */
+ public static @NonNull SparseArray<int[]> createDependenciesFromPackage(PackageLite pkg)
+ throws IllegalDependencyException {
+ // The data structure that holds the dependencies. In ParsingPackageUtils, splits are
+ // stored in their own array, separate from the base. We treat all paths as equals, so
// we need to insert the base as index 0, and shift all other splits.
final SparseArray<int[]> splitDependencies = new SparseArray<>();
@@ -161,19 +170,19 @@
// First write out the <uses-split> dependencies. These must appear first in the
// array of ints, as is convention in this class.
- for (int splitIdx = 0; splitIdx < pkg.splitNames.length; splitIdx++) {
- if (!pkg.isFeatureSplits[splitIdx]) {
+ for (int splitIdx = 0; splitIdx < pkg.getSplitNames().length; splitIdx++) {
+ if (!pkg.getIsFeatureSplits()[splitIdx]) {
// Non-feature splits don't have dependencies.
continue;
}
// Implicit dependency on the base.
final int targetIdx;
- final String splitDependency = pkg.usesSplitNames[splitIdx];
+ final String splitDependency = pkg.getUsesSplitNames()[splitIdx];
if (splitDependency != null) {
- final int depIdx = Arrays.binarySearch(pkg.splitNames, splitDependency);
+ final int depIdx = Arrays.binarySearch(pkg.getSplitNames(), splitDependency);
if (depIdx < 0) {
- throw new IllegalDependencyException("Split '" + pkg.splitNames[splitIdx]
+ throw new IllegalDependencyException("Split '" + pkg.getSplitNames()[splitIdx]
+ "' requires split '" + splitDependency + "', which is missing.");
}
targetIdx = depIdx + 1;
@@ -188,26 +197,26 @@
// dependencies and are considered leaves.
//
// At this point, all splits in splitDependencies have the first element in their array set.
- for (int splitIdx = 0; splitIdx < pkg.splitNames.length; splitIdx++) {
- if (pkg.isFeatureSplits[splitIdx]) {
+ for (int splitIdx = 0, size = pkg.getSplitNames().length; splitIdx < size; splitIdx++) {
+ if (pkg.getIsFeatureSplits()[splitIdx]) {
// Feature splits are not configForSplits.
continue;
}
// Implicit feature for the base.
final int targetSplitIdx;
- final String configForSplit = pkg.configForSplit[splitIdx];
+ final String configForSplit = pkg.getConfigForSplit()[splitIdx];
if (configForSplit != null) {
- final int depIdx = Arrays.binarySearch(pkg.splitNames, configForSplit);
+ final int depIdx = Arrays.binarySearch(pkg.getSplitNames(), configForSplit);
if (depIdx < 0) {
- throw new IllegalDependencyException("Split '" + pkg.splitNames[splitIdx]
+ throw new IllegalDependencyException("Split '" + pkg.getSplitNames()[splitIdx]
+ "' targets split '" + configForSplit + "', which is missing.");
}
- if (!pkg.isFeatureSplits[depIdx]) {
- throw new IllegalDependencyException("Split '" + pkg.splitNames[splitIdx]
+ if (!pkg.getIsFeatureSplits()[depIdx]) {
+ throw new IllegalDependencyException("Split '" + pkg.getSplitNames()[splitIdx]
+ "' declares itself as configuration split for a non-feature split '"
- + pkg.splitNames[depIdx] + "'");
+ + pkg.getSplitNames()[depIdx] + "'");
}
targetSplitIdx = depIdx + 1;
} else {
diff --git a/core/java/android/graphics/fonts/FontManager.java b/core/java/android/graphics/fonts/FontManager.java
index ea6cf2f..eca56b3 100644
--- a/core/java/android/graphics/fonts/FontManager.java
+++ b/core/java/android/graphics/fonts/FontManager.java
@@ -16,6 +16,7 @@
package android.graphics.fonts;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
@@ -28,6 +29,8 @@
import com.android.internal.graphics.fonts.IFontManager;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Objects;
/**
@@ -41,6 +44,116 @@
private static final String TAG = "FontManager";
private final @NonNull IFontManager mIFontManager;
+ /** @hide */
+ @IntDef(prefix = "ERROR_CODE_",
+ value = { ERROR_CODE_OK, ERROR_CODE_FAILED_TO_WRITE_FONT_FILE,
+ ERROR_CODE_VERIFICATION_FAILURE, ERROR_CODE_FONT_NAME_MISMATCH,
+ ERROR_CODE_INVALID_FONT_FILE, ERROR_CODE_MISSING_POST_SCRIPT_NAME,
+ ERROR_CODE_DOWNGRADING, ERROR_CODE_FAILED_TO_CREATE_CONFIG_FILE,
+ ERROR_CODE_FONT_UPDATER_DISABLED })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ErrorCode {}
+
+ /**
+ * Indicates an operation has processed successfully.
+ * @hide
+ */
+ public static final int ERROR_CODE_OK = 0;
+
+ /**
+ * Indicates a failure of writing font files.
+ * @hide
+ */
+ public static final int ERROR_CODE_FAILED_TO_WRITE_FONT_FILE = -1;
+
+ /**
+ * Indicates a failure of fs-verity setup.
+ * @hide
+ */
+ public static final int ERROR_CODE_VERIFICATION_FAILURE = -2;
+
+ /**
+ * Indicates a failure of verifying the font name with PostScript name.
+ * @hide
+ */
+ public static final int ERROR_CODE_FONT_NAME_MISMATCH = -3;
+
+ /**
+ * Indicates a failure of placing fonts due to unexpected font contents.
+ * @hide
+ */
+ public static final int ERROR_CODE_INVALID_FONT_FILE = -4;
+
+ /**
+ * Indicates a failure due to missing PostScript name in name table.
+ * @hide
+ */
+ public static final int ERROR_CODE_MISSING_POST_SCRIPT_NAME = -5;
+
+ /**
+ * Indicates a failure of placing fonts due to downgrading.
+ * @hide
+ */
+ public static final int ERROR_CODE_DOWNGRADING = -6;
+
+ /**
+ * Indicates a failure of writing system font configuration XML file.
+ * @hide
+ */
+ public static final int ERROR_CODE_FAILED_TO_CREATE_CONFIG_FILE = -7;
+
+ /**
+ * Indicates a failure due to disabled font updater.
+ * @hide
+ */
+ public static final int ERROR_CODE_FONT_UPDATER_DISABLED = -8;
+
+ /**
+ * Indicates a failure of opening font file.
+ *
+ * This error code is only used with the shell command interaction.
+ *
+ * @hide
+ */
+ public static final int ERROR_CODE_FAILED_TO_OPEN_FONT_FILE = -10001;
+
+ /**
+ * Indicates a failure of opening signature file.
+ *
+ * This error code is only used with the shell command interaction.
+ *
+ * @hide
+ */
+ public static final int ERROR_CODE_FAILED_TO_OPEN_SIGNATURE_FILE = -10002;
+
+ /**
+ * Indicates a failure of invalid shell command arguments.
+ *
+ * This error code is only used with the shell command interaction.
+ *
+ * @hide
+ */
+ public static final int ERROR_CODE_INVALID_SHELL_ARGUMENT = -10003;
+
+ /**
+ * Indicates a failure of reading signature file.
+ *
+ * This error code is only used with the shell command interaction.
+ *
+ * @hide
+ */
+ public static final int ERROR_CODE_INVALID_SIGNATURE_FILE = -10004;
+
+ /**
+ * Indicates a failure due to exceeding allowed signature file size (8kb).
+ *
+ * This error code is only used with the shell command interaction.
+ *
+ * @hide
+ */
+ public static final int ERROR_CODE_SIGNATURE_TOO_LARGE = -10005;
+
+
private FontManager(@NonNull IFontManager iFontManager) {
mIFontManager = iFontManager;
}
diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java
index 5a03ade..95f1d12 100644
--- a/core/java/android/hardware/display/DisplayManagerInternal.java
+++ b/core/java/android/hardware/display/DisplayManagerInternal.java
@@ -465,4 +465,46 @@
public interface DisplayTransactionListener {
void onDisplayTransaction(Transaction t);
}
+
+ /**
+ * Called when there are changes to {@link com.android.server.display.DisplayGroup
+ * DisplayGroups}.
+ */
+ public interface DisplayGroupListener {
+ /**
+ * A new display group with the provided {@code groupId} was added.
+ *
+ * <ol>
+ * <li>The {@code groupId} is applied to all appropriate {@link Display displays}.
+ * <li>This method is called.
+ * <li>{@link android.hardware.display.DisplayManager.DisplayListener DisplayListeners}
+ * are informed of any corresponding changes.
+ * </ol>
+ */
+ void onDisplayGroupAdded(int groupId);
+
+ /**
+ * The display group with the provided {@code groupId} was removed.
+ *
+ * <ol>
+ * <li>All affected {@link Display displays} have their group IDs updated appropriately.
+ * <li>{@link android.hardware.display.DisplayManager.DisplayListener DisplayListeners}
+ * are informed of any corresponding changes.
+ * <li>This method is called.
+ * </ol>
+ */
+ void onDisplayGroupRemoved(int groupId);
+
+ /**
+ * The display group with the provided {@code groupId} has changed.
+ *
+ * <ol>
+ * <li>All affected {@link Display displays} have their group IDs updated appropriately.
+ * <li>{@link android.hardware.display.DisplayManager.DisplayListener DisplayListeners}
+ * are informed of any corresponding changes.
+ * <li>This method is called.
+ * </ol>
+ */
+ void onDisplayGroupChanged(int groupId);
+ }
}
diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java
index e829821..46141e0 100644
--- a/core/java/android/net/Network.java
+++ b/core/java/android/net/Network.java
@@ -22,6 +22,7 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Build;
import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
import android.system.ErrnoException;
import android.system.Os;
@@ -381,7 +382,13 @@
// Query a property of the underlying socket to ensure that the socket's file descriptor
// exists, is available to bind to a network and is not closed.
socket.getReuseAddress();
- bindSocket(socket.getFileDescriptor$());
+ final ParcelFileDescriptor pfd = ParcelFileDescriptor.fromDatagramSocket(socket);
+ bindSocket(pfd.getFileDescriptor());
+ // ParcelFileDescriptor.fromSocket() creates a dup of the original fd. The original and the
+ // dup share the underlying socket in the kernel. The socket is never truly closed until the
+ // last fd pointing to the socket being closed. So close the dup one after binding the
+ // socket to control the lifetime of the dup fd.
+ pfd.close();
}
/**
@@ -393,7 +400,13 @@
// Query a property of the underlying socket to ensure that the socket's file descriptor
// exists, is available to bind to a network and is not closed.
socket.getReuseAddress();
- bindSocket(socket.getFileDescriptor$());
+ final ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(socket);
+ bindSocket(pfd.getFileDescriptor());
+ // ParcelFileDescriptor.fromSocket() creates a dup of the original fd. The original and the
+ // dup share the underlying socket in the kernel. The socket is never truly closed until the
+ // last fd pointing to the socket being closed. So close the dup one after binding the
+ // socket to control the lifetime of the dup fd.
+ pfd.close();
}
/**
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 0a895b9..3843b9a 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -204,6 +204,7 @@
NET_CAPABILITY_TEMPORARILY_NOT_METERED,
NET_CAPABILITY_OEM_PRIVATE,
NET_CAPABILITY_VEHICLE_INTERNAL,
+ NET_CAPABILITY_NOT_VCN_MANAGED,
})
public @interface NetCapability { }
@@ -399,8 +400,16 @@
@SystemApi
public static final int NET_CAPABILITY_VEHICLE_INTERNAL = 27;
+ /**
+ * Indicates that this network is not managed by a Virtual Carrier Network (VCN).
+ *
+ * TODO(b/177299683): Add additional clarifying javadoc.
+ * @hide
+ */
+ public static final int NET_CAPABILITY_NOT_VCN_MANAGED = 28;
+
private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS;
- private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_VEHICLE_INTERNAL;
+ private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_NOT_VCN_MANAGED;
/**
* Network capabilities that are expected to be mutable, i.e., can change while a particular
@@ -417,7 +426,8 @@
| (1 << NET_CAPABILITY_NOT_CONGESTED)
| (1 << NET_CAPABILITY_NOT_SUSPENDED)
| (1 << NET_CAPABILITY_PARTIAL_CONNECTIVITY)
- | (1 << NET_CAPABILITY_TEMPORARILY_NOT_METERED);
+ | (1 << NET_CAPABILITY_TEMPORARILY_NOT_METERED)
+ | (1 << NET_CAPABILITY_NOT_VCN_MANAGED);
/**
* Network capabilities that are not allowed in NetworkRequests. This exists because the
@@ -426,16 +436,21 @@
* can get into a cycle where the NetworkFactory endlessly churns out NetworkAgents that then
* get immediately torn down because they do not have the requested capability.
*/
+ // Note that as a historical exception, the TRUSTED and NOT_VCN_MANAGED capabilities
+ // are mutable but requestable. Factories are responsible for not getting
+ // in an infinite loop about these.
private static final long NON_REQUESTABLE_CAPABILITIES =
- MUTABLE_CAPABILITIES & ~(1 << NET_CAPABILITY_TRUSTED);
+ MUTABLE_CAPABILITIES
+ & ~(1 << NET_CAPABILITY_TRUSTED)
+ & ~(1 << NET_CAPABILITY_NOT_VCN_MANAGED);
/**
* Capabilities that are set by default when the object is constructed.
*/
private static final long DEFAULT_CAPABILITIES =
- (1 << NET_CAPABILITY_NOT_RESTRICTED) |
- (1 << NET_CAPABILITY_TRUSTED) |
- (1 << NET_CAPABILITY_NOT_VPN);
+ (1 << NET_CAPABILITY_NOT_RESTRICTED)
+ | (1 << NET_CAPABILITY_TRUSTED)
+ | (1 << NET_CAPABILITY_NOT_VPN);
/**
* Capabilities that suggest that a network is restricted.
@@ -495,7 +510,8 @@
| (1 << NET_CAPABILITY_NOT_VPN)
| (1 << NET_CAPABILITY_NOT_ROAMING)
| (1 << NET_CAPABILITY_NOT_CONGESTED)
- | (1 << NET_CAPABILITY_NOT_SUSPENDED);
+ | (1 << NET_CAPABILITY_NOT_SUSPENDED)
+ | (1 << NET_CAPABILITY_NOT_VCN_MANAGED);
/**
* Adds the given capability to this {@code NetworkCapability} instance.
@@ -1982,6 +1998,7 @@
case NET_CAPABILITY_TEMPORARILY_NOT_METERED: return "TEMPORARILY_NOT_METERED";
case NET_CAPABILITY_OEM_PRIVATE: return "OEM_PRIVATE";
case NET_CAPABILITY_VEHICLE_INTERNAL: return "NET_CAPABILITY_VEHICLE_INTERNAL";
+ case NET_CAPABILITY_NOT_VCN_MANAGED: return "NOT_VCN_MANAGED";
default: return Integer.toString(capability);
}
}
diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java
index 66b99b9..c4d1b09 100644
--- a/core/java/android/net/NetworkRequest.java
+++ b/core/java/android/net/NetworkRequest.java
@@ -353,7 +353,9 @@
* NetworkSpecifier.
*/
public Builder setNetworkSpecifier(NetworkSpecifier networkSpecifier) {
- MatchAllNetworkSpecifier.checkNotMatchAllNetworkSpecifier(networkSpecifier);
+ if (networkSpecifier instanceof MatchAllNetworkSpecifier) {
+ throw new IllegalArgumentException("A MatchAllNetworkSpecifier is not permitted");
+ }
mNetworkCapabilities.setNetworkSpecifier(networkSpecifier);
return this;
}
diff --git a/core/java/android/net/ProxyInfo.java b/core/java/android/net/ProxyInfo.java
index 950d393..9c9fed1 100644
--- a/core/java/android/net/ProxyInfo.java
+++ b/core/java/android/net/ProxyInfo.java
@@ -355,7 +355,7 @@
port = in.readInt();
}
String exclList = in.readString();
- String[] parsedExclList = in.readStringArray();
+ String[] parsedExclList = in.createStringArray();
ProxyInfo proxyProperties = new ProxyInfo(host, port, exclList, parsedExclList);
return proxyProperties;
}
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index e504946..74df1b2 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -27,6 +27,7 @@
import android.app.Application;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
+import android.sysprop.SocProperties;
import android.sysprop.TelephonyProperties;
import android.text.TextUtils;
import android.util.Slog;
@@ -88,6 +89,14 @@
/** The end-user-visible name for the end product. */
public static final String MODEL = getString("ro.product.model");
+ /** The manufacturer of the device's primary system-on-chip. */
+ @NonNull
+ public static final String SOC_MANUFACTURER = SocProperties.soc_manufacturer().orElse(UNKNOWN);
+
+ /** The model name of the device's primary system-on-chip. */
+ @NonNull
+ public static final String SOC_MODEL = SocProperties.soc_model().orElse(UNKNOWN);
+
/** The system bootloader version number. */
public static final String BOOTLOADER = getString("ro.bootloader");
diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java
index 1c1f5c0..102f525 100644
--- a/core/java/android/os/Bundle.java
+++ b/core/java/android/os/Bundle.java
@@ -45,6 +45,7 @@
@VisibleForTesting
static final int FLAG_ALLOW_FDS = 1 << 10;
+ /** An unmodifiable {@code Bundle} that is always {@link #isEmpty() empty}. */
public static final Bundle EMPTY;
/**
diff --git a/core/java/android/os/PersistableBundle.java b/core/java/android/os/PersistableBundle.java
index 5e3a34d..e5e9b5f 100644
--- a/core/java/android/os/PersistableBundle.java
+++ b/core/java/android/os/PersistableBundle.java
@@ -47,6 +47,8 @@
public final class PersistableBundle extends BaseBundle implements Cloneable, Parcelable,
XmlUtils.WriteMapCallback {
private static final String TAG_PERSISTABLEMAP = "pbundle_as_map";
+
+ /** An unmodifiable {@code PersistableBundle} that is always {@link #isEmpty() empty}. */
public static final PersistableBundle EMPTY;
static {
diff --git a/core/java/android/os/strictmode/UnsafeIntentLaunchViolation.java b/core/java/android/os/strictmode/UnsafeIntentLaunchViolation.java
index 891fb59..f0f3cef 100644
--- a/core/java/android/os/strictmode/UnsafeIntentLaunchViolation.java
+++ b/core/java/android/os/strictmode/UnsafeIntentLaunchViolation.java
@@ -17,10 +17,13 @@
package android.os.strictmode;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.PendingIntent;
import android.content.Intent;
import android.net.Uri;
+import java.util.Objects;
+
/**
* Violation raised when your app launches an {@link Intent} which originated
* from outside your app.
@@ -46,8 +49,20 @@
* not protected, your app is likely vulnerable to malicious apps.
*/
public final class UnsafeIntentLaunchViolation extends Violation {
- /** @hide */
+ private transient Intent mIntent;
+
public UnsafeIntentLaunchViolation(@NonNull Intent intent) {
super("Launch of unsafe intent: " + intent);
+ mIntent = Objects.requireNonNull(intent);
+ }
+
+ /**
+ * Return the {@link Intent} which caused this violation to be raised. Note
+ * that this value is not available if this violation has been serialized
+ * since intents cannot be serialized.
+ */
+ @SuppressWarnings("IntentBuilderName")
+ public @Nullable Intent getIntent() {
+ return mIntent;
}
}
diff --git a/core/java/android/permission/PermissionUsageHelper.java b/core/java/android/permission/PermissionUsageHelper.java
index db55e1c..b1b2925 100644
--- a/core/java/android/permission/PermissionUsageHelper.java
+++ b/core/java/android/permission/PermissionUsageHelper.java
@@ -88,10 +88,8 @@
private static final long DEFAULT_RECENT_TIME_MS = 30000L;
private static boolean shouldShowIndicators() {
- return true;
- // TODO ntmyren: remove true set when device config is configured correctly
- //DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
- //PROPERTY_CAMERA_MIC_ICONS_ENABLED, true);
+ return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+ PROPERTY_CAMERA_MIC_ICONS_ENABLED, true);
}
private static boolean shouldShowLocationIndicator() {
@@ -142,7 +140,7 @@
}
private Context mContext;
- private Map<UserHandle, Context> mUserContexts;
+ private ArrayMap<UserHandle, Context> mUserContexts;
private PackageManager mPkgManager;
private AppOpsManager mAppOpsManager;
@@ -154,7 +152,8 @@
mContext = context;
mPkgManager = context.getPackageManager();
mAppOpsManager = context.getSystemService(AppOpsManager.class);
- mUserContexts = Map.of(Process.myUserHandle(), mContext);
+ mUserContexts = new ArrayMap<>();
+ mUserContexts.put(Process.myUserHandle(), mContext);
}
private Context getUserContext(UserHandle user) {
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index 33e33ee..9bfd75e 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -249,11 +249,13 @@
// Nasty casework for the shadow calllog begins...
// First see if we're just inserting for one user. If so, insert into the shadow
// based on whether that user is unlocked.
- if (user != null) {
- Uri baseUri = userManager.isUserUnlocked(user) ? CALL_COMPOSER_PICTURE_URI
+ UserHandle realUser = UserHandle.CURRENT.equals(user)
+ ? android.os.Process.myUserHandle() : user;
+ if (realUser != null) {
+ Uri baseUri = userManager.isUserUnlocked(realUser) ? CALL_COMPOSER_PICTURE_URI
: SHADOW_CALL_COMPOSER_PICTURE_URI;
Uri pictureInsertionUri = ContentProvider.maybeAddUserId(baseUri,
- user.getIdentifier());
+ realUser.getIdentifier());
Log.i(LOG_TAG, "Inserting call composer for single user at "
+ pictureInsertionUri);
diff --git a/core/java/android/rotationresolver/OWNERS b/core/java/android/rotationresolver/OWNERS
new file mode 100644
index 0000000..81b6f05
--- /dev/null
+++ b/core/java/android/rotationresolver/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/rotationresolver/OWNERS
diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java
index 017f405..f994d29 100644
--- a/core/java/android/security/keymaster/KeymasterDefs.java
+++ b/core/java/android/security/keymaster/KeymasterDefs.java
@@ -177,6 +177,7 @@
public static final int KM_PURPOSE_SIGN = KeyPurpose.SIGN;
public static final int KM_PURPOSE_VERIFY = KeyPurpose.VERIFY;
public static final int KM_PURPOSE_WRAP = KeyPurpose.WRAP_KEY;
+ public static final int KM_PURPOSE_AGREE_KEY = KeyPurpose.AGREE_KEY;
// Key formats.
public static final int KM_KEY_FORMAT_X509 = KeyFormat.X509;
diff --git a/core/java/android/service/rotationresolver/OWNERS b/core/java/android/service/rotationresolver/OWNERS
new file mode 100644
index 0000000..e381d17
--- /dev/null
+++ b/core/java/android/service/rotationresolver/OWNERS
@@ -0,0 +1,10 @@
+# Bug component: 814982
+
+asalo@google.com
+augale@google.com
+bquezada@google.com
+eejiang@google.com
+payamp@google.com
+siddikap@google.com
+svetoslavganov@google.com
+tgadh@google.com
diff --git a/apex/permission/service/java/com/android/permission/util/package-info.java b/core/java/android/speech/IRecognitionServiceManager.aidl
similarity index 68%
rename from apex/permission/service/java/com/android/permission/util/package-info.java
rename to core/java/android/speech/IRecognitionServiceManager.aidl
index 18fada5..7158ba2 100644
--- a/apex/permission/service/java/com/android/permission/util/package-info.java
+++ b/core/java/android/speech/IRecognitionServiceManager.aidl
@@ -14,9 +14,15 @@
* limitations under the License.
*/
+package android.speech;
+
+import android.speech.IRecognitionServiceManagerCallback;
+
/**
- * @hide
- * TODO(b/146466118) remove this javadoc tag
+ * Binder service allowing speech recognition proxied by the system.
+ *
+ * {@hide}
*/
-@android.annotation.Hide
-package com.android.permission.util;
+interface IRecognitionServiceManager {
+ void createSession(in IRecognitionServiceManagerCallback callback);
+}
diff --git a/apex/permission/service/java/com/android/permission/util/package-info.java b/core/java/android/speech/IRecognitionServiceManagerCallback.aidl
similarity index 67%
copy from apex/permission/service/java/com/android/permission/util/package-info.java
copy to core/java/android/speech/IRecognitionServiceManagerCallback.aidl
index 18fada5..d760810 100644
--- a/apex/permission/service/java/com/android/permission/util/package-info.java
+++ b/core/java/android/speech/IRecognitionServiceManagerCallback.aidl
@@ -14,9 +14,16 @@
* limitations under the License.
*/
+package android.speech;
+
+import android.speech.IRecognitionService;
+
/**
- * @hide
- * TODO(b/146466118) remove this javadoc tag
+ * Callback for the service allowing speech recognition proxied by the system.
+ *
+ * {@hide}
*/
-@android.annotation.Hide
-package com.android.permission.util;
+oneway interface IRecognitionServiceManagerCallback {
+ void onSuccess(in IRecognitionService service);
+ void onError();
+}
diff --git a/core/java/android/speech/RecognitionService.java b/core/java/android/speech/RecognitionService.java
index 5fd192a..c97dbfe 100644
--- a/core/java/android/speech/RecognitionService.java
+++ b/core/java/android/speech/RecognitionService.java
@@ -28,6 +28,7 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
+import android.os.Process;
import android.os.RemoteException;
import android.util.Log;
@@ -50,7 +51,9 @@
/**
* Name under which a RecognitionService component publishes information about itself.
* This meta-data should reference an XML resource containing a
- * <code><{@link android.R.styleable#RecognitionService recognition-service}></code> tag.
+ * <code><{@link android.R.styleable#RecognitionService recognition-service}></code> or
+ * <code><{@link android.R.styleable#RecognitionService on-device-recognition-service}
+ * ></code> tag.
*/
public static final String SERVICE_META_DATA = "android.speech";
@@ -182,6 +185,13 @@
private boolean checkPermissions(IRecognitionListener listener, boolean forDataDelivery,
@NonNull String packageName, @Nullable String featureId) {
if (DBG) Log.d(TAG, "checkPermissions");
+
+ final int callingUid = Binder.getCallingUid();
+ if (callingUid == Process.SYSTEM_UID) {
+ // Assuming system has verified permissions of the caller.
+ return true;
+ }
+
if (forDataDelivery) {
if (PermissionChecker.checkCallingOrSelfPermissionForDataDelivery(this,
android.Manifest.permission.RECORD_AUDIO, packageName, featureId,
@@ -342,6 +352,7 @@
* Return the Linux uid assigned to the process that sent you the current transaction that
* is being processed. This is obtained from {@link Binder#getCallingUid()}.
*/
+ // TODO(b/176578753): need to make sure this is fixed when proxied through system.
public int getCallingUid() {
return mCallingUid;
}
diff --git a/core/java/android/speech/SpeechRecognizer.java b/core/java/android/speech/SpeechRecognizer.java
index aea94bf..de879c6 100644
--- a/core/java/android/speech/SpeechRecognizer.java
+++ b/core/java/android/speech/SpeechRecognizer.java
@@ -16,6 +16,7 @@
package android.speech;
+import android.annotation.NonNull;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -27,6 +28,7 @@
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
@@ -38,8 +40,9 @@
/**
* This class provides access to the speech recognition service. This service allows access to the
* speech recognizer. Do not instantiate this class directly, instead, call
- * {@link SpeechRecognizer#createSpeechRecognizer(Context)}. This class's methods must be
- * invoked only from the main application thread.
+ * {@link SpeechRecognizer#createSpeechRecognizer(Context)}, or
+ * {@link SpeechRecognizer#createOnDeviceSpeechRecognizer(Context)}. This class's methods must be
+ * invoked only from the main application thread.
*
* <p>The implementation of this API is likely to stream audio to remote servers to perform speech
* recognition. As such this API is not intended to be used for continuous recognition, which would
@@ -122,8 +125,13 @@
/** Component to direct service intent to */
private final ComponentName mServiceComponent;
+ /** Whether to use on-device speech recognizer. */
+ private final boolean mOnDevice;
+
+ private IRecognitionServiceManager mManagerService;
+
/** Handler that will execute the main tasks */
- private Handler mHandler = new Handler() {
+ private Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
@@ -159,6 +167,17 @@
private SpeechRecognizer(final Context context, final ComponentName serviceComponent) {
mContext = context;
mServiceComponent = serviceComponent;
+ mOnDevice = false;
+ }
+
+ /**
+ * The right way to create a {@code SpeechRecognizer} is by using
+ * {@link #createOnDeviceSpeechRecognizer} static factory method
+ */
+ private SpeechRecognizer(final Context context, boolean onDevice) {
+ mContext = context;
+ mServiceComponent = null;
+ mOnDevice = onDevice;
}
/**
@@ -194,6 +213,7 @@
* @return {@code true} if recognition is available, {@code false} otherwise
*/
public static boolean isRecognitionAvailable(final Context context) {
+ // TODO(b/176578753): make sure this works well with system speech recognizers.
final List<ResolveInfo> list = context.getPackageManager().queryIntentServices(
new Intent(RecognitionService.SERVICE_INTERFACE), 0);
return list != null && list.size() != 0;
@@ -231,13 +251,32 @@
public static SpeechRecognizer createSpeechRecognizer(final Context context,
final ComponentName serviceComponent) {
if (context == null) {
- throw new IllegalArgumentException("Context cannot be null)");
+ throw new IllegalArgumentException("Context cannot be null");
}
checkIsCalledFromMainThread();
return new SpeechRecognizer(context, serviceComponent);
}
/**
+ * Factory method to create a new {@code SpeechRecognizer}.
+ *
+ * <p>Please note that {@link #setRecognitionListener(RecognitionListener)} should be called
+ * before dispatching any command to the created {@code SpeechRecognizer}, otherwise no
+ * notifications will be received.
+ *
+ * @param context in which to create {@code SpeechRecognizer}
+ * @return a new on-device {@code SpeechRecognizer}.
+ */
+ @NonNull
+ public static SpeechRecognizer createOnDeviceSpeechRecognizer(@NonNull final Context context) {
+ if (context == null) {
+ throw new IllegalArgumentException("Context cannot be null");
+ }
+ checkIsCalledFromMainThread();
+ return new SpeechRecognizer(context, /* onDevice */ true);
+ }
+
+ /**
* Sets the listener that will receive all the callbacks. The previous unfinished commands will
* be executed with the old listener, while any following command will be executed with the new
* listener.
@@ -265,36 +304,74 @@
}
checkIsCalledFromMainThread();
if (mConnection == null) { // first time connection
- mConnection = new Connection();
-
- Intent serviceIntent = new Intent(RecognitionService.SERVICE_INTERFACE);
-
- if (mServiceComponent == null) {
- String serviceComponent = Settings.Secure.getString(mContext.getContentResolver(),
- Settings.Secure.VOICE_RECOGNITION_SERVICE);
-
- if (TextUtils.isEmpty(serviceComponent)) {
- Log.e(TAG, "no selected voice recognition service");
- mListener.onError(ERROR_CLIENT);
- return;
- }
-
- serviceIntent.setComponent(ComponentName.unflattenFromString(serviceComponent));
+ // TODO(b/176578753): both flows should go through system service.
+ if (mOnDevice) {
+ connectToSystemService();
} else {
- serviceIntent.setComponent(mServiceComponent);
- }
- if (!mContext.bindService(serviceIntent, mConnection,
- Context.BIND_AUTO_CREATE | Context.BIND_INCLUDE_CAPABILITIES)) {
- Log.e(TAG, "bind to recognition service failed");
- mConnection = null;
- mService = null;
- mListener.onError(ERROR_CLIENT);
- return;
+ connectToService();
}
}
putMessage(Message.obtain(mHandler, MSG_START, recognizerIntent));
}
+ private void connectToSystemService() {
+ mManagerService = IRecognitionServiceManager.Stub.asInterface(
+ ServiceManager.getService(Context.SPEECH_RECOGNITION_SERVICE));
+
+ if (mManagerService == null) {
+ mListener.onError(ERROR_CLIENT);
+ return;
+ }
+
+ try {
+ // TODO(b/176578753): this has to supply information on whether to use on-device impl.
+ mManagerService.createSession(new IRecognitionServiceManagerCallback.Stub(){
+ @Override
+ public void onSuccess(IRecognitionService service) throws RemoteException {
+ mService = service;
+ }
+
+ @Override
+ public void onError() throws RemoteException {
+ Log.e(TAG, "Bind to system recognition service failed");
+ mListener.onError(ERROR_CLIENT);
+ }
+ });
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ private void connectToService() {
+ mConnection = new Connection();
+
+ Intent serviceIntent = new Intent(RecognitionService.SERVICE_INTERFACE);
+
+ if (mServiceComponent == null) {
+ String serviceComponent = Settings.Secure.getString(mContext.getContentResolver(),
+ Settings.Secure.VOICE_RECOGNITION_SERVICE);
+
+ if (TextUtils.isEmpty(serviceComponent)) {
+ Log.e(TAG, "no selected voice recognition service");
+ mListener.onError(ERROR_CLIENT);
+ return;
+ }
+
+ serviceIntent.setComponent(
+ ComponentName.unflattenFromString(serviceComponent));
+ } else {
+ serviceIntent.setComponent(mServiceComponent);
+ }
+ if (!mContext.bindService(serviceIntent, mConnection,
+ Context.BIND_AUTO_CREATE | Context.BIND_INCLUDE_CAPABILITIES)) {
+ Log.e(TAG, "bind to recognition service failed");
+ mConnection = null;
+ mService = null;
+ mListener.onError(ERROR_CLIENT);
+ return;
+ }
+ }
+
/**
* Stops listening for speech. Speech captured so far will be recognized as if the user had
* stopped speaking at this point. Note that in the default case, this does not need to be
@@ -378,7 +455,7 @@
mListener.onError(ERROR_CLIENT);
}
}
-
+
private boolean checkOpenConnection() {
if (mService != null) {
return true;
@@ -433,7 +510,7 @@
private final static int MSG_RMS_CHANGED = 8;
private final static int MSG_ON_EVENT = 9;
- private final Handler mInternalHandler = new Handler() {
+ private final Handler mInternalHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
if (mInternalListener == null) {
diff --git a/core/java/android/text/FontConfig.java b/core/java/android/text/FontConfig.java
index 82d7399..53fe1ba 100644
--- a/core/java/android/text/FontConfig.java
+++ b/core/java/android/text/FontConfig.java
@@ -53,6 +53,8 @@
public final class FontConfig implements Parcelable {
private final @NonNull List<FontFamily> mFamilies;
private final @NonNull List<Alias> mAliases;
+ private final long mLastModifiedDate;
+ private final int mConfigVersion;
/**
* Construct a FontConfig instance.
@@ -62,9 +64,12 @@
*
* @hide Only system server can create this instance and passed via IPC.
*/
- public FontConfig(@NonNull List<FontFamily> families, @NonNull List<Alias> aliases) {
+ public FontConfig(@NonNull List<FontFamily> families, @NonNull List<Alias> aliases,
+ long lastModifiedDate, @IntRange(from = 0) int configVersion) {
mFamilies = families;
mAliases = aliases;
+ mLastModifiedDate = lastModifiedDate;
+ mConfigVersion = configVersion;
}
/**
@@ -88,6 +93,26 @@
}
/**
+ * Returns the last modified date as Java epoch seconds.
+ *
+ * If there is no update, this return 0.
+ * @hide
+ */
+ public long getLastModifiedDate() {
+ return mLastModifiedDate;
+ }
+
+ /**
+ * Returns the monotonically increasing config version value.
+ *
+ * The config version is reset to 0 when the system is restarted.
+ * @hide
+ */
+ public @IntRange(from = 0) int getConfigVersion() {
+ return mConfigVersion;
+ }
+
+ /**
* Returns the ordered list of families included in the system fonts.
* @deprecated Use getFontFamilies instead.
* @hide
@@ -107,6 +132,8 @@
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeParcelableList(mFamilies, flags);
dest.writeParcelableList(mAliases, flags);
+ dest.writeLong(mLastModifiedDate);
+ dest.writeInt(mConfigVersion);
}
public static final @NonNull Creator<FontConfig> CREATOR = new Creator<FontConfig>() {
@@ -116,7 +143,9 @@
FontFamily.class.getClassLoader());
List<Alias> aliases = source.readParcelableList(new ArrayList<>(),
Alias.class.getClassLoader());
- return new FontConfig(families, aliases);
+ long lastModifiedDate = source.readLong();
+ int configVersion = source.readInt();
+ return new FontConfig(families, aliases, lastModifiedDate, configVersion);
}
@Override
diff --git a/core/java/android/util/apk/ApkSignatureVerifier.java b/core/java/android/util/apk/ApkSignatureVerifier.java
index 02edb7e..696271c 100644
--- a/core/java/android/util/apk/ApkSignatureVerifier.java
+++ b/core/java/android/util/apk/ApkSignatureVerifier.java
@@ -27,6 +27,7 @@
import android.content.pm.PackageParser.PackageParserException;
import android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion;
import android.content.pm.Signature;
+import android.content.pm.parsing.ParsingPackageUtils;
import android.os.Build;
import android.os.Trace;
import android.util.jar.StrictJarFile;
@@ -361,7 +362,7 @@
// Gather certs from AndroidManifest.xml, which every APK must have, as an optimization
// to not need to verify the whole APK when verifyFUll == false.
final ZipEntry manifestEntry = jarFile.findEntry(
- PackageParser.ANDROID_MANIFEST_FILENAME);
+ ParsingPackageUtils.ANDROID_MANIFEST_FILENAME);
if (manifestEntry == null) {
throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
"Package " + apkPath + " has no manifest");
@@ -370,7 +371,7 @@
if (ArrayUtils.isEmpty(lastCerts)) {
throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES, "Package "
+ apkPath + " has no certificates at entry "
- + PackageParser.ANDROID_MANIFEST_FILENAME);
+ + ParsingPackageUtils.ANDROID_MANIFEST_FILENAME);
}
lastSigs = convertToSignatures(lastCerts);
@@ -383,7 +384,7 @@
final String entryName = entry.getName();
if (entryName.startsWith("META-INF/")) continue;
- if (entryName.equals(PackageParser.ANDROID_MANIFEST_FILENAME)) continue;
+ if (entryName.equals(ParsingPackageUtils.ANDROID_MANIFEST_FILENAME)) continue;
toVerify.add(entry);
}
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 0e5fb2c..c664ccb 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -121,6 +121,19 @@
public static final int INVALID_DISPLAY = -1;
/**
+ * The default display group id, which is the display group id of the primary display assuming
+ * there is one.
+ * @hide
+ */
+ public static final int DEFAULT_DISPLAY_GROUP = 0;
+
+ /**
+ * Invalid display group id.
+ * @hide
+ */
+ public static final int INVALID_DISPLAY_GROUP = -1;
+
+ /**
* Display flag: Indicates that the display supports compositing content
* that is stored in protected graphics buffers.
* <p>
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index fc42cd0..d200a328 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -66,6 +66,11 @@
public int displayId;
/**
+ * Display Group identifier.
+ */
+ public int displayGroupId;
+
+ /**
* Display address, or null if none.
* Interpretation varies by display type.
*/
@@ -331,6 +336,7 @@
&& flags == other.flags
&& type == other.type
&& displayId == other.displayId
+ && displayGroupId == other.displayGroupId
&& Objects.equals(address, other.address)
&& Objects.equals(deviceProductInfo, other.deviceProductInfo)
&& Objects.equals(uniqueId, other.uniqueId)
@@ -376,6 +382,7 @@
flags = other.flags;
type = other.type;
displayId = other.displayId;
+ displayGroupId = other.displayGroupId;
address = other.address;
deviceProductInfo = other.deviceProductInfo;
name = other.name;
@@ -418,6 +425,7 @@
flags = source.readInt();
type = source.readInt();
displayId = source.readInt();
+ displayGroupId = source.readInt();
address = source.readParcelable(null);
deviceProductInfo = source.readParcelable(null);
name = source.readString8();
@@ -468,6 +476,7 @@
dest.writeInt(this.flags);
dest.writeInt(type);
dest.writeInt(displayId);
+ dest.writeInt(displayGroupId);
dest.writeParcelable(address, flags);
dest.writeParcelable(deviceProductInfo, flags);
dest.writeString8(name);
@@ -547,16 +556,17 @@
* Returns the id of the "default" mode with the given refresh rate, or {@code 0} if no suitable
* mode could be found.
*/
- public int findDefaultModeByRefreshRate(float refreshRate) {
+ @Nullable
+ public Display.Mode findDefaultModeByRefreshRate(float refreshRate) {
Display.Mode[] modes = supportedModes;
Display.Mode defaultMode = getDefaultMode();
for (int i = 0; i < modes.length; i++) {
if (modes[i].matches(
defaultMode.getPhysicalWidth(), defaultMode.getPhysicalHeight(), refreshRate)) {
- return modes[i].getModeId();
+ return modes[i];
}
}
- return 0;
+ return null;
}
/**
@@ -661,6 +671,8 @@
sb.append(name);
sb.append("\", displayId ");
sb.append(displayId);
+ sb.append("\", displayGroupId ");
+ sb.append(displayGroupId);
sb.append(flagsToString(flags));
sb.append(", real ");
sb.append(logicalWidth);
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index d68e903..bf377b0 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -106,9 +106,7 @@
public static final int ITYPE_NAVIGATION_BAR = 1;
public static final int ITYPE_CAPTION_BAR = 2;
- // The always visible types are visible to all windows regardless of the z-order.
- public static final int FIRST_ALWAYS_VISIBLE_TYPE = 3;
- public static final int ITYPE_TOP_GESTURES = FIRST_ALWAYS_VISIBLE_TYPE;
+ public static final int ITYPE_TOP_GESTURES = 3;
public static final int ITYPE_BOTTOM_GESTURES = 4;
public static final int ITYPE_LEFT_GESTURES = 5;
public static final int ITYPE_RIGHT_GESTURES = 6;
@@ -119,16 +117,15 @@
public static final int ITYPE_LEFT_MANDATORY_GESTURES = 9;
public static final int ITYPE_RIGHT_MANDATORY_GESTURES = 10;
- public static final int ITYPE_LEFT_DISPLAY_CUTOUT = 11;
- public static final int ITYPE_TOP_DISPLAY_CUTOUT = 12;
- public static final int ITYPE_RIGHT_DISPLAY_CUTOUT = 13;
- public static final int ITYPE_BOTTOM_DISPLAY_CUTOUT = 14;
- public static final int LAST_ALWAYS_VISIBLE_TYPE = ITYPE_BOTTOM_DISPLAY_CUTOUT;
+ public static final int ITYPE_LEFT_TAPPABLE_ELEMENT = 11;
+ public static final int ITYPE_TOP_TAPPABLE_ELEMENT = 12;
+ public static final int ITYPE_RIGHT_TAPPABLE_ELEMENT = 13;
+ public static final int ITYPE_BOTTOM_TAPPABLE_ELEMENT = 14;
- public static final int ITYPE_LEFT_TAPPABLE_ELEMENT = 15;
- public static final int ITYPE_TOP_TAPPABLE_ELEMENT = 16;
- public static final int ITYPE_RIGHT_TAPPABLE_ELEMENT = 17;
- public static final int ITYPE_BOTTOM_TAPPABLE_ELEMENT = 18;
+ public static final int ITYPE_LEFT_DISPLAY_CUTOUT = 15;
+ public static final int ITYPE_TOP_DISPLAY_CUTOUT = 16;
+ public static final int ITYPE_RIGHT_DISPLAY_CUTOUT = 17;
+ public static final int ITYPE_BOTTOM_DISPLAY_CUTOUT = 18;
/** Input method window. */
public static final int ITYPE_IME = 19;
@@ -185,18 +182,6 @@
}
/**
- * Mirror the always visible sources from the other state. They will share the same object for
- * the always visible types.
- *
- * @param other the state to mirror the mirrored sources from.
- */
- public void mirrorAlwaysVisibleInsetsSources(InsetsState other) {
- for (int type = FIRST_ALWAYS_VISIBLE_TYPE; type <= LAST_ALWAYS_VISIBLE_TYPE; type++) {
- mSources[type] = other.mSources[type];
- }
- }
-
- /**
* Calculates {@link WindowInsets} based on the current source configuration.
*
* @param frame The frame to calculate the insets relative to.
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
index a2777fe..24bc308 100644
--- a/core/java/android/view/Surface.java
+++ b/core/java/android/view/Surface.java
@@ -218,6 +218,15 @@
public static final int FRAME_RATE_COMPATIBILITY_FIXED_SOURCE = 1;
/**
+ * This surface belongs to an app on the High Refresh Rate Deny list, and needs the display
+ * to operate at the exact frame rate.
+ *
+ * This is used internally by the platform and should not be used by apps.
+ * @hide
+ */
+ public static final int FRAME_RATE_COMPATIBILITY_EXACT = 100;
+
+ /**
* Create an empty surface, which will later be filled in by readFromParcel().
* @hide
*/
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 9932b2a..106e392 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -339,8 +339,6 @@
*/
public long mNativeObject;
private long mNativeHandle;
- private boolean mDebugRelease = false;
- private Throwable mReleaseStack = null;
// TODO: Move width/height to native and fix locking through out.
private final Object mLock = new Object();
@@ -588,13 +586,6 @@
}
mNativeObject = nativeObject;
mNativeHandle = mNativeObject != 0 ? nativeGetHandle(nativeObject) : 0;
- if (mNativeObject == 0) {
- if (mDebugRelease) {
- mReleaseStack = new Throwable("assigned zero nativeObject here");
- }
- } else {
- mReleaseStack = null;
- }
}
/**
@@ -605,7 +596,6 @@
mWidth = other.mWidth;
mHeight = other.mHeight;
mLocalOwnerView = other.mLocalOwnerView;
- mDebugRelease = other.mDebugRelease;
assignNativeObject(nativeCopyFromSurfaceControl(other.mNativeObject), callsite);
}
@@ -1435,7 +1425,6 @@
mName = in.readString8();
mWidth = in.readInt();
mHeight = in.readInt();
- mDebugRelease = in.readBoolean();
long object = 0;
if (in.readInt() != 0) {
@@ -1454,12 +1443,8 @@
dest.writeString8(mName);
dest.writeInt(mWidth);
dest.writeInt(mHeight);
- dest.writeBoolean(mDebugRelease);
if (mNativeObject == 0) {
dest.writeInt(0);
- if (mReleaseStack != null) {
- Log.w(TAG, "Sending invalid " + this + " caused by:", mReleaseStack);
- }
} else {
dest.writeInt(1);
}
@@ -1471,13 +1456,6 @@
}
/**
- * @hide
- */
- public void setDebugRelease(boolean debug) {
- mDebugRelease = debug;
- }
-
- /**
* Checks whether two {@link SurfaceControl} objects represent the same surface.
*
* @param other The other object to check
@@ -1547,9 +1525,6 @@
nativeRelease(mNativeObject);
mNativeObject = 0;
mNativeHandle = 0;
- if (mDebugRelease) {
- mReleaseStack = new Throwable("released here");
- }
mCloseGuard.close();
}
}
@@ -1565,11 +1540,8 @@
}
private void checkNotReleased() {
- if (mNativeObject == 0) {
- Log.wtf(TAG, "Invalid " + this + " caused by:", mReleaseStack);
- throw new NullPointerException(
- "mNativeObject of " + this + " is null. Have you called release() already?");
- }
+ if (mNativeObject == 0) throw new NullPointerException(
+ "Invalid " + this + ", mNativeObject is null. Have you called release() already?");
}
/**
@@ -2417,7 +2389,6 @@
public static SurfaceControl mirrorSurface(SurfaceControl mirrorOf) {
long nativeObj = nativeMirrorSurface(mirrorOf.mNativeObject);
SurfaceControl sc = new SurfaceControl();
- sc.mDebugRelease = mirrorOf.mDebugRelease;
sc.assignNativeObject(nativeObj, "mirrorSurface");
return sc;
}
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index 2168dd0..7a5561c 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -240,7 +240,7 @@
/**
* Creates a indexOf(type) -> inset map for which the {@code insets} is just mapped to
- * {@link InsetsType#statusBars()} and {@link InsetsType#navigationBars()}, depending on the
+ * {@link Type#statusBars()} and {@link Type#navigationBars()}, depending on the
* location of the inset.
*/
private static Insets[] createCompatTypeMap(@Nullable Rect insets) {
@@ -321,9 +321,9 @@
/**
* Returns the insets of a specific set of windows causing insets, denoted by the
- * {@code typeMask} bit mask of {@link InsetsType}s.
+ * {@code typeMask} bit mask of {@link Type}s.
*
- * @param typeMask Bit mask of {@link InsetsType}s to query the insets for.
+ * @param typeMask Bit mask of {@link Type}s to query the insets for.
* @return The insets.
*/
@NonNull
@@ -333,7 +333,7 @@
/**
* Returns the insets a specific set of windows can cause, denoted by the
- * {@code typeMask} bit mask of {@link InsetsType}s, regardless of whether that type is
+ * {@code typeMask} bit mask of {@link Type}s, regardless of whether that type is
* currently visible or not.
*
* <p>The insets represents the area of a a window that that <b>may</b> be partially
@@ -342,7 +342,7 @@
* normally shown, but temporarily hidden, the inset returned here will still provide the inset
* associated with the status bar being shown.</p>
*
- * @param typeMask Bit mask of {@link InsetsType}s to query the insets for.
+ * @param typeMask Bit mask of {@link Type}s to query the insets for.
* @return The insets.
*
* @throws IllegalArgumentException If the caller tries to query {@link Type#ime()}. Insets are
@@ -362,7 +362,7 @@
* Returns whether a set of windows that may cause insets is currently visible on screen,
* regardless of whether it actually overlaps with this window.
*
- * @param typeMask Bit mask of {@link Type.InsetsType}s to query visibility status.
+ * @param typeMask Bit mask of {@link Type}s to query visibility status.
* @return {@code true} if and only if all windows included in {@code typeMask} are currently
* visible on screen.
*/
@@ -1148,7 +1148,7 @@
*
* @see #getInsets(int)
*
- * @param typeMask The bitmask of {@link InsetsType} to set the insets for.
+ * @param typeMask The bitmask of {@link Type} to set the insets for.
* @param insets The insets to set.
*
* @return itself
@@ -1172,7 +1172,7 @@
*
* @see #getInsetsIgnoringVisibility(int)
*
- * @param typeMask The bitmask of {@link InsetsType} to set the insets for.
+ * @param typeMask The bitmask of {@link Type} to set the insets for.
* @param insets The insets to set.
*
* @return itself
@@ -1201,7 +1201,7 @@
*
* @see #isVisible(int)
*
- * @param typeMask The bitmask of {@link InsetsType} to set the visibility for.
+ * @param typeMask The bitmask of {@link Type} to set the visibility for.
* @param visible Whether to mark the windows as visible or not.
*
* @return itself
diff --git a/core/java/android/view/WindowInsetsAnimation.java b/core/java/android/view/WindowInsetsAnimation.java
index cf5e7e3..ab5b5ba 100644
--- a/core/java/android/view/WindowInsetsAnimation.java
+++ b/core/java/android/view/WindowInsetsAnimation.java
@@ -61,7 +61,7 @@
}
/**
- * @return The bitmask of {@link WindowInsets.Type.InsetsType}s that are animating.
+ * @return The bitmask of {@link WindowInsets.Type}s that are animating.
*/
@WindowInsets.Type.InsetsType
public int getTypeMask() {
@@ -140,7 +140,7 @@
}
/**
- * Set fraction of the progress if {@link WindowInsets.Type.InsetsType} animation is
+ * Set fraction of the progress if {@link WindowInsets.Type} animation is
* controlled by the app.
* <p>
* Note: This should only be used for testing, as the system fills in the fraction for the
@@ -159,7 +159,7 @@
/**
* Retrieves the translucency of the windows that are animating.
*
- * @return Alpha of windows that cause insets of type {@link WindowInsets.Type.InsetsType}.
+ * @return Alpha of windows that cause insets of type {@link WindowInsets.Type}.
*/
@FloatRange(from = 0f, to = 1f)
public float getAlpha() {
@@ -174,8 +174,7 @@
* {@link WindowInsetsAnimationController#setInsetsAndAlpha(Insets, float, float)} is being
* used.
* </p>
- * @param alpha Alpha of windows that cause insets of type
- * {@link WindowInsets.Type.InsetsType}.
+ * @param alpha Alpha of windows that cause insets of type {@link WindowInsets.Type}.
* @see #getAlpha()
*/
public void setAlpha(@FloatRange(from = 0f, to = 1f) float alpha) {
diff --git a/core/java/android/view/WindowInsetsAnimationControlListener.java b/core/java/android/view/WindowInsetsAnimationControlListener.java
index b61fa36..140a9a8 100644
--- a/core/java/android/view/WindowInsetsAnimationControlListener.java
+++ b/core/java/android/view/WindowInsetsAnimationControlListener.java
@@ -46,8 +46,8 @@
* to redraw because of an {@link EditorInfo} change, or when the window is starting up.
*
* @param controller The controller to control the inset animation.
- * @param types The {@link InsetsType}s it was able to gain control over. Note that this may be
- * different than the types passed into
+ * @param types The {@link WindowInsets.Type}s it was able to gain control over. Note that this
+ * may be different than the types passed into
* {@link WindowInsetsController#controlWindowInsetsAnimation} in case the window
* wasn't able to gain the controls because it wasn't the IME target or not
* currently the window that's controlling the system bars.
diff --git a/core/java/android/view/WindowInsetsAnimationController.java b/core/java/android/view/WindowInsetsAnimationController.java
index 792b974..6578e9b 100644
--- a/core/java/android/view/WindowInsetsAnimationController.java
+++ b/core/java/android/view/WindowInsetsAnimationController.java
@@ -99,7 +99,7 @@
float getCurrentAlpha();
/**
- * @return The {@link InsetsType}s this object is currently controlling.
+ * @return The {@link WindowInsets.Type}s this object is currently controlling.
*/
@InsetsType int getTypes();
diff --git a/core/java/android/view/WindowInsetsController.java b/core/java/android/view/WindowInsetsController.java
index fb9bcbd..991ed55 100644
--- a/core/java/android/view/WindowInsetsController.java
+++ b/core/java/android/view/WindowInsetsController.java
@@ -142,7 +142,7 @@
* change as soon as the window gains control. The app can listen to the event by observing
* {@link View#onApplyWindowInsets} and checking visibility with {@link WindowInsets#isVisible}.
*
- * @param types A bitmask of {@link InsetsType} specifying what windows the app
+ * @param types A bitmask of {@link WindowInsets.Type} specifying what windows the app
* would like to make appear on screen.
*/
void show(@InsetsType int types);
@@ -154,7 +154,7 @@
* change as soon as the window gains control. The app can listen to the event by observing
* {@link View#onApplyWindowInsets} and checking visibility with {@link WindowInsets#isVisible}.
*
- * @param types A bitmask of {@link InsetsType} specifying what windows the app
+ * @param types A bitmask of {@link WindowInsets.Type} specifying what windows the app
* would like to make disappear.
*/
void hide(@InsetsType int types);
@@ -163,7 +163,7 @@
* Lets the application control window inset animations in a frame-by-frame manner by modifying
* the position of the windows in the system causing insets directly.
*
- * @param types The {@link InsetsType}s the application has requested to control.
+ * @param types The {@link WindowInsets.Type}s the application has requested to control.
* @param durationMillis Duration of animation in
* {@link java.util.concurrent.TimeUnit#MILLISECONDS}, or -1 if the
* animation doesn't have a predetermined duration. This value will be
@@ -199,7 +199,7 @@
* setSystemBarsAppearance(0, APPEARANCE_LIGHT_STATUS_BARS)
* </pre>
*
- * @param appearance Bitmask of {@link Appearance} flags.
+ * @param appearance Bitmask of appearance flags.
* @param mask Specifies which flags of appearance should be changed.
* @see #getSystemBarsAppearance
*/
@@ -280,11 +280,11 @@
@NonNull OnControllableInsetsChangedListener listener);
/**
- * Listener to be notified when the set of controllable {@link InsetsType} controlled by a
- * {@link WindowInsetsController} changes.
+ * Listener to be notified when the set of controllable {@link WindowInsets.Type} controlled by
+ * a {@link WindowInsetsController} changes.
* <p>
- * Once a {@link InsetsType} becomes controllable, the app will be able to control the window
- * that is causing this type of insets by calling {@link #controlWindowInsetsAnimation}.
+ * Once a {@link WindowInsets.Type} becomes controllable, the app will be able to control the
+ * window that is causing this type of insets by calling {@link #controlWindowInsetsAnimation}.
* <p>
* Note: When listening to controllability of the {@link Type#ime},
* {@link #controlWindowInsetsAnimation} may still fail in case the {@link InputMethodService}
@@ -297,12 +297,12 @@
interface OnControllableInsetsChangedListener {
/**
- * Called when the set of controllable {@link InsetsType} changes.
+ * Called when the set of controllable {@link WindowInsets.Type} changes.
*
- * @param controller The controller for which the set of controllable {@link InsetsType}s
- * are changing.
- * @param typeMask Bitwise type-mask of the {@link InsetsType}s the controller is currently
- * able to control.
+ * @param controller The controller for which the set of controllable
+ * {@link WindowInsets.Type}s are changing.
+ * @param typeMask Bitwise type-mask of the {@link WindowInsets.Type}s the controller is
+ * currently able to control.
*/
void onControllableInsetsChanged(@NonNull WindowInsetsController controller,
@InsetsType int typeMask);
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 1327f9c..7faa222 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -3326,8 +3326,8 @@
/**
* Specifies types of insets that this window should avoid overlapping during layout.
*
- * @param types which types of insets that this window should avoid. The initial value of
- * this object includes all system bars.
+ * @param types which {@link WindowInsets.Type}s of insets that this window should avoid.
+ * The initial value of this object includes all system bars.
*/
public void setFitInsetsTypes(@InsetsType int types) {
mFitInsetsTypes = types;
@@ -3401,7 +3401,7 @@
}
/**
- * @return the insets types that this window is avoiding overlapping.
+ * @return the {@link WindowInsets.Type}s that this window is avoiding overlapping.
*/
public @InsetsType int getFitInsetsTypes() {
return mFitInsetsTypes;
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 5140c09..90c8e17 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -2307,7 +2307,9 @@
public void removeImeSurface(IBinder windowToken) {
synchronized (mH) {
try {
- mService.removeImeSurfaceFromWindow(windowToken);
+ final Completable.Void value = Completable.createVoid();
+ mService.removeImeSurfaceFromWindow(windowToken, ResultCallbacks.of(value));
+ Completable.getResult(value);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -3239,7 +3241,9 @@
@Deprecated
public void setAdditionalInputMethodSubtypes(String imiId, InputMethodSubtype[] subtypes) {
try {
- mService.setAdditionalInputMethodSubtypes(imiId, subtypes);
+ final Completable.Void value = Completable.createVoid();
+ mService.setAdditionalInputMethodSubtypes(imiId, subtypes, ResultCallbacks.of(value));
+ Completable.getResult(value);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
diff --git a/core/java/android/view/translation/Translator.java b/core/java/android/view/translation/Translator.java
index 675f32b..22c3e57 100644
--- a/core/java/android/view/translation/Translator.java
+++ b/core/java/android/view/translation/Translator.java
@@ -28,6 +28,7 @@
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
+import android.service.translation.ITranslationCallback;
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
@@ -36,9 +37,11 @@
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.List;
import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
/**
* The {@link Translator} for translation, defined by a source and a dest {@link TranslationSpec}.
@@ -295,4 +298,49 @@
}
// TODO: add methods for UI-toolkit case.
+ /** @hide */
+ public void requestUiTranslate(@NonNull List<TranslationRequest> requests,
+ @NonNull Consumer<TranslationResponse> responseCallback) {
+ if (mDirectServiceBinder == null) {
+ Log.wtf(TAG, "Translator created without proper initialization.");
+ return;
+ }
+ final android.service.translation.TranslationRequest request =
+ new android.service.translation.TranslationRequest
+ .Builder(getNextRequestId(), mSourceSpec, mDestSpec, requests)
+ .build();
+ final ITranslationCallback callback =
+ new TranslationResponseCallbackImpl(responseCallback);
+ try {
+ mDirectServiceBinder.onTranslationRequest(request, mId, callback, null);
+ } catch (RemoteException e) {
+ Log.w(TAG, "RemoteException calling flushRequest");
+ }
+ }
+
+ private static class TranslationResponseCallbackImpl extends ITranslationCallback.Stub {
+
+ private final WeakReference<Consumer<TranslationResponse>> mResponseCallback;
+
+ TranslationResponseCallbackImpl(Consumer<TranslationResponse> responseCallback) {
+ mResponseCallback = new WeakReference<>(responseCallback);
+ }
+
+ @Override
+ public void onTranslationComplete(TranslationResponse response) throws RemoteException {
+ provideTranslationResponse(response);
+ }
+
+ @Override
+ public void onError() throws RemoteException {
+ provideTranslationResponse(null);
+ }
+
+ private void provideTranslationResponse(TranslationResponse response) {
+ final Consumer<TranslationResponse> responseCallback = mResponseCallback.get();
+ if (responseCallback != null) {
+ responseCallback.accept(response);
+ }
+ }
+ }
}
diff --git a/core/java/android/view/translation/UiTranslationController.java b/core/java/android/view/translation/UiTranslationController.java
index a810c2e..fa46146 100644
--- a/core/java/android/view/translation/UiTranslationController.java
+++ b/core/java/android/view/translation/UiTranslationController.java
@@ -16,35 +16,274 @@
package android.view.translation;
+import static android.view.translation.UiTranslationManager.STATE_UI_TRANSLATION_FINISHED;
+import static android.view.translation.UiTranslationManager.STATE_UI_TRANSLATION_PAUSED;
+import static android.view.translation.UiTranslationManager.STATE_UI_TRANSLATION_RESUMED;
+import static android.view.translation.UiTranslationManager.STATE_UI_TRANSLATION_STARTED;
+
+import android.annotation.NonNull;
+import android.annotation.WorkerThread;
import android.app.Activity;
import android.content.Context;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Process;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.Pair;
+import android.view.View;
import android.view.autofill.AutofillId;
+import android.view.translation.UiTranslationManager.UiTranslationState;
+import com.android.internal.util.function.pooled.PooledLambda;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
import java.util.List;
+import java.util.function.Consumer;
/**
- * A controller to manage the ui translation requests.
+ * A controller to manage the ui translation requests for the {@link Activity}.
*
* @hide
*/
public class UiTranslationController {
private static final String TAG = "UiTranslationController";
-
+ @NonNull
private final Activity mActivity;
-
+ @NonNull
private final Context mContext;
+ @NonNull
+ private final Object mLock = new Object();
+
+ // Each Translator is distinguished by sourceSpec and desSepc.
+ @NonNull
+ private final ArrayMap<Pair<TranslationSpec, TranslationSpec>, Translator> mTranslators;
+ @NonNull
+ private final ArrayMap<AutofillId, WeakReference<View>> mViews;
+ @NonNull
+ private final HandlerThread mWorkerThread;
+ @NonNull
+ private final Handler mWorkerHandler;
public UiTranslationController(Activity activity, Context context) {
mActivity = activity;
mContext = context;
+ mViews = new ArrayMap<>();
+ mTranslators = new ArrayMap<>();
+
+ mWorkerThread =
+ new HandlerThread("UiTranslationController_" + mActivity.getComponentName(),
+ Process.THREAD_PRIORITY_FOREGROUND);
+ mWorkerThread.start();
+ mWorkerHandler = mWorkerThread.getThreadHandler();
}
/**
* Update the Ui translation state.
*/
- public void updateUiTranslationState(int state, TranslationSpec sourceSpec,
+ public void updateUiTranslationState(@UiTranslationState int state, TranslationSpec sourceSpec,
TranslationSpec destSpec, List<AutofillId> views) {
- // Implement it. Deal with the each states
+ if (!mActivity.isResumed()) {
+ return;
+ }
+ switch (state) {
+ case STATE_UI_TRANSLATION_STARTED:
+ final Pair<TranslationSpec, TranslationSpec> specs =
+ new Pair<>(sourceSpec, destSpec);
+ if (!mTranslators.containsKey(specs)) {
+ mWorkerHandler.sendMessage(PooledLambda.obtainMessage(
+ UiTranslationController::createTranslatorAndStart,
+ UiTranslationController.this, sourceSpec, destSpec, views));
+ } else {
+ onUiTranslationStarted(mTranslators.get(specs), views);
+ }
+ break;
+ case STATE_UI_TRANSLATION_PAUSED:
+ runForEachView((view) -> view.onPauseUiTranslation(), STATE_UI_TRANSLATION_PAUSED);
+ break;
+ case STATE_UI_TRANSLATION_RESUMED:
+ runForEachView((view) -> view.onRestoreUiTranslation(),
+ STATE_UI_TRANSLATION_PAUSED);
+ break;
+ case STATE_UI_TRANSLATION_FINISHED:
+ destroyTranslators();
+ runForEachView((view) -> view.onFinishUiTranslation(), STATE_UI_TRANSLATION_PAUSED);
+ break;
+ default:
+ Log.w(TAG, "onAutoTranslationStateChange(): unknown state: " + state);
+ }
+ }
+
+ /**
+ * Called when the Activity is destroyed.
+ */
+ public void onActivityDestroyed() {
+ synchronized (mLock) {
+ mViews.clear();
+ destroyTranslators();
+ mWorkerThread.quitSafely();
+ }
+ }
+
+ /**
+ * The method is used by {@link Translator}, it will be called when the translation is done. The
+ * translation result can be get from here.
+ */
+ public void onTranslationCompleted(TranslationResponse response) {
+ if (response == null || response.getTranslationStatus()
+ != TranslationResponse.TRANSLATION_STATUS_SUCCESS) {
+ Log.w(TAG, "Fail result from TranslationService, response: " + response);
+ return;
+ }
+ final List<TranslationRequest> translatedResult = response.getTranslations();
+ onTranslationCompleted(translatedResult);
+ }
+
+ private void onTranslationCompleted(List<TranslationRequest> translatedResult) {
+ if (!mActivity.isResumed()) {
+ return;
+ }
+ final int resultCount = translatedResult.size();
+ synchronized (mLock) {
+ for (int i = 0; i < resultCount; i++) {
+ final TranslationRequest request = translatedResult.get(i);
+ final AutofillId autofillId = request.getAutofillId();
+ if (autofillId == null) {
+ continue;
+ }
+ final View view = mViews.get(autofillId).get();
+ if (view == null) {
+ Log.w(TAG, "onTranslationCompleted: the Veiew for autofill id " + autofillId
+ + " may be gone.");
+ continue;
+ }
+ mActivity.runOnUiThread(() -> view.onTranslationComplete(request));
+ }
+ }
+ }
+
+ /**
+ * Called when there is an ui translation request comes to request view translation.
+ */
+ @WorkerThread
+ private void createTranslatorAndStart(TranslationSpec sourceSpec, TranslationSpec destSpec,
+ List<AutofillId> views) {
+ // Create Translator
+ final Translator translator = createTranslatorIfNeeded(sourceSpec, destSpec);
+ if (translator == null) {
+ Log.w(TAG, "Can not create Translator for sourceSpec:" + sourceSpec + " destSpec:"
+ + destSpec);
+ return;
+ }
+ onUiTranslationStarted(translator, views);
+ }
+
+ @WorkerThread
+ private void sendTranslationRequest(Translator translator,
+ ArrayList<TranslationRequest> requests) {
+ translator.requestUiTranslate(requests, this::onTranslationCompleted);
+ }
+
+ /**
+ * Called when there is an ui translation request comes to request view translation.
+ */
+ private void onUiTranslationStarted(Translator translator, List<AutofillId> views) {
+ synchronized (mLock) {
+ if (views == null || views.size() == 0) {
+ throw new IllegalArgumentException("Invalid empty views: " + views);
+ }
+ // Find Views collect the translation data
+ // TODO(b/178084101): try to optimize, e.g. to this in a single traversal
+ final int viewCounts = views.size();
+ final ArrayList<TranslationRequest> requests = new ArrayList<>();
+ for (int i = 0; i < viewCounts; i++) {
+ final AutofillId viewAutofillId = views.get(i);
+ final View view = mActivity.findViewByAutofillIdTraversal(viewAutofillId);
+ if (view == null) {
+ Log.w(TAG, "Can not find the View for autofill id= " + viewAutofillId);
+ continue;
+ }
+ mViews.put(viewAutofillId, new WeakReference<>(view));
+ mActivity.runOnUiThread(() -> {
+ final TranslationRequest translationRequest = view.onCreateTranslationRequest();
+ if (translationRequest != null
+ && translationRequest.getTranslationText().length() > 0) {
+ requests.add(translationRequest);
+ }
+ if (requests.size() == viewCounts) {
+ Log.v(TAG, "onUiTranslationStarted: send " + requests.size() + " request.");
+ mWorkerHandler.sendMessage(PooledLambda.obtainMessage(
+ UiTranslationController::sendTranslationRequest,
+ UiTranslationController.this, translator, requests));
+ }
+ });
+ }
+ }
+ }
+
+ private void runForEachView(Consumer<View> action, @UiTranslationState int state) {
+ synchronized (mLock) {
+ mActivity.runOnUiThread(() -> {
+ final int viewCounts = mViews.size();
+ for (int i = 0; i < viewCounts; i++) {
+ final View view = mViews.valueAt(i).get();
+ if (view == null) {
+ Log.w(TAG, "The View for autofill id " + mViews.keyAt(i)
+ + " may be gone for state " + stateToString(state));
+ continue;
+ }
+ action.accept(view);
+ }
+ if (state == STATE_UI_TRANSLATION_FINISHED) {
+ mViews.clear();
+ }
+ });
+ }
+ }
+
+ private Translator createTranslatorIfNeeded(
+ TranslationSpec sourceSpec, TranslationSpec destSpec) {
+ final TranslationManager tm = mContext.getSystemService(TranslationManager.class);
+ if (tm == null) {
+ Log.e(TAG, "Can not find TranslationManager when trying to create translator.");
+ return null;
+ }
+ final Translator translator = tm.createTranslator(sourceSpec, destSpec);
+ if (translator != null) {
+ final Pair<TranslationSpec, TranslationSpec> specs = new Pair<>(sourceSpec, destSpec);
+ mTranslators.put(specs, translator);
+ }
+ return translator;
+ }
+
+ private void destroyTranslators() {
+ synchronized (mLock) {
+ final int count = mTranslators.size();
+ for (int i = 0; i < count; i++) {
+ Translator translator = mTranslators.valueAt(i);
+ translator.destroy();
+ }
+ mTranslators.clear();
+ }
+ }
+
+ /**
+ * Returns a string representation of the state.
+ */
+ public static String stateToString(@UiTranslationState int state) {
+ switch (state) {
+ case STATE_UI_TRANSLATION_STARTED:
+ return "UI_TRANSLATION_STARTED";
+ case STATE_UI_TRANSLATION_PAUSED:
+ return "UI_TRANSLATION_PAUSED";
+ case STATE_UI_TRANSLATION_RESUMED:
+ return "UI_TRANSLATION_RESUMED";
+ case STATE_UI_TRANSLATION_FINISHED:
+ return "UI_TRANSLATION_FINISHED";
+ default:
+ return "Unknown state (" + state + ")";
+ }
}
}
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 7a30ee2..55f8c40 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -134,7 +134,7 @@
void noteWifiBatchedScanStartedFromSource(in WorkSource ws, int csph);
void noteWifiBatchedScanStoppedFromSource(in WorkSource ws);
void noteWifiRadioPowerState(int powerState, long timestampNs, int uid);
- void noteNetworkInterfaceType(String iface, int type);
+ void noteNetworkInterfaceForTransports(String iface, in int[] transportTypes);
void noteNetworkStatsEnabled();
void noteDeviceIdleMode(int mode, String activeReason, int activeUid);
void setBatteryState(int status, int health, int plugType, int level, int temp, int volt,
diff --git a/core/java/com/android/internal/content/NativeLibraryHelper.java b/core/java/com/android/internal/content/NativeLibraryHelper.java
index bbfd07b..c74c39a 100644
--- a/core/java/com/android/internal/content/NativeLibraryHelper.java
+++ b/core/java/com/android/internal/content/NativeLibraryHelper.java
@@ -27,9 +27,10 @@
import android.content.Context;
import android.content.pm.PackageManager;
-import android.content.pm.PackageParser;
-import android.content.pm.PackageParser.PackageLite;
-import android.content.pm.PackageParser.PackageParserException;
+import android.content.pm.parsing.ApkLiteParseUtils;
+import android.content.pm.parsing.PackageLite;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.pm.parsing.result.ParseTypeImpl;
import android.os.Build;
import android.os.IBinder;
import android.os.SELinux;
@@ -86,17 +87,19 @@
final boolean debuggable;
public static Handle create(File packageFile) throws IOException {
- try {
- final PackageLite lite = PackageParser.parsePackageLite(packageFile, 0);
- return create(lite);
- } catch (PackageParserException e) {
- throw new IOException("Failed to parse package: " + packageFile, e);
+ final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
+ final ParseResult<PackageLite> ret = ApkLiteParseUtils.parsePackageLite(input.reset(),
+ packageFile, /* flags */ 0);
+ if (ret.isError()) {
+ throw new IOException("Failed to parse package: " + packageFile,
+ ret.getException());
}
+ return create(ret.getResult());
}
public static Handle create(PackageLite lite) throws IOException {
- return create(lite.getAllCodePaths(), lite.multiArch, lite.extractNativeLibs,
- lite.debuggable);
+ return create(lite.getAllApkPaths(), lite.isMultiArch(), lite.isExtractNativeLibs(),
+ lite.isDebuggable());
}
public static Handle create(List<String> codePaths, boolean multiArch,
@@ -122,14 +125,14 @@
public static Handle createFd(PackageLite lite, FileDescriptor fd) throws IOException {
final long[] apkHandles = new long[1];
- final String path = lite.baseCodePath;
+ final String path = lite.getBaseApkPath();
apkHandles[0] = nativeOpenApkFd(fd, path);
if (apkHandles[0] == 0) {
throw new IOException("Unable to open APK " + path + " from fd " + fd);
}
- return new Handle(new String[]{path}, apkHandles, lite.multiArch,
- lite.extractNativeLibs, lite.debuggable);
+ return new Handle(new String[]{path}, apkHandles, lite.isMultiArch(),
+ lite.isExtractNativeLibs(), lite.isDebuggable());
}
Handle(String[] apkPaths, long[] apkHandles, boolean multiArch,
diff --git a/core/java/com/android/internal/content/PackageHelper.java b/core/java/com/android/internal/content/PackageHelper.java
index 2b78be3..c2f2052 100644
--- a/core/java/com/android/internal/content/PackageHelper.java
+++ b/core/java/com/android/internal/content/PackageHelper.java
@@ -24,8 +24,8 @@
import android.content.pm.PackageInstaller.SessionParams;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.PackageParser.PackageLite;
import android.content.pm.dex.DexMetadataHelper;
+import android.content.pm.parsing.PackageLite;
import android.os.Environment;
import android.os.IBinder;
import android.os.RemoteException;
@@ -84,8 +84,8 @@
/**
* A group of external dependencies used in
- * {@link #resolveInstallVolume(Context, String, int, long)}. It can be backed by real values
- * from the system or mocked ones for testing purposes.
+ * {@link #resolveInstallVolume(Context, String, int, long, TestableInterface)}.
+ * It can be backed by real values from the system or mocked ones for testing purposes.
*/
public static abstract class TestableInterface {
abstract public StorageManager getStorageManager(Context context);
@@ -447,7 +447,7 @@
long sizeBytes = 0;
// Include raw APKs, and possibly unpacked resources
- for (String codePath : pkg.getAllCodePaths()) {
+ for (String codePath : pkg.getAllApkPaths()) {
final File codeFile = new File(codePath);
sizeBytes += codeFile.length();
}
diff --git a/core/java/com/android/internal/content/om/OverlayScanner.java b/core/java/com/android/internal/content/om/OverlayScanner.java
index a85cf56..6b5cb8d 100644
--- a/core/java/com/android/internal/content/om/OverlayScanner.java
+++ b/core/java/com/android/internal/content/om/OverlayScanner.java
@@ -20,7 +20,10 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.content.pm.PackageParser;
+import android.content.pm.parsing.ApkLite;
+import android.content.pm.parsing.ApkLiteParseUtils;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.pm.parsing.result.ParseTypeImpl;
import android.util.ArrayMap;
import android.util.Log;
@@ -124,15 +127,17 @@
/** Extracts information about the overlay from its manifest. */
@VisibleForTesting
public ParsedOverlayInfo parseOverlayManifest(File overlayApk) {
- try {
- final PackageParser.ApkLite apkLite = PackageParser.parseApkLite(overlayApk, 0);
- return apkLite.targetPackageName == null ? null :
- new ParsedOverlayInfo(apkLite.packageName, apkLite.targetPackageName,
- apkLite.targetSdkVersion, apkLite.overlayIsStatic,
- apkLite.overlayPriority, new File(apkLite.codePath));
- } catch (PackageParser.PackageParserException e) {
- Log.w(TAG, "Got exception loading overlay.", e);
+ final ParseTypeImpl input = ParseTypeImpl.forParsingWithoutPlatformCompat();
+ final ParseResult<ApkLite> ret = ApkLiteParseUtils.parseApkLite(input.reset(),
+ overlayApk, /* flags */ 0);
+ if (ret.isError()) {
+ Log.w(TAG, "Got exception loading overlay.", ret.getException());
return null;
}
+ final ApkLite apkLite = ret.getResult();
+ return apkLite.getTargetPackageName() == null ? null :
+ new ParsedOverlayInfo(apkLite.getPackageName(), apkLite.getTargetPackageName(),
+ apkLite.getTargetSdkVersion(), apkLite.isOverlayIsStatic(),
+ apkLite.getOverlayPriority(), new File(apkLite.getPath()));
}
}
diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java
index 4a24358..fcf8bb4 100644
--- a/core/java/com/android/internal/os/BatteryStatsHelper.java
+++ b/core/java/com/android/internal/os/BatteryStatsHelper.java
@@ -23,7 +23,6 @@
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.hardware.SensorManager;
-import android.net.ConnectivityManager;
import android.os.BatteryStats;
import android.os.BatteryStats.Uid;
import android.os.Build;
@@ -37,11 +36,11 @@
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.telephony.TelephonyManager;
import android.text.format.DateUtils;
import android.util.ArrayMap;
import android.util.Log;
import android.util.SparseArray;
-import android.util.SparseLongArray;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IBatteryStats;
@@ -120,12 +119,11 @@
private double mMaxDrainedPower;
public static boolean checkWifiOnly(Context context) {
- ConnectivityManager cm = (ConnectivityManager) context.getSystemService(
- Context.CONNECTIVITY_SERVICE);
- if (cm == null) {
+ final TelephonyManager tm = context.getSystemService(TelephonyManager.class);
+ if (tm == null) {
return false;
}
- return !cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
+ return !tm.isDataCapable();
}
@UnsupportedAppUsage
@@ -380,6 +378,7 @@
mMaxDrainedPower = (mStats.getHighDischargeAmountSinceCharge()
* mPowerProfile.getBatteryCapacity()) / 100;
+ // Create list of (almost all) sippers, calculate their usage, and put them in mUsageList.
processAppUsage(asUsers);
Collections.sort(mUsageList);
@@ -557,8 +556,7 @@
}
/**
- * Mark the {@link BatterySipper} that we should hide and smear the screen usage based on
- * foreground activity time.
+ * Mark the {@link BatterySipper} that we should hide.
*
* @param sippers sipper list that need to check and remove
* @return the total power of the hidden items of {@link BatterySipper}
@@ -566,7 +564,6 @@
*/
public double removeHiddenBatterySippers(List<BatterySipper> sippers) {
double proportionalSmearPowerMah = 0;
- BatterySipper screenSipper = null;
for (int i = sippers.size() - 1; i >= 0; i--) {
final BatterySipper sipper = sippers.get(i);
sipper.shouldHide = shouldHideSipper(sipper);
@@ -582,45 +579,11 @@
proportionalSmearPowerMah += sipper.totalPowerMah;
}
}
-
- if (sipper.drainType == BatterySipper.DrainType.SCREEN) {
- screenSipper = sipper;
- }
}
-
- smearScreenBatterySipper(sippers, screenSipper);
-
return proportionalSmearPowerMah;
}
/**
- * Smear the screen on power usage among {@code sippers}, based on ratio of foreground activity
- * time.
- */
- public void smearScreenBatterySipper(List<BatterySipper> sippers, BatterySipper screenSipper) {
- long totalActivityTimeMs = 0;
- final SparseLongArray activityTimeArray = new SparseLongArray();
- for (int i = 0, size = sippers.size(); i < size; i++) {
- final BatteryStats.Uid uid = sippers.get(i).uidObj;
- if (uid != null) {
- final long timeMs = getProcessForegroundTimeMs(uid,
- BatteryStats.STATS_SINCE_CHARGED);
- activityTimeArray.put(uid.getUid(), timeMs);
- totalActivityTimeMs += timeMs;
- }
- }
-
- if (screenSipper != null && totalActivityTimeMs >= 10 * DateUtils.MINUTE_IN_MILLIS) {
- final double screenPowerMah = screenSipper.totalPowerMah;
- for (int i = 0, size = sippers.size(); i < size; i++) {
- final BatterySipper sipper = sippers.get(i);
- sipper.screenPowerMah = screenPowerMah * activityTimeArray.get(sipper.getUid(), 0)
- / totalActivityTimeMs;
- }
- }
- }
-
- /**
* Check whether we should hide the battery sipper.
*/
public boolean shouldHideSipper(BatterySipper sipper) {
@@ -683,33 +646,6 @@
}
@VisibleForTesting
- public long getForegroundActivityTotalTimeUs(BatteryStats.Uid uid, long rawRealtimeUs) {
- final BatteryStats.Timer timer = uid.getForegroundActivityTimer();
- if (timer != null) {
- return timer.getTotalTimeLocked(rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED);
- }
-
- return 0;
- }
-
- @VisibleForTesting
- public long getProcessForegroundTimeMs(BatteryStats.Uid uid, int which) {
- final long rawRealTimeUs = convertMsToUs(SystemClock.elapsedRealtime());
- final int foregroundTypes[] = {BatteryStats.Uid.PROCESS_STATE_TOP};
-
- long timeUs = 0;
- for (int type : foregroundTypes) {
- final long localTime = uid.getProcessStateTime(type, rawRealTimeUs, which);
- timeUs += localTime;
- }
-
- // Return the min value of STATE_TOP time and foreground activity time, since both of these
- // time have some errors.
- return convertUsToMs(
- Math.min(timeUs, getForegroundActivityTotalTimeUs(uid, rawRealTimeUs)));
- }
-
- @VisibleForTesting
public void setPackageManager(PackageManager packageManager) {
mPackageManager = packageManager;
}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 37b621d..2b034b0 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -16,6 +16,8 @@
package com.android.internal.os;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.os.BatteryStatsManager.NUM_WIFI_STATES;
import static android.os.BatteryStatsManager.NUM_WIFI_SUPPL_STATES;
@@ -36,7 +38,6 @@
import android.database.ContentObserver;
import android.hardware.usb.UsbManager;
import android.location.GnssSignalQuality;
-import android.net.ConnectivityManager;
import android.net.INetworkStatsService;
import android.net.NetworkStats;
import android.net.Uri;
@@ -111,6 +112,7 @@
import com.android.internal.util.FastPrintWriter;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.XmlUtils;
+import com.android.net.module.util.NetworkCapabilitiesUtils;
import libcore.util.EmptyArray;
@@ -1097,16 +1099,6 @@
private long[] mCpuFreqs;
/**
- * Times spent by the system server process grouped by cluster and CPU speed.
- */
- private LongSamplingCounterArray mSystemServerCpuTimesUs;
-
- /**
- * Times spent by the system server threads grouped by cluster and CPU speed.
- */
- private LongSamplingCounterArray mSystemServerThreadCpuTimesUs;
-
- /**
* Times spent by the system server threads handling incoming binder requests.
*/
private LongSamplingCounterArray mBinderThreadCpuTimesUs;
@@ -6711,11 +6703,12 @@
}
/** @hide */
- public void noteNetworkInterfaceType(String iface, int networkType) {
+ public void noteNetworkInterfaceForTransports(String iface, int[] transportTypes) {
if (TextUtils.isEmpty(iface)) return;
+ final int displayTransport = NetworkCapabilitiesUtils.getDisplayTransport(transportTypes);
synchronized (mModemNetworkLock) {
- if (ConnectivityManager.isNetworkTypeMobile(networkType)) {
+ if (displayTransport == TRANSPORT_CELLULAR) {
mModemIfaces = includeInStringArray(mModemIfaces, iface);
if (DEBUG) Slog.d(TAG, "Note mobile iface " + iface + ": " + mModemIfaces);
} else {
@@ -6725,7 +6718,7 @@
}
synchronized (mWifiNetworkLock) {
- if (ConnectivityManager.isNetworkTypeWifi(networkType)) {
+ if (displayTransport == TRANSPORT_WIFI) {
mWifiIfaces = includeInStringArray(mWifiIfaces, iface);
if (DEBUG) Slog.d(TAG, "Note wifi iface " + iface + ": " + mWifiIfaces);
} else {
@@ -10853,6 +10846,14 @@
}
}
+ /**
+ * Starts tracking CPU time-in-state for threads of the system server process,
+ * keeping a separate account of threads receiving incoming binder calls.
+ */
+ public void startTrackingSystemServerCpuTime() {
+ mSystemServerCpuThreadReader.startTrackingThreadCpuTime();
+ }
+
public void setCallback(BatteryCallback cb) {
mCallback = cb;
}
@@ -11505,8 +11506,6 @@
MeasuredEnergyStats.resetIfNotNull(mGlobalMeasuredEnergyStats);
- resetIfNotNull(mSystemServerCpuTimesUs, false, elapsedRealtimeUs);
- resetIfNotNull(mSystemServerThreadCpuTimesUs, false, elapsedRealtimeUs);
resetIfNotNull(mBinderThreadCpuTimesUs, false, elapsedRealtimeUs);
mLastHistoryStepDetails = null;
@@ -12692,27 +12691,17 @@
return;
}
- if (mSystemServerCpuTimesUs == null) {
- mSystemServerCpuTimesUs = new LongSamplingCounterArray(mOnBatteryTimeBase);
- mSystemServerThreadCpuTimesUs = new LongSamplingCounterArray(mOnBatteryTimeBase);
+ if (mBinderThreadCpuTimesUs == null) {
mBinderThreadCpuTimesUs = new LongSamplingCounterArray(mOnBatteryTimeBase);
}
- mSystemServerCpuTimesUs.addCountLocked(systemServiceCpuThreadTimes.processCpuTimesUs);
- mSystemServerThreadCpuTimesUs.addCountLocked(systemServiceCpuThreadTimes.threadCpuTimesUs);
mBinderThreadCpuTimesUs.addCountLocked(systemServiceCpuThreadTimes.binderThreadCpuTimesUs);
if (DEBUG_BINDER_STATS) {
- Slog.d(TAG, "System server threads per CPU cluster (binder threads/total threads/%)");
- long totalCpuTimeMs = 0;
- long totalThreadTimeMs = 0;
+ Slog.d(TAG, "System server threads per CPU cluster (incoming binder threads)");
long binderThreadTimeMs = 0;
int cpuIndex = 0;
- final long[] systemServerCpuTimesUs =
- mSystemServerCpuTimesUs.getCountsLocked(0);
- final long[] systemServerThreadCpuTimesUs =
- mSystemServerThreadCpuTimesUs.getCountsLocked(0);
- final long[] binderThreadCpuTimesUs =
- mBinderThreadCpuTimesUs.getCountsLocked(0);
+ final long[] binderThreadCpuTimesUs = mBinderThreadCpuTimesUs.getCountsLocked(
+ BatteryStats.STATS_SINCE_CHARGED);
int index = 0;
int numCpuClusters = mPowerProfile.getNumCpuClusters();
for (int cluster = 0; cluster < numCpuClusters; cluster++) {
@@ -12723,28 +12712,15 @@
if (speed != 0) {
sb.append(", ");
}
- long totalCountMs = systemServerThreadCpuTimesUs[index] / 1000;
long binderCountMs = binderThreadCpuTimesUs[index] / 1000;
- sb.append(String.format("%d/%d(%.1f%%)",
- binderCountMs,
- totalCountMs,
- totalCountMs != 0 ? (double) binderCountMs * 100 / totalCountMs : 0));
+ sb.append(TextUtils.formatSimple("%10d", binderCountMs));
- totalCpuTimeMs += systemServerCpuTimesUs[index] / 1000;
- totalThreadTimeMs += totalCountMs;
binderThreadTimeMs += binderCountMs;
index++;
}
cpuIndex += mPowerProfile.getNumCoresInCpuCluster(cluster);
Slog.d(TAG, sb.toString());
}
-
- Slog.d(TAG, "Total system server CPU time (ms): " + totalCpuTimeMs);
- Slog.d(TAG, "Total system server thread time (ms): " + totalThreadTimeMs);
- Slog.d(TAG, String.format("Total Binder thread time (ms): %d (%.1f%%)",
- binderThreadTimeMs,
- binderThreadTimeMs != 0
- ? (double) binderThreadTimeMs * 100 / totalThreadTimeMs : 0));
}
}
@@ -14019,60 +13995,16 @@
}
+ /**
+ * Estimates the time spent by the system server handling incoming binder requests.
+ */
@Override
public long[] getSystemServiceTimeAtCpuSpeeds() {
- // Estimates the time spent by the system server handling incoming binder requests.
- //
- // The data that we can get from the kernel is this:
- // - CPU duration for a (thread - cluster - CPU speed) combination
- // - CPU duration for a (UID - cluster - CPU speed) combination
- //
- // The configuration we have in the Power Profile is this:
- // - Average CPU power for a (cluster - CPU speed) combination.
- //
- // The model used by BatteryStats can be illustrated with this example:
- //
- // - Let's say the system server has 10 threads.
- // - These 10 threads spent 1000 ms of CPU time in aggregate
- // - Of the 10 threads 4 were execute exclusively incoming binder calls.
- // - These 4 "binder" threads consumed 600 ms of CPU time in aggregate
- // - The real time spent by the system server process doing all of this is, say, 200 ms.
- //
- // We will assume that power consumption is proportional to the time spent by the CPU
- // across all threads. This is a crude assumption, but we don't have more detailed data.
- // Thus,
- // binderRealTime = realTime * aggregateBinderThreadTime / aggregateAllThreadTime
- //
- // In our example,
- // binderRealTime = 200 * 600 / 1000 = 120ms
- //
- // We can then multiply this estimated time by the average power to obtain an estimate
- // of the total power consumed by incoming binder calls for the given cluster/speed
- // combination.
-
- if (mSystemServerCpuTimesUs == null) {
+ if (mBinderThreadCpuTimesUs == null) {
return null;
}
- final long[] systemServerCpuTimesUs = mSystemServerCpuTimesUs.getCountsLocked(
- BatteryStats.STATS_SINCE_CHARGED);
- final long [] systemServerThreadCpuTimesUs = mSystemServerThreadCpuTimesUs.getCountsLocked(
- BatteryStats.STATS_SINCE_CHARGED);
- final long[] binderThreadCpuTimesUs = mBinderThreadCpuTimesUs.getCountsLocked(
- BatteryStats.STATS_SINCE_CHARGED);
-
- final int size = systemServerCpuTimesUs.length;
- final long[] results = new long[size];
-
- for (int i = 0; i < size; i++) {
- if (systemServerThreadCpuTimesUs[i] == 0) {
- continue;
- }
-
- results[i] = systemServerCpuTimesUs[i] * binderThreadCpuTimesUs[i]
- / systemServerThreadCpuTimesUs[i];
- }
- return results;
+ return mBinderThreadCpuTimesUs.getCountsLocked(BatteryStats.STATS_SINCE_CHARGED);
}
/**
@@ -14503,7 +14435,7 @@
}
updateSystemServiceCallStats();
- if (mSystemServerThreadCpuTimesUs != null) {
+ if (mBinderThreadCpuTimesUs != null) {
pw.println("Per UID System server binder time in ms:");
long[] systemServiceTimeAtCpuSpeeds = getSystemServiceTimeAtCpuSpeeds();
for (int i = 0; i < size; i++) {
@@ -16094,9 +16026,6 @@
mUidStats.append(uid, u);
}
- mSystemServerCpuTimesUs = LongSamplingCounterArray.readFromParcel(in, mOnBatteryTimeBase);
- mSystemServerThreadCpuTimesUs = LongSamplingCounterArray.readFromParcel(in,
- mOnBatteryTimeBase);
mBinderThreadCpuTimesUs = LongSamplingCounterArray.readFromParcel(in, mOnBatteryTimeBase);
}
@@ -16305,8 +16234,6 @@
} else {
out.writeInt(0);
}
- LongSamplingCounterArray.writeToParcel(out, mSystemServerCpuTimesUs);
- LongSamplingCounterArray.writeToParcel(out, mSystemServerThreadCpuTimesUs);
LongSamplingCounterArray.writeToParcel(out, mBinderThreadCpuTimesUs);
}
diff --git a/core/java/com/android/internal/os/KernelSingleProcessCpuThreadReader.java b/core/java/com/android/internal/os/KernelSingleProcessCpuThreadReader.java
index e6a9623..4d2a08a 100644
--- a/core/java/com/android/internal/os/KernelSingleProcessCpuThreadReader.java
+++ b/core/java/com/android/internal/os/KernelSingleProcessCpuThreadReader.java
@@ -16,23 +16,12 @@
package com.android.internal.os;
-import static android.os.Process.PROC_OUT_LONG;
-import static android.os.Process.PROC_SPACE_TERM;
-
import android.annotation.Nullable;
-import android.os.Process;
-import android.system.Os;
-import android.system.OsConstants;
import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import java.io.IOException;
-import java.nio.file.DirectoryIteratorException;
-import java.nio.file.DirectoryStream;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.Paths;
import java.util.Arrays;
/**
@@ -45,93 +34,65 @@
private static final String TAG = "KernelSingleProcCpuThreadRdr";
private static final boolean DEBUG = false;
- private static final boolean NATIVE_ENABLED = true;
-
- /**
- * The name of the file to read CPU statistics from, must be found in {@code
- * /proc/$PID/task/$TID}
- */
- private static final String CPU_STATISTICS_FILENAME = "time_in_state";
-
- private static final String PROC_STAT_FILENAME = "stat";
-
- /** Directory under /proc/$PID containing CPU stats files for threads */
- public static final String THREAD_CPU_STATS_DIRECTORY = "task";
-
- /** Default mount location of the {@code proc} filesystem */
- private static final Path DEFAULT_PROC_PATH = Paths.get("/proc");
-
- /** The initial {@code time_in_state} file for {@link ProcTimeInStateReader} */
- private static final Path INITIAL_TIME_IN_STATE_PATH = Paths.get("self/time_in_state");
-
- /** See https://man7.org/linux/man-pages/man5/proc.5.html */
- private static final int[] PROCESS_FULL_STATS_FORMAT = new int[] {
- PROC_SPACE_TERM,
- PROC_SPACE_TERM,
- PROC_SPACE_TERM,
- PROC_SPACE_TERM,
- PROC_SPACE_TERM,
- PROC_SPACE_TERM,
- PROC_SPACE_TERM,
- PROC_SPACE_TERM,
- PROC_SPACE_TERM,
- PROC_SPACE_TERM,
- PROC_SPACE_TERM,
- PROC_SPACE_TERM,
- PROC_SPACE_TERM,
- PROC_SPACE_TERM | PROC_OUT_LONG, // 14: utime
- PROC_SPACE_TERM | PROC_OUT_LONG, // 15: stime
- // Ignore remaining fields
- };
-
- private final long[] mProcessFullStatsData = new long[2];
-
- private static final int PROCESS_FULL_STAT_UTIME = 0;
- private static final int PROCESS_FULL_STAT_STIME = 1;
-
- /** Used to read and parse {@code time_in_state} files */
- private final ProcTimeInStateReader mProcTimeInStateReader;
private final int mPid;
- /** Where the proc filesystem is mounted */
- private final Path mProcPath;
+ private final CpuTimeInStateReader mCpuTimeInStateReader;
- // How long a CPU jiffy is in milliseconds.
- private final long mJiffyMillis;
-
- // Path: /proc/<pid>/stat
- private final String mProcessStatFilePath;
-
- // Path: /proc/<pid>/task
- private final Path mThreadsDirectoryPath;
+ private int[] mSelectedThreadNativeTids = new int[0]; // Sorted
/**
- * Count of frequencies read from the {@code time_in_state} file. Read from {@link
- * #mProcTimeInStateReader#getCpuFrequenciesKhz()}.
+ * Count of frequencies read from the {@code time_in_state} file.
*/
private int mFrequencyCount;
+ private boolean mIsTracking;
+
+ /**
+ * A CPU time-in-state provider for testing. Imitates the behavior of the corresponding
+ * methods in frameworks/native/libs/cputimeinstate/cputimeinstate.c
+ */
+ @VisibleForTesting
+ public interface CpuTimeInStateReader {
+ /**
+ * Returns the overall number of cluster-frequency combinations.
+ */
+ int getCpuFrequencyCount();
+
+ /**
+ * Returns true to indicate success.
+ *
+ * Called from native.
+ */
+ boolean startTrackingProcessCpuTimes(int tgid);
+
+ /**
+ * Returns true to indicate success.
+ *
+ * Called from native.
+ */
+ boolean startAggregatingTaskCpuTimes(int pid, int aggregationKey);
+
+ /**
+ * Must return an array of strings formatted like this:
+ * "aggKey:t0_0 t0_1...:t1_0 t1_1..."
+ * Times should be provided in nanoseconds.
+ *
+ * Called from native.
+ */
+ String[] getAggregatedTaskCpuFreqTimes(int pid);
+ }
+
/**
* Create with a path where `proc` is mounted. Used primarily for testing
*
* @param pid PID of the process whose threads are to be read.
- * @param procPath where `proc` is mounted (to find, see {@code mount | grep ^proc})
*/
@VisibleForTesting
- public KernelSingleProcessCpuThreadReader(
- int pid,
- Path procPath) throws IOException {
+ public KernelSingleProcessCpuThreadReader(int pid,
+ @Nullable CpuTimeInStateReader cpuTimeInStateReader) throws IOException {
mPid = pid;
- mProcPath = procPath;
- mProcTimeInStateReader = new ProcTimeInStateReader(
- mProcPath.resolve(INITIAL_TIME_IN_STATE_PATH));
- long jiffyHz = Os.sysconf(OsConstants._SC_CLK_TCK);
- mJiffyMillis = 1000 / jiffyHz;
- mProcessStatFilePath =
- mProcPath.resolve(String.valueOf(mPid)).resolve(PROC_STAT_FILENAME).toString();
- mThreadsDirectoryPath =
- mProcPath.resolve(String.valueOf(mPid)).resolve(THREAD_CPU_STATS_DIRECTORY);
+ mCpuTimeInStateReader = cpuTimeInStateReader;
}
/**
@@ -142,7 +103,7 @@
@Nullable
public static KernelSingleProcessCpuThreadReader create(int pid) {
try {
- return new KernelSingleProcessCpuThreadReader(pid, DEFAULT_PROC_PATH);
+ return new KernelSingleProcessCpuThreadReader(pid, null);
} catch (IOException e) {
Slog.e(TAG, "Failed to initialize KernelSingleProcessCpuThreadReader", e);
return null;
@@ -150,146 +111,98 @@
}
/**
- * Get the CPU frequencies that correspond to the times reported in {@link
- * ProcessCpuUsage#processCpuTimesMillis} etc.
+ * Starts tracking aggregated CPU time-in-state of all threads of the process with the PID
+ * supplied in the constructor.
+ */
+ public void startTrackingThreadCpuTimes() {
+ if (!mIsTracking) {
+ if (!startTrackingProcessCpuTimes(mPid, mCpuTimeInStateReader)) {
+ Slog.e(TAG, "Failed to start tracking process CPU times for " + mPid);
+ }
+ if (mSelectedThreadNativeTids.length > 0) {
+ if (!startAggregatingThreadCpuTimes(mSelectedThreadNativeTids,
+ mCpuTimeInStateReader)) {
+ Slog.e(TAG, "Failed to start tracking aggregated thread CPU times for "
+ + Arrays.toString(mSelectedThreadNativeTids));
+ }
+ }
+ mIsTracking = true;
+ }
+ }
+
+ /**
+ * @param nativeTids an array of native Thread IDs whose CPU times should
+ * be aggregated as a group. This is expected to be a subset
+ * of all thread IDs owned by the process.
+ */
+ public void setSelectedThreadIds(int[] nativeTids) {
+ mSelectedThreadNativeTids = nativeTids.clone();
+ if (mIsTracking) {
+ startAggregatingThreadCpuTimes(mSelectedThreadNativeTids, mCpuTimeInStateReader);
+ }
+ }
+
+ /**
+ * Get the CPU frequencies that correspond to the times reported in {@link ProcessCpuUsage}.
*/
public int getCpuFrequencyCount() {
if (mFrequencyCount == 0) {
- mFrequencyCount = mProcTimeInStateReader.getFrequenciesKhz().length;
+ mFrequencyCount = getCpuFrequencyCount(mCpuTimeInStateReader);
}
return mFrequencyCount;
}
/**
- * Get the total and per-thread CPU usage of the process with the PID specified in the
- * constructor.
- *
- * @param selectedThreadIds a SORTED array of native Thread IDs whose CPU times should
- * be aggregated as a group. This is expected to be a subset
- * of all thread IDs owned by the process.
+ * Get the total CPU usage of the process with the PID specified in the
+ * constructor. The CPU usage time is aggregated across all threads and may
+ * exceed the time the entire process has been running.
*/
@Nullable
- public ProcessCpuUsage getProcessCpuUsage(int[] selectedThreadIds) {
+ public ProcessCpuUsage getProcessCpuUsage() {
if (DEBUG) {
- Slog.d(TAG, "Reading CPU thread usages with directory " + mProcPath + " process ID "
- + mPid);
+ Slog.d(TAG, "Reading CPU thread usages for PID " + mPid);
}
- int cpuFrequencyCount = getCpuFrequencyCount();
- ProcessCpuUsage processCpuUsage = new ProcessCpuUsage(cpuFrequencyCount);
+ ProcessCpuUsage processCpuUsage = new ProcessCpuUsage(getCpuFrequencyCount());
- if (NATIVE_ENABLED) {
- boolean result = readProcessCpuUsage(mProcPath.toString(), mPid,
- selectedThreadIds, processCpuUsage.processCpuTimesMillis,
- processCpuUsage.threadCpuTimesMillis,
- processCpuUsage.selectedThreadCpuTimesMillis);
- if (!result) {
- return null;
- }
- return processCpuUsage;
- }
-
- if (!isSorted(selectedThreadIds)) {
- throw new IllegalArgumentException("selectedThreadIds is not sorted: "
- + Arrays.toString(selectedThreadIds));
- }
-
- if (!Process.readProcFile(mProcessStatFilePath, PROCESS_FULL_STATS_FORMAT, null,
- mProcessFullStatsData, null)) {
- Slog.e(TAG, "Failed to read process stat file " + mProcessStatFilePath);
+ boolean result = readProcessCpuUsage(mPid,
+ processCpuUsage.threadCpuTimesMillis,
+ processCpuUsage.selectedThreadCpuTimesMillis,
+ mCpuTimeInStateReader);
+ if (!result) {
return null;
}
- long utime = mProcessFullStatsData[PROCESS_FULL_STAT_UTIME];
- long stime = mProcessFullStatsData[PROCESS_FULL_STAT_STIME];
-
- long processCpuTimeMillis = (utime + stime) * mJiffyMillis;
-
- try (DirectoryStream<Path> threadPaths = Files.newDirectoryStream(mThreadsDirectoryPath)) {
- for (Path threadDirectory : threadPaths) {
- readThreadCpuUsage(processCpuUsage, selectedThreadIds, threadDirectory);
- }
- } catch (IOException | DirectoryIteratorException e) {
- // Expected when a process finishes
- return null;
- }
-
- // Estimate per cluster per frequency CPU time for the entire process
- // by distributing the total process CPU time proportionately to how much
- // CPU time its threads took on those clusters/frequencies. This algorithm
- // works more accurately when when we have equally distributed concurrency.
- // TODO(b/169279846): obtain actual process CPU times from the kernel
- long totalCpuTimeAllThreads = 0;
- for (int i = cpuFrequencyCount - 1; i >= 0; i--) {
- totalCpuTimeAllThreads += processCpuUsage.threadCpuTimesMillis[i];
- }
-
- for (int i = cpuFrequencyCount - 1; i >= 0; i--) {
- processCpuUsage.processCpuTimesMillis[i] =
- processCpuTimeMillis * processCpuUsage.threadCpuTimesMillis[i]
- / totalCpuTimeAllThreads;
+ if (DEBUG) {
+ Slog.d(TAG, "threadCpuTimesMillis = "
+ + Arrays.toString(processCpuUsage.threadCpuTimesMillis));
+ Slog.d(TAG, "selectedThreadCpuTimesMillis = "
+ + Arrays.toString(processCpuUsage.selectedThreadCpuTimesMillis));
}
return processCpuUsage;
}
- /**
- * Reads a thread's CPU usage and aggregates the per-cluster per-frequency CPU times.
- *
- * @param threadDirectory the {@code /proc} directory of the thread
- */
- private void readThreadCpuUsage(ProcessCpuUsage processCpuUsage, int[] selectedThreadIds,
- Path threadDirectory) {
- // Get the thread ID from the directory name
- final int threadId;
- try {
- final String directoryName = threadDirectory.getFileName().toString();
- threadId = Integer.parseInt(directoryName);
- } catch (NumberFormatException e) {
- Slog.w(TAG, "Failed to parse thread ID when iterating over /proc/*/task", e);
- return;
- }
-
- // Get the CPU statistics from the directory
- final Path threadCpuStatPath = threadDirectory.resolve(CPU_STATISTICS_FILENAME);
- final long[] cpuUsages = mProcTimeInStateReader.getUsageTimesMillis(threadCpuStatPath);
- if (cpuUsages == null) {
- return;
- }
-
- final int cpuFrequencyCount = getCpuFrequencyCount();
- final boolean isSelectedThread = Arrays.binarySearch(selectedThreadIds, threadId) >= 0;
- for (int i = cpuFrequencyCount - 1; i >= 0; i--) {
- processCpuUsage.threadCpuTimesMillis[i] += cpuUsages[i];
- if (isSelectedThread) {
- processCpuUsage.selectedThreadCpuTimesMillis[i] += cpuUsages[i];
- }
- }
- }
-
/** CPU usage of a process, all of its threads and a selected subset of its threads */
public static class ProcessCpuUsage {
- public long[] processCpuTimesMillis;
public long[] threadCpuTimesMillis;
public long[] selectedThreadCpuTimesMillis;
public ProcessCpuUsage(int cpuFrequencyCount) {
- processCpuTimesMillis = new long[cpuFrequencyCount];
threadCpuTimesMillis = new long[cpuFrequencyCount];
selectedThreadCpuTimesMillis = new long[cpuFrequencyCount];
}
}
- private static boolean isSorted(int[] array) {
- for (int i = 0; i < array.length - 1; i++) {
- if (array[i] > array[i + 1]) {
- return false;
- }
- }
- return true;
- }
+ private native int getCpuFrequencyCount(CpuTimeInStateReader reader);
- private native boolean readProcessCpuUsage(String procPath, int pid, int[] selectedThreadIds,
- long[] processCpuTimesMillis, long[] threadCpuTimesMillis,
- long[] selectedThreadCpuTimesMillis);
+ private native boolean startTrackingProcessCpuTimes(int pid, CpuTimeInStateReader reader);
+
+ private native boolean startAggregatingThreadCpuTimes(int[] selectedThreadIds,
+ CpuTimeInStateReader reader);
+
+ private native boolean readProcessCpuUsage(int pid,
+ long[] threadCpuTimesMillis,
+ long[] selectedThreadCpuTimesMillis,
+ CpuTimeInStateReader reader);
}
diff --git a/core/java/com/android/internal/os/ScreenPowerCalculator.java b/core/java/com/android/internal/os/ScreenPowerCalculator.java
index 25f6b4d..9c4a267 100644
--- a/core/java/com/android/internal/os/ScreenPowerCalculator.java
+++ b/core/java/com/android/internal/os/ScreenPowerCalculator.java
@@ -21,9 +21,14 @@
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
import android.os.SystemBatteryConsumer;
+import android.os.SystemClock;
import android.os.UserHandle;
+import android.text.format.DateUtils;
import android.util.Log;
import android.util.SparseArray;
+import android.util.SparseLongArray;
+
+import com.android.internal.annotations.VisibleForTesting;
import java.util.List;
@@ -57,6 +62,7 @@
.setUsageDurationMillis(BatteryConsumer.TIME_COMPONENT_USAGE, durationMs)
.setConsumedPower(BatteryConsumer.POWER_COMPONENT_USAGE, powerMah);
}
+ // TODO(b/178140704): Attribute screen usage similar to smearScreenBatterySipper.
}
/**
@@ -73,6 +79,8 @@
bs.usageTimeMs = durationMs;
bs.sumPower();
sippers.add(bs);
+
+ smearScreenBatterySipper(sippers, bs);
}
}
@@ -96,4 +104,60 @@
}
return power;
}
+
+ /**
+ * Smear the screen on power usage among {@code sippers}, based on ratio of foreground activity
+ * time, and store this in the {@link BatterySipper#screenPowerMah} field.
+ */
+ @VisibleForTesting
+ public void smearScreenBatterySipper(List<BatterySipper> sippers, BatterySipper screenSipper) {
+
+ long totalActivityTimeMs = 0;
+ final SparseLongArray activityTimeArray = new SparseLongArray();
+ for (int i = sippers.size() - 1; i >= 0; i--) {
+ final BatteryStats.Uid uid = sippers.get(i).uidObj;
+ if (uid != null) {
+ final long timeMs = getProcessForegroundTimeMs(uid);
+ activityTimeArray.put(uid.getUid(), timeMs);
+ totalActivityTimeMs += timeMs;
+ }
+ }
+
+ if (screenSipper != null && totalActivityTimeMs >= 10 * DateUtils.MINUTE_IN_MILLIS) {
+ final double screenPowerMah = screenSipper.totalPowerMah;
+ for (int i = sippers.size() - 1; i >= 0; i--) {
+ final BatterySipper sipper = sippers.get(i);
+ sipper.screenPowerMah = screenPowerMah * activityTimeArray.get(sipper.getUid(), 0)
+ / totalActivityTimeMs;
+ }
+ }
+ }
+
+ /** Get the minimum of the uid's ForegroundActivity time and its TOP time. */
+ @VisibleForTesting
+ public long getProcessForegroundTimeMs(BatteryStats.Uid uid) {
+ final long rawRealTimeUs = SystemClock.elapsedRealtime() * 1000;
+ final int[] foregroundTypes = {BatteryStats.Uid.PROCESS_STATE_TOP};
+
+ long timeUs = 0;
+ for (int type : foregroundTypes) {
+ final long localTime = uid.getProcessStateTime(type, rawRealTimeUs,
+ BatteryStats.STATS_SINCE_CHARGED);
+ timeUs += localTime;
+ }
+
+ // Return the min value of STATE_TOP time and foreground activity time, since both of these
+ // time have some errors.
+ return Math.min(timeUs, getForegroundActivityTotalTimeUs(uid, rawRealTimeUs)) / 1000;
+ }
+
+ /** Get the ForegroundActivity time of the given uid. */
+ @VisibleForTesting
+ public long getForegroundActivityTotalTimeUs(BatteryStats.Uid uid, long rawRealtimeUs) {
+ final BatteryStats.Timer timer = uid.getForegroundActivityTimer();
+ if (timer == null) {
+ return 0;
+ }
+ return timer.getTotalTimeLocked(rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED);
+ }
}
diff --git a/core/java/com/android/internal/os/SystemServerCpuThreadReader.java b/core/java/com/android/internal/os/SystemServerCpuThreadReader.java
index fbbee94..fbad75e 100644
--- a/core/java/com/android/internal/os/SystemServerCpuThreadReader.java
+++ b/core/java/com/android/internal/os/SystemServerCpuThreadReader.java
@@ -22,8 +22,6 @@
import com.android.internal.annotations.VisibleForTesting;
import java.io.IOException;
-import java.nio.file.Path;
-import java.util.Arrays;
/**
* Reads /proc/UID/task/TID/time_in_state files to obtain statistics on CPU usage
@@ -31,9 +29,7 @@
*/
public class SystemServerCpuThreadReader {
private final KernelSingleProcessCpuThreadReader mKernelCpuThreadReader;
- private int[] mBinderThreadNativeTids = new int[0]; // Sorted
- private long[] mLastProcessCpuTimeUs;
private long[] mLastThreadCpuTimesUs;
private long[] mLastBinderThreadCpuTimesUs;
@@ -41,8 +37,6 @@
* Times (in microseconds) spent by the system server UID.
*/
public static class SystemServiceCpuThreadTimes {
- // The entire process
- public long[] processCpuTimesUs;
// All threads
public long[] threadCpuTimesUs;
// Just the threads handling incoming binder calls
@@ -61,8 +55,10 @@
}
@VisibleForTesting
- public SystemServerCpuThreadReader(Path procPath, int pid) throws IOException {
- this(new KernelSingleProcessCpuThreadReader(pid, procPath));
+ public SystemServerCpuThreadReader(int pid,
+ KernelSingleProcessCpuThreadReader.CpuTimeInStateReader cpuTimeInStateReader)
+ throws IOException {
+ this(new KernelSingleProcessCpuThreadReader(pid, cpuTimeInStateReader));
}
@VisibleForTesting
@@ -70,9 +66,15 @@
mKernelCpuThreadReader = kernelCpuThreadReader;
}
+ /**
+ * Start tracking CPU time-in-state for the process specified in the constructor.
+ */
+ public void startTrackingThreadCpuTime() {
+ mKernelCpuThreadReader.startTrackingThreadCpuTimes();
+ }
+
public void setBinderThreadNativeTids(int[] nativeTids) {
- mBinderThreadNativeTids = nativeTids.clone();
- Arrays.sort(mBinderThreadNativeTids);
+ mKernelCpuThreadReader.setSelectedThreadIds(nativeTids);
}
/**
@@ -81,33 +83,27 @@
@Nullable
public SystemServiceCpuThreadTimes readDelta() {
final int numCpuFrequencies = mKernelCpuThreadReader.getCpuFrequencyCount();
- if (mLastProcessCpuTimeUs == null) {
- mLastProcessCpuTimeUs = new long[numCpuFrequencies];
+ if (mLastThreadCpuTimesUs == null) {
mLastThreadCpuTimesUs = new long[numCpuFrequencies];
mLastBinderThreadCpuTimesUs = new long[numCpuFrequencies];
- mDeltaCpuThreadTimes.processCpuTimesUs = new long[numCpuFrequencies];
mDeltaCpuThreadTimes.threadCpuTimesUs = new long[numCpuFrequencies];
mDeltaCpuThreadTimes.binderThreadCpuTimesUs = new long[numCpuFrequencies];
}
final KernelSingleProcessCpuThreadReader.ProcessCpuUsage processCpuUsage =
- mKernelCpuThreadReader.getProcessCpuUsage(mBinderThreadNativeTids);
+ mKernelCpuThreadReader.getProcessCpuUsage();
if (processCpuUsage == null) {
return null;
}
for (int i = numCpuFrequencies - 1; i >= 0; i--) {
- long processCpuTimesUs = processCpuUsage.processCpuTimesMillis[i] * 1000;
long threadCpuTimesUs = processCpuUsage.threadCpuTimesMillis[i] * 1000;
long binderThreadCpuTimesUs = processCpuUsage.selectedThreadCpuTimesMillis[i] * 1000;
- mDeltaCpuThreadTimes.processCpuTimesUs[i] =
- Math.max(0, processCpuTimesUs - mLastProcessCpuTimeUs[i]);
mDeltaCpuThreadTimes.threadCpuTimesUs[i] =
Math.max(0, threadCpuTimesUs - mLastThreadCpuTimesUs[i]);
mDeltaCpuThreadTimes.binderThreadCpuTimesUs[i] =
Math.max(0, binderThreadCpuTimesUs - mLastBinderThreadCpuTimesUs[i]);
- mLastProcessCpuTimeUs[i] = processCpuTimesUs;
mLastThreadCpuTimesUs[i] = threadCpuTimesUs;
mLastBinderThreadCpuTimesUs[i] = binderThreadCpuTimesUs;
}
diff --git a/core/java/com/android/internal/util/LatencyTracker.java b/core/java/com/android/internal/util/LatencyTracker.java
index 1e9801f..30cd94c 100644
--- a/core/java/com/android/internal/util/LatencyTracker.java
+++ b/core/java/com/android/internal/util/LatencyTracker.java
@@ -106,7 +106,6 @@
private static LatencyTracker sLatencyTracker;
private final SparseLongArray mStartRtc = new SparseLongArray();
- private final Context mContext;
private volatile int mSamplingInterval;
private volatile boolean mEnabled;
@@ -114,15 +113,14 @@
if (sLatencyTracker == null) {
synchronized (LatencyTracker.class) {
if (sLatencyTracker == null) {
- sLatencyTracker = new LatencyTracker(context);
+ sLatencyTracker = new LatencyTracker();
}
}
}
return sLatencyTracker;
}
- public LatencyTracker(Context context) {
- mContext = context;
+ private LatencyTracker() {
mEnabled = DEFAULT_ENABLED;
mSamplingInterval = DEFAULT_SAMPLING_INTERVAL;
@@ -173,8 +171,8 @@
}
}
- private String getTraceNameOfAcion(int action) {
- return "L<" + getNameOfAction(action) + ">";
+ private static String getTraceNameOfAction(int action) {
+ return "L<" + getNameOfAction(STATSD_ACTION[action]) + ">";
}
public static boolean isEnabled(Context ctx) {
@@ -194,7 +192,7 @@
if (!isEnabled()) {
return;
}
- Trace.asyncTraceBegin(Trace.TRACE_TAG_APP, getTraceNameOfAcion(action), 0);
+ Trace.asyncTraceBegin(Trace.TRACE_TAG_APP, getTraceNameOfAction(action), 0);
mStartRtc.put(action, SystemClock.elapsedRealtime());
}
@@ -213,7 +211,7 @@
return;
}
mStartRtc.delete(action);
- Trace.asyncTraceEnd(Trace.TRACE_TAG_APP, getTraceNameOfAcion(action), 0);
+ Trace.asyncTraceEnd(Trace.TRACE_TAG_APP, getTraceNameOfAction(action), 0);
logAction(action, (int) (endRtc - startRtc));
}
@@ -236,7 +234,7 @@
* @param writeToStatsLog Whether to write the measured latency to FrameworkStatsLog.
*/
public static void logActionDeprecated(int action, int duration, boolean writeToStatsLog) {
- Log.i(TAG, "action=" + action + " latency=" + duration);
+ Log.i(TAG, getNameOfAction(STATSD_ACTION[action]) + " latency=" + duration);
EventLog.writeEvent(EventLogTags.SYSUI_LATENCY, action, duration);
if (writeToStatsLog) {
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index b42404f..892c5a5 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -41,22 +41,24 @@
int untrustedDisplayId);
// TODO: Use ParceledListSlice instead
- void getInputMethodList(int userId, in IInputMethodInfoListResultCallback resultCallback);
- // TODO: Use ParceledListSlice instead
- void getEnabledInputMethodList(int userId,
+ oneway void getInputMethodList(int userId,
in IInputMethodInfoListResultCallback resultCallback);
- void getEnabledInputMethodSubtypeList(in String imiId, boolean allowsImplicitlySelectedSubtypes,
+ // TODO: Use ParceledListSlice instead
+ oneway void getEnabledInputMethodList(int userId,
+ in IInputMethodInfoListResultCallback resultCallback);
+ oneway void getEnabledInputMethodSubtypeList(in String imiId,
+ boolean allowsImplicitlySelectedSubtypes,
in IInputMethodSubtypeListResultCallback resultCallback);
- void getLastInputMethodSubtype(in IInputMethodSubtypeResultCallback resultCallback);
+ oneway void getLastInputMethodSubtype(in IInputMethodSubtypeResultCallback resultCallback);
- void showSoftInput(in IInputMethodClient client, IBinder windowToken, int flags,
+ oneway void showSoftInput(in IInputMethodClient client, IBinder windowToken, int flags,
in ResultReceiver resultReceiver, in IBooleanResultCallback resultCallback);
- void hideSoftInput(in IInputMethodClient client, IBinder windowToken, int flags,
+ oneway void hideSoftInput(in IInputMethodClient client, IBinder windowToken, int flags,
in ResultReceiver resultReceiver, in IBooleanResultCallback resultCallback);
// If windowToken is null, this just does startInput(). Otherwise this reports that a window
// has gained focus, and if 'attribute' is non-null then also does startInput.
// @NonNull
- void startInputOrWindowGainedFocus(
+ oneway void startInputOrWindowGainedFocus(
/* @StartInputReason */ int startInputReason,
in IInputMethodClient client, in IBinder windowToken,
/* @StartInputFlags */ int startInputFlags,
@@ -66,29 +68,31 @@
int unverifiedTargetSdkVersion,
in IInputBindResultResultCallback inputBindResult);
- void showInputMethodPickerFromClient(in IInputMethodClient client,
+ oneway void showInputMethodPickerFromClient(in IInputMethodClient client,
int auxiliarySubtypeMode, in IVoidResultCallback resultCallback);
- void showInputMethodPickerFromSystem(in IInputMethodClient client, int auxiliarySubtypeMode,
- int displayId, in IVoidResultCallback resultCallback);
- void showInputMethodAndSubtypeEnablerFromClient(in IInputMethodClient client, String topId,
+ oneway void showInputMethodPickerFromSystem(in IInputMethodClient client,
+ int auxiliarySubtypeMode, int displayId, in IVoidResultCallback resultCallback);
+ oneway void showInputMethodAndSubtypeEnablerFromClient(in IInputMethodClient client,
+ String topId, in IVoidResultCallback resultCallback);
+ oneway void isInputMethodPickerShownForTest(in IBooleanResultCallback resultCallback);
+ oneway void getCurrentInputMethodSubtype(in IInputMethodSubtypeResultCallback resultCallback);
+ oneway void setAdditionalInputMethodSubtypes(String id, in InputMethodSubtype[] subtypes,
in IVoidResultCallback resultCallback);
- void isInputMethodPickerShownForTest(in IBooleanResultCallback resultCallback);
- void getCurrentInputMethodSubtype(in IInputMethodSubtypeResultCallback resultCallback);
- void setAdditionalInputMethodSubtypes(String id, in InputMethodSubtype[] subtypes);
// This is kept due to @UnsupportedAppUsage.
// TODO(Bug 113914148): Consider removing this.
- void getInputMethodWindowVisibleHeight(IIntResultCallback resultCallback);
+ oneway void getInputMethodWindowVisibleHeight(IIntResultCallback resultCallback);
- void reportActivityView(in IInputMethodClient parentClient, int childDisplayId,
+ oneway void reportActivityView(in IInputMethodClient parentClient, int childDisplayId,
in float[] matrixValues, in IVoidResultCallback resultCallback);
oneway void reportPerceptible(in IBinder windowToken, boolean perceptible);
/** Remove the IME surface. Requires INTERNAL_SYSTEM_WINDOW permission. */
- void removeImeSurface();
+ oneway void removeImeSurface(in IVoidResultCallback resultCallback);
/** Remove the IME surface. Requires passing the currently focused window. */
- void removeImeSurfaceFromWindow(in IBinder windowToken);
+ oneway void removeImeSurfaceFromWindow(in IBinder windowToken,
+ in IVoidResultCallback resultCallback);
void startProtoDump(in byte[] protoDump, int source, String where);
- void isImeTraceEnabled(in IBooleanResultCallback resultCallback);
+ oneway void isImeTraceEnabled(in IBooleanResultCallback resultCallback);
// Starts an ime trace.
void startImeTrace();
diff --git a/core/jni/com_android_internal_os_KernelSingleProcessCpuThreadReader.cpp b/core/jni/com_android_internal_os_KernelSingleProcessCpuThreadReader.cpp
index 52bed6b..dfae684 100644
--- a/core/jni/com_android_internal_os_KernelSingleProcessCpuThreadReader.cpp
+++ b/core/jni/com_android_internal_os_KernelSingleProcessCpuThreadReader.cpp
@@ -26,239 +26,230 @@
#include <android_runtime/Log.h>
#include <nativehelper/ScopedPrimitiveArray.h>
-#include <nativehelper/ScopedUtfChars.h>
namespace android {
+static constexpr uint16_t DEFAULT_THREAD_AGGREGATION_KEY = 0;
+static constexpr uint16_t SELECTED_THREAD_AGGREGATION_KEY = 1;
+
+static constexpr uint64_t NSEC_PER_MSEC = 1000000;
+
// Number of milliseconds in a jiffy - the unit of time measurement for processes and threads
static const uint32_t gJiffyMillis = (uint32_t)(1000 / sysconf(_SC_CLK_TCK));
-// Given a PID, returns a vector of all TIDs for the process' tasks. Thread IDs are
-// file names in the /proc/<pid>/task directory.
-static bool getThreadIds(const std::string &procPath, const pid_t pid,
- std::vector<pid_t> &outThreadIds) {
- std::string taskPath = android::base::StringPrintf("%s/%u/task", procPath.c_str(), pid);
+// Abstract class for readers of CPU time-in-state. There are two implementations of
+// this class: BpfCpuTimeInStateReader and MockCpuTimeInStateReader. The former is used
+// by the production code. The latter is used by unit tests to provide mock
+// CPU time-in-state data via a Java implementation.
+class ICpuTimeInStateReader {
+public:
+ virtual ~ICpuTimeInStateReader() {}
- struct dirent **dirlist;
- int threadCount = scandir(taskPath.c_str(), &dirlist, NULL, NULL);
- if (threadCount == -1) {
- ALOGE("Cannot read directory %s", taskPath.c_str());
- return false;
- }
+ // Returns the overall number of cluser-frequency combinations
+ virtual size_t getCpuFrequencyCount();
- outThreadIds.reserve(threadCount);
+ // Marks the CPU time-in-state tracking for threads of the specified TGID
+ virtual bool startTrackingProcessCpuTimes(pid_t) = 0;
- for (int i = 0; i < threadCount; i++) {
- pid_t tid;
- if (android::base::ParseInt<pid_t>(dirlist[i]->d_name, &tid)) {
- outThreadIds.push_back(tid);
+ // Marks the thread specified by its PID for CPU time-in-state tracking.
+ virtual bool startAggregatingTaskCpuTimes(pid_t, uint16_t) = 0;
+
+ // Retrieves the accumulated time-in-state data, which is organized as a map
+ // from aggregation keys to vectors of vectors using the format:
+ // { aggKey0 -> [[t0_0_0, t0_0_1, ...], [t0_1_0, t0_1_1, ...], ...],
+ // aggKey1 -> [[t1_0_0, t1_0_1, ...], [t1_1_0, t1_1_1, ...], ...], ... }
+ // where ti_j_k is the ns tid i spent running on the jth cluster at the cluster's kth lowest
+ // freq.
+ virtual std::optional<std::unordered_map<uint16_t, std::vector<std::vector<uint64_t>>>>
+ getAggregatedTaskCpuFreqTimes(pid_t, const std::vector<uint16_t> &);
+};
+
+// ICpuTimeInStateReader that uses eBPF to provide a map of aggregated CPU time-in-state values.
+// See cputtimeinstate.h/.cpp
+class BpfCpuTimeInStateReader : public ICpuTimeInStateReader {
+public:
+ size_t getCpuFrequencyCount() {
+ std::optional<std::vector<std::vector<uint32_t>>> cpuFreqs = android::bpf::getCpuFreqs();
+ if (!cpuFreqs) {
+ ALOGE("Cannot obtain CPU frequency count");
+ return 0;
}
- free(dirlist[i]);
- }
- free(dirlist);
- return true;
+ size_t freqCount = 0;
+ for (auto cluster : *cpuFreqs) {
+ freqCount += cluster.size();
+ }
+
+ return freqCount;
+ }
+
+ bool startTrackingProcessCpuTimes(pid_t tgid) {
+ return android::bpf::startTrackingProcessCpuTimes(tgid);
+ }
+
+ bool startAggregatingTaskCpuTimes(pid_t pid, uint16_t aggregationKey) {
+ return android::bpf::startAggregatingTaskCpuTimes(pid, aggregationKey);
+ }
+
+ std::optional<std::unordered_map<uint16_t, std::vector<std::vector<uint64_t>>>>
+ getAggregatedTaskCpuFreqTimes(pid_t pid, const std::vector<uint16_t> &aggregationKeys) {
+ return android::bpf::getAggregatedTaskCpuFreqTimes(pid, aggregationKeys);
+ }
+};
+
+// ICpuTimeInStateReader that uses JNI to provide a map of aggregated CPU time-in-state
+// values.
+// This version of CpuTimeInStateReader is used exclusively for providing mock data in tests.
+class MockCpuTimeInStateReader : public ICpuTimeInStateReader {
+private:
+ JNIEnv *mEnv;
+ jobject mCpuTimeInStateReader;
+
+public:
+ MockCpuTimeInStateReader(JNIEnv *env, jobject cpuTimeInStateReader)
+ : mEnv(env), mCpuTimeInStateReader(cpuTimeInStateReader) {}
+
+ size_t getCpuFrequencyCount();
+
+ bool startTrackingProcessCpuTimes(pid_t tgid);
+
+ bool startAggregatingTaskCpuTimes(pid_t pid, uint16_t aggregationKey);
+
+ std::optional<std::unordered_map<uint16_t, std::vector<std::vector<uint64_t>>>>
+ getAggregatedTaskCpuFreqTimes(pid_t tgid, const std::vector<uint16_t> &aggregationKeys);
+};
+
+static ICpuTimeInStateReader *getCpuTimeInStateReader(JNIEnv *env,
+ jobject cpuTimeInStateReaderObject) {
+ if (cpuTimeInStateReaderObject) {
+ return new MockCpuTimeInStateReader(env, cpuTimeInStateReaderObject);
+ } else {
+ return new BpfCpuTimeInStateReader();
+ }
}
-// Reads contents of a time_in_state file and returns times as a vector of times per frequency
-// A time_in_state file contains pairs of frequency - time (in jiffies):
-//
-// cpu0
-// 300000 30
-// 403200 0
-// cpu4
-// 710400 10
-// 825600 20
-// 940800 30
-//
-static bool getThreadTimeInState(const std::string &procPath, const pid_t pid, const pid_t tid,
- const size_t frequencyCount,
- std::vector<uint64_t> &outThreadTimeInState) {
- std::string timeInStateFilePath =
- android::base::StringPrintf("%s/%u/task/%u/time_in_state", procPath.c_str(), pid, tid);
- std::string data;
+static jint getCpuFrequencyCount(JNIEnv *env, jclass, jobject cpuTimeInStateReaderObject) {
+ std::unique_ptr<ICpuTimeInStateReader> cpuTimeInStateReader(
+ getCpuTimeInStateReader(env, cpuTimeInStateReaderObject));
+ return cpuTimeInStateReader->getCpuFrequencyCount();
+}
- if (!android::base::ReadFileToString(timeInStateFilePath, &data)) {
- ALOGE("Cannot read file: %s", timeInStateFilePath.c_str());
- return false;
- }
+static jboolean startTrackingProcessCpuTimes(JNIEnv *env, jclass, jint tgid,
+ jobject cpuTimeInStateReaderObject) {
+ std::unique_ptr<ICpuTimeInStateReader> cpuTimeInStateReader(
+ getCpuTimeInStateReader(env, cpuTimeInStateReaderObject));
+ return cpuTimeInStateReader->startTrackingProcessCpuTimes(tgid);
+}
- auto lines = android::base::Split(data, "\n");
- size_t index = 0;
- for (const auto &line : lines) {
- if (line.empty()) {
- continue;
- }
+static jboolean startAggregatingThreadCpuTimes(JNIEnv *env, jclass, jintArray selectedThreadIdArray,
+ jobject cpuTimeInStateReaderObject) {
+ ScopedIntArrayRO selectedThreadIds(env, selectedThreadIdArray);
+ std::unique_ptr<ICpuTimeInStateReader> cpuTimeInStateReader(
+ getCpuTimeInStateReader(env, cpuTimeInStateReaderObject));
- auto numbers = android::base::Split(line, " ");
- if (numbers.size() != 2) {
- continue;
- }
- uint64_t timeInState;
- if (!android::base::ParseUint<uint64_t>(numbers[1], &timeInState)) {
- ALOGE("Invalid time_in_state file format: %s", timeInStateFilePath.c_str());
+ for (int i = 0; i < selectedThreadIds.size(); i++) {
+ if (!cpuTimeInStateReader->startAggregatingTaskCpuTimes(selectedThreadIds[i],
+ SELECTED_THREAD_AGGREGATION_KEY)) {
return false;
}
- if (index < frequencyCount) {
- outThreadTimeInState[index] = timeInState;
- }
- index++;
}
+ return true;
+}
+// Converts time-in-state data from a vector of vectors to a flat array.
+// Also converts from nanoseconds to milliseconds.
+static bool flattenTimeInStateData(ScopedLongArrayRW &cpuTimesMillis,
+ const std::vector<std::vector<uint64_t>> &data) {
+ size_t frequencyCount = cpuTimesMillis.size();
+ size_t index = 0;
+ for (const auto &cluster : data) {
+ for (const uint64_t &timeNanos : cluster) {
+ if (index < frequencyCount) {
+ cpuTimesMillis[index] = timeNanos / NSEC_PER_MSEC;
+ }
+ index++;
+ }
+ }
if (index != frequencyCount) {
- ALOGE("Incorrect number of frequencies %u in %s. Expected %u",
- (uint32_t)outThreadTimeInState.size(), timeInStateFilePath.c_str(),
- (uint32_t)frequencyCount);
+ ALOGE("CPU time-in-state reader returned data for %zu frequencies; expected: %zu", index,
+ frequencyCount);
return false;
}
return true;
}
-static int pidCompare(const void *a, const void *b) {
- return (*(pid_t *)a - *(pid_t *)b);
-}
-
-static inline bool isSelectedThread(const pid_t tid, const pid_t *selectedThreadIds,
- const size_t selectedThreadCount) {
- return bsearch(&tid, selectedThreadIds, selectedThreadCount, sizeof(pid_t), pidCompare) != NULL;
-}
-
-// Reads all /proc/<pid>/task/*/time_in_state files and aggregates per-frequency
+// Reads all CPU time-in-state data accumulated by BPF and aggregates per-frequency
// time in state data for all threads. Also, separately aggregates time in state for
// selected threads whose TIDs are passes as selectedThreadIds.
-static void aggregateThreadCpuTimes(const std::string &procPath, const pid_t pid,
- const std::vector<pid_t> &threadIds,
- const size_t frequencyCount, const pid_t *selectedThreadIds,
- const size_t selectedThreadCount,
- uint64_t *threadCpuTimesMillis,
- uint64_t *selectedThreadCpuTimesMillis) {
- for (size_t j = 0; j < frequencyCount; j++) {
- threadCpuTimesMillis[j] = 0;
- selectedThreadCpuTimesMillis[j] = 0;
- }
-
- for (size_t i = 0; i < threadIds.size(); i++) {
- pid_t tid = threadIds[i];
- std::vector<uint64_t> timeInState(frequencyCount);
- if (!getThreadTimeInState(procPath, pid, tid, frequencyCount, timeInState)) {
- continue;
- }
-
- bool selectedThread = isSelectedThread(tid, selectedThreadIds, selectedThreadCount);
- for (size_t j = 0; j < frequencyCount; j++) {
- threadCpuTimesMillis[j] += timeInState[j];
- if (selectedThread) {
- selectedThreadCpuTimesMillis[j] += timeInState[j];
- }
- }
- }
- for (size_t i = 0; i < frequencyCount; i++) {
- threadCpuTimesMillis[i] *= gJiffyMillis;
- selectedThreadCpuTimesMillis[i] *= gJiffyMillis;
- }
-}
-
-// Reads process utime and stime from the /proc/<pid>/stat file.
-// Format of this file is described in https://man7.org/linux/man-pages/man5/proc.5.html.
-static bool getProcessCpuTime(const std::string &procPath, const pid_t pid,
- uint64_t &outTimeMillis) {
- std::string statFilePath = android::base::StringPrintf("%s/%u/stat", procPath.c_str(), pid);
- std::string data;
- if (!android::base::ReadFileToString(statFilePath, &data)) {
- return false;
- }
-
- auto fields = android::base::Split(data, " ");
- uint64_t utime, stime;
-
- // Field 14 (counting from 1) is utime - process time in user space, in jiffies
- // Field 15 (counting from 1) is stime - process time in system space, in jiffies
- if (fields.size() < 15 || !android::base::ParseUint(fields[13], &utime) ||
- !android::base::ParseUint(fields[14], &stime)) {
- ALOGE("Invalid file format %s", statFilePath.c_str());
- return false;
- }
-
- outTimeMillis = (utime + stime) * gJiffyMillis;
- return true;
-}
-
-// Estimates per cluster per frequency CPU time for the entire process
-// by distributing the total process CPU time proportionately to how much
-// CPU time its threads took on those clusters/frequencies. This algorithm
-// works more accurately when when we have equally distributed concurrency.
-// TODO(b/169279846): obtain actual process CPU times from the kernel
-static void estimateProcessTimeInState(const uint64_t processCpuTimeMillis,
- const uint64_t *threadCpuTimesMillis,
- const size_t frequencyCount,
- uint64_t *processCpuTimesMillis) {
- uint64_t totalCpuTimeAllThreads = 0;
- for (size_t i = 0; i < frequencyCount; i++) {
- totalCpuTimeAllThreads += threadCpuTimesMillis[i];
- }
-
- if (totalCpuTimeAllThreads != 0) {
- for (size_t i = 0; i < frequencyCount; i++) {
- processCpuTimesMillis[i] =
- processCpuTimeMillis * threadCpuTimesMillis[i] / totalCpuTimeAllThreads;
- }
- } else {
- for (size_t i = 0; i < frequencyCount; i++) {
- processCpuTimesMillis[i] = 0;
- }
- }
-}
-
-static jboolean readProcessCpuUsage(JNIEnv *env, jclass, jstring procPath, jint pid,
- jintArray selectedThreadIdArray,
- jlongArray processCpuTimesMillisArray,
+static jboolean readProcessCpuUsage(JNIEnv *env, jclass, jint pid,
jlongArray threadCpuTimesMillisArray,
- jlongArray selectedThreadCpuTimesMillisArray) {
- ScopedUtfChars procPathChars(env, procPath);
- ScopedIntArrayRO selectedThreadIds(env, selectedThreadIdArray);
- ScopedLongArrayRW processCpuTimesMillis(env, processCpuTimesMillisArray);
+ jlongArray selectedThreadCpuTimesMillisArray,
+ jobject cpuTimeInStateReaderObject) {
ScopedLongArrayRW threadCpuTimesMillis(env, threadCpuTimesMillisArray);
ScopedLongArrayRW selectedThreadCpuTimesMillis(env, selectedThreadCpuTimesMillisArray);
+ std::unique_ptr<ICpuTimeInStateReader> cpuTimeInStateReader(
+ getCpuTimeInStateReader(env, cpuTimeInStateReaderObject));
- std::string procPathStr(procPathChars.c_str());
-
- // Get all thread IDs for the process.
- std::vector<pid_t> threadIds;
- if (!getThreadIds(procPathStr, pid, threadIds)) {
- ALOGE("Could not obtain thread IDs from: %s", procPathStr.c_str());
- return false;
- }
-
- size_t frequencyCount = processCpuTimesMillis.size();
+ const size_t frequencyCount = cpuTimeInStateReader->getCpuFrequencyCount();
if (threadCpuTimesMillis.size() != frequencyCount) {
- ALOGE("Invalid array length: threadCpuTimesMillis");
+ ALOGE("Invalid threadCpuTimesMillis array length: %zu frequencies; expected: %zu",
+ threadCpuTimesMillis.size(), frequencyCount);
return false;
}
+
if (selectedThreadCpuTimesMillis.size() != frequencyCount) {
- ALOGE("Invalid array length: selectedThreadCpuTimesMillisArray");
+ ALOGE("Invalid selectedThreadCpuTimesMillis array length: %zu frequencies; expected: %zu",
+ selectedThreadCpuTimesMillis.size(), frequencyCount);
return false;
}
- aggregateThreadCpuTimes(procPathStr, pid, threadIds, frequencyCount, selectedThreadIds.get(),
- selectedThreadIds.size(),
- reinterpret_cast<uint64_t *>(threadCpuTimesMillis.get()),
- reinterpret_cast<uint64_t *>(selectedThreadCpuTimesMillis.get()));
-
- uint64_t processCpuTime;
- bool ret = getProcessCpuTime(procPathStr, pid, processCpuTime);
- if (ret) {
- estimateProcessTimeInState(processCpuTime,
- reinterpret_cast<uint64_t *>(threadCpuTimesMillis.get()),
- frequencyCount,
- reinterpret_cast<uint64_t *>(processCpuTimesMillis.get()));
+ for (size_t i = 0; i < frequencyCount; i++) {
+ threadCpuTimesMillis[i] = 0;
+ selectedThreadCpuTimesMillis[i] = 0;
}
- return ret;
+
+ std::optional<std::unordered_map<uint16_t, std::vector<std::vector<uint64_t>>>> data =
+ cpuTimeInStateReader->getAggregatedTaskCpuFreqTimes(pid,
+ {DEFAULT_THREAD_AGGREGATION_KEY,
+ SELECTED_THREAD_AGGREGATION_KEY});
+ if (!data) {
+ ALOGE("Cannot read thread CPU times for PID %d", pid);
+ return false;
+ }
+
+ if (!flattenTimeInStateData(threadCpuTimesMillis, (*data)[DEFAULT_THREAD_AGGREGATION_KEY])) {
+ return false;
+ }
+
+ if (!flattenTimeInStateData(selectedThreadCpuTimesMillis,
+ (*data)[SELECTED_THREAD_AGGREGATION_KEY])) {
+ return false;
+ }
+
+ // threadCpuTimesMillis returns CPU times for _all_ threads, including the selected ones
+ for (size_t i = 0; i < frequencyCount; i++) {
+ threadCpuTimesMillis[i] += selectedThreadCpuTimesMillis[i];
+ }
+
+ return true;
}
static const JNINativeMethod g_single_methods[] = {
- {"readProcessCpuUsage", "(Ljava/lang/String;I[I[J[J[J)Z", (void *)readProcessCpuUsage},
+ {"getCpuFrequencyCount",
+ "(Lcom/android/internal/os/KernelSingleProcessCpuThreadReader$CpuTimeInStateReader;)I",
+ (void *)getCpuFrequencyCount},
+ {"startTrackingProcessCpuTimes",
+ "(ILcom/android/internal/os/KernelSingleProcessCpuThreadReader$CpuTimeInStateReader;)Z",
+ (void *)startTrackingProcessCpuTimes},
+ {"startAggregatingThreadCpuTimes",
+ "([ILcom/android/internal/os/KernelSingleProcessCpuThreadReader$CpuTimeInStateReader;)Z",
+ (void *)startAggregatingThreadCpuTimes},
+ {"readProcessCpuUsage",
+ "(I[J[J"
+ "Lcom/android/internal/os/KernelSingleProcessCpuThreadReader$CpuTimeInStateReader;)Z",
+ (void *)readProcessCpuUsage},
};
int register_com_android_internal_os_KernelSingleProcessCpuThreadReader(JNIEnv *env) {
@@ -266,4 +257,77 @@
g_single_methods, NELEM(g_single_methods));
}
+size_t MockCpuTimeInStateReader::getCpuFrequencyCount() {
+ jclass cls = mEnv->GetObjectClass(mCpuTimeInStateReader);
+ jmethodID mid = mEnv->GetMethodID(cls, "getCpuFrequencyCount", "()I");
+ if (mid == 0) {
+ ALOGE("Couldn't find the method getCpuFrequencyCount");
+ return false;
+ }
+ return (size_t)mEnv->CallIntMethod(mCpuTimeInStateReader, mid);
+}
+
+bool MockCpuTimeInStateReader::startTrackingProcessCpuTimes(pid_t tgid) {
+ jclass cls = mEnv->GetObjectClass(mCpuTimeInStateReader);
+ jmethodID mid = mEnv->GetMethodID(cls, "startTrackingProcessCpuTimes", "(I)Z");
+ if (mid == 0) {
+ ALOGE("Couldn't find the method startTrackingProcessCpuTimes");
+ return false;
+ }
+ return mEnv->CallBooleanMethod(mCpuTimeInStateReader, mid, tgid);
+}
+
+bool MockCpuTimeInStateReader::startAggregatingTaskCpuTimes(pid_t pid, uint16_t aggregationKey) {
+ jclass cls = mEnv->GetObjectClass(mCpuTimeInStateReader);
+ jmethodID mid = mEnv->GetMethodID(cls, "startAggregatingTaskCpuTimes", "(II)Z");
+ if (mid == 0) {
+ ALOGE("Couldn't find the method startAggregatingTaskCpuTimes");
+ return false;
+ }
+ return mEnv->CallBooleanMethod(mCpuTimeInStateReader, mid, pid, aggregationKey);
+}
+
+std::optional<std::unordered_map<uint16_t, std::vector<std::vector<uint64_t>>>>
+MockCpuTimeInStateReader::getAggregatedTaskCpuFreqTimes(
+ pid_t pid, const std::vector<uint16_t> &aggregationKeys) {
+ jclass cls = mEnv->GetObjectClass(mCpuTimeInStateReader);
+ jmethodID mid =
+ mEnv->GetMethodID(cls, "getAggregatedTaskCpuFreqTimes", "(I)[Ljava/lang/String;");
+ if (mid == 0) {
+ ALOGE("Couldn't find the method getAggregatedTaskCpuFreqTimes");
+ return {};
+ }
+
+ std::unordered_map<uint16_t, std::vector<std::vector<uint64_t>>> map;
+
+ jobjectArray stringArray =
+ (jobjectArray)mEnv->CallObjectMethod(mCpuTimeInStateReader, mid, pid);
+ int size = mEnv->GetArrayLength(stringArray);
+ for (int i = 0; i < size; i++) {
+ ScopedUtfChars line(mEnv, (jstring)mEnv->GetObjectArrayElement(stringArray, i));
+ uint16_t aggregationKey;
+ std::vector<std::vector<uint64_t>> times;
+
+ // Each string is formatted like this: "aggKey:t0_0 t0_1...:t1_0 t1_1..."
+ auto fields = android::base::Split(line.c_str(), ":");
+ android::base::ParseUint(fields[0], &aggregationKey);
+
+ for (int j = 1; j < fields.size(); j++) {
+ auto numbers = android::base::Split(fields[j], " ");
+
+ std::vector<uint64_t> chunk;
+ for (int k = 0; k < numbers.size(); k++) {
+ uint64_t time;
+ android::base::ParseUint(numbers[k], &time);
+ chunk.emplace_back(time);
+ }
+ times.emplace_back(chunk);
+ }
+
+ map.emplace(aggregationKey, times);
+ }
+
+ return map;
+}
+
} // namespace android
diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto
index 4809419..2b665c0 100644
--- a/core/proto/android/os/incident.proto
+++ b/core/proto/android/os/incident.proto
@@ -41,7 +41,6 @@
import "frameworks/base/core/proto/android/server/location/context_hub.proto";
import "frameworks/base/core/proto/android/server/powermanagerservice.proto";
import "frameworks/base/core/proto/android/server/powerstatsservice.proto";
-import "frameworks/base/apex/permission/service/proto/com/android/role/roleservice.proto";
import "frameworks/base/core/proto/android/server/windowmanagerservice.proto";
import "frameworks/base/core/proto/android/service/appwidget.proto";
import "frameworks/base/core/proto/android/service/battery.proto";
@@ -63,6 +62,7 @@
import "frameworks/base/core/proto/android/section.proto";
import "frameworks/base/proto/src/ipconnectivity.proto";
import "frameworks/proto_logging/stats/enums/service/usb.proto";
+import "packages/modules/Permission/service/proto/com/android/role/roleservice.proto";
package android.os;
diff --git a/core/proto/android/server/powerstatsservice.proto b/core/proto/android/server/powerstatsservice.proto
index ec9bc11..0c5a360 100644
--- a/core/proto/android/server/powerstatsservice.proto
+++ b/core/proto/android/server/powerstatsservice.proto
@@ -220,6 +220,9 @@
/** Name of the energy meter channel */
optional string name = 2;
+
+ /** Name of the subsystem associated with this Channel. Opaque to framework */
+ optional string subsystem = 3;
}
/**
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index c359a7d..c882431 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -307,6 +307,7 @@
optional bool animating_bounds = 26 [deprecated = true];
optional float minimize_amount = 27;
optional bool created_by_organizer = 28;
+ optional string affinity = 29;
}
/* represents ActivityRecordProto */
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 3975529..396f954 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1744,6 +1744,12 @@
<permission android:name="android.permission.REQUEST_NETWORK_SCORES"
android:protectionLevel="signature|setup" />
+ <!-- Allows applications to restart the Wi-Fi subsystem.
+ @SystemApi
+ <p>Not for use by third-party applications. @hide -->
+ <permission android:name="android.permission.RESTART_WIFI_SUBSYSTEM"
+ android:protectionLevel="signature|setup" />
+
<!-- @SystemApi @hide Allows applications to toggle airplane mode.
<p>Not for use by third-party or privileged applications.
-->
@@ -5906,7 +5912,7 @@
<!-- AOSP configures a default secondary LocationTimeZoneProvider that uses an on-device
data set from the com.android.geotz APEX. -->
- <service android:name="com.android.timezone.geotz.provider.OfflineLocationTimeZoneProviderService"
+ <service android:name="com.android.timezone.location.provider.OfflineLocationTimeZoneProviderService"
android:enabled="@bool/config_enableSecondaryLocationTimeZoneProvider"
android:permission="android.permission.BIND_TIME_ZONE_PROVIDER_SERVICE"
android:exported="false">
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 243c244..415a0a2 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -8288,6 +8288,23 @@
</declare-styleable>
<!-- =============================== -->
+ <!-- System Speech Recognition attributes -->
+ <!-- =============================== -->
+ <eat-comment />
+
+ <!-- Use <code>on-device-recognition-service</code> as the root tag of the XML resource that
+ describes a {@link android.service.speech.RecognitionService}, which is referenced
+ from its {@link android.service.speech.RecognitionService#SERVICE_META_DATA} meta-data
+ entry.
+ @hide @SystemApi
+ -->
+ <declare-styleable name="OnDeviceRecognitionService">
+ <!-- Fully qualified class name of an activity that allows the user to modify
+ the settings for this service. -->
+ <attr name="settingsActivity" />
+ </declare-styleable>
+
+ <!-- =============================== -->
<!-- Content Capture attributes -->
<!-- =============================== -->
<eat-comment />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 4e272f1..a928408 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3800,6 +3800,12 @@
-->
<string name="config_defaultSearchUiService" translatable="false"></string>
+ <!-- The package name for the system's speech recognition service.
+ This service must be trusted, as it can be activated without explicit consent of the user.
+ Example: "com.android.speech/.RecognitionService"
+ -->
+ <string name="config_defaultOnDeviceSpeechRecognitionService" translatable="false"></string>
+
<string name="config_defaultMusicRecognitionService" translatable="false"></string>
<!-- The package name for the default retail demo app.
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 996fbb3..98b36c5 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -298,10 +298,10 @@
<item>@string/crossSimFormat_spn_cross_sim_calling</item>
</string-array>
- <!-- Spn during Cross-SIM Calling: "<operator> " [CHAR LIMIT=NONE] -->
+ <!-- Spn during Backup Calling: "<operator> " [CHAR LIMIT=NONE] -->
<string name="crossSimFormat_spn"><xliff:g id="spn" example="Operator">%s</xliff:g></string>
- <!-- Spn during Cross SIM Calling: "<operator> Cross-SIM Calling" [CHAR LIMIT=NONE] -->
- <string name="crossSimFormat_spn_cross_sim_calling"><xliff:g id="spn" example="Operator">%s</xliff:g> Cross-SIM Calling</string>
+ <!-- Spn during Backup Calling: "<operator> Backup Calling" [CHAR LIMIT=NONE] -->
+ <string name="crossSimFormat_spn_cross_sim_calling"><xliff:g id="spn" example="Operator">%s</xliff:g> Backup Calling</string>
<!--
{0} is one of "bearerServiceCode*"
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index e92eb97..bcef680 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3489,6 +3489,7 @@
<java-symbol type="string" name="notification_channel_do_not_disturb" />
<java-symbol type="string" name="notification_channel_accessibility_magnification" />
<java-symbol type="string" name="config_defaultAutofillService" />
+ <java-symbol type="string" name="config_defaultOnDeviceSpeechRecognitionService" />
<java-symbol type="string" name="config_defaultTextClassifierPackage" />
<java-symbol type="string" name="config_defaultWellbeingPackage" />
<java-symbol type="string" name="config_defaultContentCaptureService" />
diff --git a/core/tests/coretests/src/android/content/OWNERS b/core/tests/coretests/src/android/content/OWNERS
index 912db1e..c61a4b5 100644
--- a/core/tests/coretests/src/android/content/OWNERS
+++ b/core/tests/coretests/src/android/content/OWNERS
@@ -1,3 +1,4 @@
+per-file AssetTest.java = file:/core/java/android/content/res/OWNERS
per-file ContextTest.java = file:/services/core/java/com/android/server/wm/OWNERS
-per-file *Shortcut* = file:/core/java/android/content/pm/SHORTCUT_OWNERS
per-file *Launcher* = file:/core/java/android/content/pm/LAUNCHER_OWNERS
+per-file *Shortcut* = file:/core/java/android/content/pm/SHORTCUT_OWNERS
diff --git a/core/tests/coretests/src/android/content/integrity/OWNERS b/core/tests/coretests/src/android/content/integrity/OWNERS
new file mode 100644
index 0000000..4ffc704
--- /dev/null
+++ b/core/tests/coretests/src/android/content/integrity/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/content/integrity/OWNERS
diff --git a/core/tests/coretests/src/android/content/pm/OWNERS b/core/tests/coretests/src/android/content/pm/OWNERS
index 7b76706..8673365 100644
--- a/core/tests/coretests/src/android/content/pm/OWNERS
+++ b/core/tests/coretests/src/android/content/pm/OWNERS
@@ -1,3 +1,5 @@
+include /core/java/android/content/pm/OWNERS
+
per-file AppSearchPersonTest.java = file:/core/java/android/content/pm/SHORTCUT_OWNERS
-per-file SigningDetailsTest.java = mpgroover@google.com
per-file SigningDetailsTest.java = cbrubaker@google.com
+per-file SigningDetailsTest.java = mpgroover@google.com
diff --git a/core/tests/coretests/src/android/content/res/OWNERS b/core/tests/coretests/src/android/content/res/OWNERS
new file mode 100644
index 0000000..3e79d8f
--- /dev/null
+++ b/core/tests/coretests/src/android/content/res/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/content/res/OWNERS
diff --git a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java
index 65ea2a8..3df2e90 100644
--- a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java
+++ b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java
@@ -171,7 +171,8 @@
FontConfig fontConfig;
try {
fontConfig = FontListParser.parse(
- TEST_FONTS_XML, TEST_FONT_DIR, oemXmlPath, TEST_OEM_DIR, updatableFontMap);
+ TEST_FONTS_XML, TEST_FONT_DIR, oemXmlPath, TEST_OEM_DIR, updatableFontMap, 0,
+ 0);
} catch (IOException | XmlPullParserException e) {
throw new RuntimeException(e);
}
@@ -199,7 +200,7 @@
FontConfig fontConfig;
try {
fontConfig = FontListParser.parse(
- SYSTEM_FONTS_XML, SYSTEM_FONT_DIR, null, TEST_OEM_DIR, null);
+ SYSTEM_FONTS_XML, SYSTEM_FONT_DIR, null, TEST_OEM_DIR, null, 0, 0);
} catch (IOException | XmlPullParserException e) {
throw new RuntimeException(e);
}
diff --git a/core/tests/coretests/src/android/text/FontFallbackSetup.java b/core/tests/coretests/src/android/text/FontFallbackSetup.java
index e301037..90a6ca3 100644
--- a/core/tests/coretests/src/android/text/FontFallbackSetup.java
+++ b/core/tests/coretests/src/android/text/FontFallbackSetup.java
@@ -79,7 +79,7 @@
FontConfig fontConfig;
try {
- fontConfig = FontListParser.parse(testFontsXml, mTestFontsDir, null, null, null);
+ fontConfig = FontListParser.parse(testFontsXml, mTestFontsDir, null, null, null, 0, 0);
} catch (IOException | XmlPullParserException e) {
throw new RuntimeException(e);
}
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java
index fbe16f2..d2107ea 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java
@@ -21,12 +21,10 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
-import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
@@ -194,7 +192,6 @@
sippers.add(mBluetoothBatterySipper);
sippers.add(mIdleBatterySipper);
doReturn(true).when(mBatteryStatsHelper).isTypeSystem(mSystemBatterySipper);
- doNothing().when(mBatteryStatsHelper).smearScreenBatterySipper(any(), any());
final double totalUsage = mBatteryStatsHelper.removeHiddenBatterySippers(sippers);
@@ -208,19 +205,20 @@
@Test
public void testSmearScreenBatterySipper() {
+ final ScreenPowerCalculator spc = spy(ScreenPowerCalculator.class);
final BatterySipper sipperNull = createTestSmearBatterySipper(TIME_FOREGROUND_ACTIVITY_ZERO,
- BATTERY_APP_USAGE, 0 /* uid */, true /* isUidNull */);
+ BATTERY_APP_USAGE, 0 /* uid */, true /* isUidNull */, spc);
final BatterySipper sipperBg = createTestSmearBatterySipper(TIME_FOREGROUND_ACTIVITY_ZERO,
- BATTERY_APP_USAGE, 1 /* uid */, false /* isUidNull */);
+ BATTERY_APP_USAGE, 1 /* uid */, false /* isUidNull */, spc);
final BatterySipper sipperFg = createTestSmearBatterySipper(TIME_FOREGROUND_ACTIVITY,
- BATTERY_APP_USAGE, 2 /* uid */, false /* isUidNull */);
+ BATTERY_APP_USAGE, 2 /* uid */, false /* isUidNull */, spc);
final List<BatterySipper> sippers = new ArrayList<>();
sippers.add(sipperNull);
sippers.add(sipperBg);
sippers.add(sipperFg);
- mBatteryStatsHelper.smearScreenBatterySipper(sippers, mScreenBatterySipper);
+ spc.smearScreenBatterySipper(sippers, mScreenBatterySipper);
assertThat(sipperNull.screenPowerMah).isWithin(PRECISION).of(0);
assertThat(sipperBg.screenPowerMah).isWithin(PRECISION).of(0);
@@ -249,13 +247,13 @@
@Test
public void testGetProcessForegroundTimeMs_largerActivityTime_returnMinTime() {
- doReturn(TIME_STATE_FOREGROUND_US + 500).when(mBatteryStatsHelper)
+ final ScreenPowerCalculator spc = spy(ScreenPowerCalculator.class);
+ doReturn(TIME_STATE_FOREGROUND_US + 500).when(spc)
.getForegroundActivityTotalTimeUs(eq(mUid), anyLong());
doReturn(TIME_STATE_FOREGROUND_US).when(mUid).getProcessStateTime(eq(PROCESS_STATE_TOP),
anyLong(), anyInt());
- final long time = mBatteryStatsHelper.getProcessForegroundTimeMs(mUid,
- BatteryStats.STATS_SINCE_CHARGED);
+ final long time = spc.getProcessForegroundTimeMs(mUid);
assertThat(time).isEqualTo(TIME_STATE_FOREGROUND_MS);
}
@@ -291,15 +289,14 @@
}
private BatterySipper createTestSmearBatterySipper(long activityTime, double totalPowerMah,
- int uidCode, boolean isUidNull) {
+ int uidCode, boolean isUidNull, ScreenPowerCalculator spc) {
final BatterySipper sipper = mock(BatterySipper.class);
sipper.drainType = BatterySipper.DrainType.APP;
sipper.totalPowerMah = totalPowerMah;
doReturn(uidCode).when(sipper).getUid();
if (!isUidNull) {
final BatteryStats.Uid uid = mock(BatteryStats.Uid.class, RETURNS_DEEP_STUBS);
- doReturn(activityTime).when(mBatteryStatsHelper).getProcessForegroundTimeMs(eq(uid),
- anyInt());
+ doReturn(activityTime).when(spc).getProcessForegroundTimeMs(eq(uid));
doReturn(uidCode).when(uid).getUid();
sipper.uidObj = uid;
}
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelSingleProcessCpuThreadReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelSingleProcessCpuThreadReaderTest.java
index b5720a2..2de800b 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelSingleProcessCpuThreadReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelSingleProcessCpuThreadReaderTest.java
@@ -19,122 +19,87 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.junit.Assert.assertTrue;
-
-import android.content.Context;
-import android.os.FileUtils;
-
-import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import org.junit.After;
-import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.io.File;
import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.file.Files;
-import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.List;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class KernelSingleProcessCpuThreadReaderTest {
- private File mProcDirectory;
-
- @Before
- public void setUp() {
- Context context = InstrumentationRegistry.getContext();
- mProcDirectory = context.getDir("proc", Context.MODE_PRIVATE);
- }
-
- @After
- public void tearDown() throws Exception {
- FileUtils.deleteContents(mProcDirectory);
- }
-
@Test
public void getProcessCpuUsage() throws IOException {
- setupDirectory(42,
- new int[] {42, 1, 2, 3},
- new int[] {1000, 2000},
- // Units are 10ms aka 10000Us
- new int[][] {{100, 200}, {0, 200}, {100, 300}, {0, 600}},
- new int[] {4500, 500});
+ // Units are nanoseconds
+ MockCpuTimeInStateReader mockReader = new MockCpuTimeInStateReader(4, new String[] {
+ "0:1000000000 2000000000 3000000000:4000000000",
+ "1:100000000 200000000 300000000:400000000",
+ });
KernelSingleProcessCpuThreadReader reader = new KernelSingleProcessCpuThreadReader(42,
- mProcDirectory.toPath());
+ mockReader);
+ reader.setSelectedThreadIds(new int[] {2, 3});
+ reader.startTrackingThreadCpuTimes();
KernelSingleProcessCpuThreadReader.ProcessCpuUsage processCpuUsage =
- reader.getProcessCpuUsage(new int[] {2, 3});
- assertThat(processCpuUsage.threadCpuTimesMillis).isEqualTo(new long[] {2000, 13000});
- assertThat(processCpuUsage.selectedThreadCpuTimesMillis).isEqualTo(new long[] {1000, 9000});
- assertThat(processCpuUsage.processCpuTimesMillis).isEqualTo(new long[] {6666, 43333});
+ reader.getProcessCpuUsage();
+ assertThat(mockReader.mTrackedTgid).isEqualTo(42);
+ // The strings are formatted as <TID TGID AGG_KEY>, where AGG_KEY is 1 for binder
+ // threads and 0 for all other threads.
+ assertThat(mockReader.mTrackedTasks).containsExactly(
+ "2 1",
+ "3 1");
+ assertThat(processCpuUsage.threadCpuTimesMillis).isEqualTo(
+ new long[] {1100, 2200, 3300, 4400});
+ assertThat(processCpuUsage.selectedThreadCpuTimesMillis).isEqualTo(
+ new long[] {100, 200, 300, 400});
}
@Test
public void getCpuFrequencyCount() throws IOException {
- setupDirectory(13,
- new int[] {13},
- new int[] {1000, 2000, 3000},
- new int[][] {{100, 200, 300}},
- new int[] {14, 15});
+ MockCpuTimeInStateReader mockReader = new MockCpuTimeInStateReader(3, new String[0]);
KernelSingleProcessCpuThreadReader reader = new KernelSingleProcessCpuThreadReader(13,
- mProcDirectory.toPath());
+ mockReader);
int cpuFrequencyCount = reader.getCpuFrequencyCount();
assertThat(cpuFrequencyCount).isEqualTo(3);
}
- private void setupDirectory(int pid, int[] threadIds, int[] cpuFrequencies,
- int[][] threadCpuTimes, int[] processCpuTimes)
- throws IOException {
+ public static class MockCpuTimeInStateReader implements
+ KernelSingleProcessCpuThreadReader.CpuTimeInStateReader {
+ private final int mCpuFrequencyCount;
+ private final String[] mAggregatedTaskCpuFreqTimes;
+ public int mTrackedTgid;
+ public List<String> mTrackedTasks = new ArrayList<>();
- assertTrue(mProcDirectory.toPath().resolve("self").toFile().mkdirs());
-
- try (OutputStream timeInStateStream =
- Files.newOutputStream(
- mProcDirectory.toPath().resolve("self").resolve("time_in_state"))) {
- for (int i = 0; i < cpuFrequencies.length; i++) {
- final String line = cpuFrequencies[i] + " 0\n";
- timeInStateStream.write(line.getBytes());
- }
+ public MockCpuTimeInStateReader(int cpuFrequencyCount,
+ String[] aggregatedTaskCpuFreqTimes) {
+ mCpuFrequencyCount = cpuFrequencyCount;
+ mAggregatedTaskCpuFreqTimes = aggregatedTaskCpuFreqTimes;
}
- Path processPath = mProcDirectory.toPath().resolve(String.valueOf(pid));
-
- // Make /proc/$PID
- assertTrue(processPath.toFile().mkdirs());
-
- // Write /proc/$PID/stat. Only the fields 14-17 matter.
- try (OutputStream timeInStateStream = Files.newOutputStream(processPath.resolve("stat"))) {
- timeInStateStream.write(
- (pid + " (test) S 4 5 6 7 8 9 10 11 12 13 "
- + processCpuTimes[0] + " "
- + processCpuTimes[1] + " "
- + "16 17 18 19 20 ...").getBytes());
+ @Override
+ public int getCpuFrequencyCount() {
+ return mCpuFrequencyCount;
}
- // Make /proc/$PID/task
- final Path selfThreadsPath = processPath.resolve("task");
- assertTrue(selfThreadsPath.toFile().mkdirs());
+ @Override
+ public boolean startTrackingProcessCpuTimes(int tgid) {
+ mTrackedTgid = tgid;
+ return true;
+ }
- // Make thread directories
- for (int i = 0; i < threadIds.length; i++) {
- // Make /proc/$PID/task/$TID
- final Path threadPath = selfThreadsPath.resolve(String.valueOf(threadIds[i]));
- assertTrue(threadPath.toFile().mkdirs());
+ public boolean startAggregatingTaskCpuTimes(int pid, int aggregationKey) {
+ mTrackedTasks.add(pid + " " + aggregationKey);
+ return true;
+ }
- // Make /proc/$PID/task/$TID/time_in_state
- try (OutputStream timeInStateStream =
- Files.newOutputStream(threadPath.resolve("time_in_state"))) {
- for (int j = 0; j < cpuFrequencies.length; j++) {
- final String line = cpuFrequencies[j] + " " + threadCpuTimes[i][j] + "\n";
- timeInStateStream.write(line.getBytes());
- }
- }
+ public String[] getAggregatedTaskCpuFreqTimes(int pid) {
+ return mAggregatedTaskCpuFreqTimes;
}
}
}
diff --git a/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java
index 4230066..66a8379 100644
--- a/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java
@@ -21,7 +21,7 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
-import android.net.ConnectivityManager;
+import android.net.NetworkCapabilities;
import android.net.NetworkStats;
import android.os.BatteryConsumer;
import android.os.Process;
@@ -78,7 +78,8 @@
8_000_000_000L, APP_UID, 8000, 8000);
// Note established network
- stats.noteNetworkInterfaceType("cellular", ConnectivityManager.TYPE_MOBILE);
+ stats.noteNetworkInterfaceForTransports("cellular",
+ new int[] { NetworkCapabilities.TRANSPORT_CELLULAR });
// Note application network activity
NetworkStats networkStats = new NetworkStats(10000, 1)
diff --git a/core/tests/coretests/src/com/android/internal/os/SystemServerCpuThreadReaderTest.java b/core/tests/coretests/src/com/android/internal/os/SystemServerCpuThreadReaderTest.java
index 121c637..d116d4d 100644
--- a/core/tests/coretests/src/com/android/internal/os/SystemServerCpuThreadReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/SystemServerCpuThreadReaderTest.java
@@ -16,146 +16,86 @@
package com.android.internal.os;
-import static org.junit.Assert.assertArrayEquals;
-import static org.junit.Assert.assertTrue;
+import static com.google.common.truth.Truth.assertThat;
-import android.content.Context;
-import android.os.FileUtils;
-
-import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import org.junit.After;
-import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.io.File;
import java.io.IOException;
-import java.io.OutputStream;
-import java.nio.file.Files;
-import java.nio.file.Path;
@SmallTest
@RunWith(AndroidJUnit4.class)
public class SystemServerCpuThreadReaderTest {
- private File mProcDirectory;
-
- @Before
- public void setUp() {
- Context context = InstrumentationRegistry.getContext();
- mProcDirectory = context.getDir("proc", Context.MODE_PRIVATE);
- }
-
- @After
- public void tearDown() throws Exception {
- FileUtils.deleteContents(mProcDirectory);
- }
@Test
- public void testReaderDelta_firstTime() throws IOException {
+ public void testReadDelta() throws IOException {
int pid = 42;
- setupDirectory(
- pid,
- new int[] {42, 1, 2, 3},
- new int[] {1000, 2000},
- // Units are 10ms aka 10000Us
- new int[][] {{100, 200}, {0, 200}, {0, 300}, {0, 400}},
- new int[] {1400, 1500});
- SystemServerCpuThreadReader reader = new SystemServerCpuThreadReader(
- mProcDirectory.toPath(), pid);
- reader.setBinderThreadNativeTids(new int[] {1, 3});
- SystemServerCpuThreadReader.SystemServiceCpuThreadTimes systemServiceCpuThreadTimes =
- reader.readDelta();
- assertArrayEquals(new long[] {100 * 10000, 1100 * 10000},
- systemServiceCpuThreadTimes.threadCpuTimesUs);
- assertArrayEquals(new long[] {0, 600 * 10000},
- systemServiceCpuThreadTimes.binderThreadCpuTimesUs);
- }
+ MockCpuTimeInStateReader mockReader = new MockCpuTimeInStateReader(4);
+ // Units are nanoseconds
+ mockReader.setAggregatedTaskCpuFreqTimes(new String[] {
+ "0:1000000000 2000000000 3000000000:4000000000",
+ "1:100000000 200000000 300000000:400000000",
+ });
- @Test
- public void testReaderDelta_nextTime() throws IOException {
- int pid = 42;
- setupDirectory(
- pid,
- new int[] {42, 1, 2, 3},
- new int[] {1000, 2000},
- new int[][] {{100, 200}, {0, 200}, {0, 300}, {0, 400}},
- new int[] {1400, 1500});
-
- SystemServerCpuThreadReader reader = new SystemServerCpuThreadReader(
- mProcDirectory.toPath(), pid);
+ SystemServerCpuThreadReader reader = new SystemServerCpuThreadReader(pid, mockReader);
reader.setBinderThreadNativeTids(new int[] {1, 3});
- // First time, populate "last" snapshot
- reader.readDelta();
-
- FileUtils.deleteContents(mProcDirectory);
- setupDirectory(
- pid,
- new int[] {42, 1, 2, 3},
- new int[] {1000, 2000},
- new int[][] {{500, 600}, {700, 800}, {900, 1000}, {1100, 1200}},
- new int[] {2400, 2500});
-
- // Second time, get the actual delta
+ // The first invocation of readDelta populates the "last" snapshot
SystemServerCpuThreadReader.SystemServiceCpuThreadTimes systemServiceCpuThreadTimes =
reader.readDelta();
- assertArrayEquals(new long[] {3100 * 10000, 2500 * 10000},
- systemServiceCpuThreadTimes.threadCpuTimesUs);
- assertArrayEquals(new long[] {1800 * 10000, 1400 * 10000},
- systemServiceCpuThreadTimes.binderThreadCpuTimesUs);
+ assertThat(systemServiceCpuThreadTimes.threadCpuTimesUs)
+ .isEqualTo(new long[] {1100000, 2200000, 3300000, 4400000});
+ assertThat(systemServiceCpuThreadTimes.binderThreadCpuTimesUs)
+ .isEqualTo(new long[] {100000, 200000, 300000, 400000});
+
+ mockReader.setAggregatedTaskCpuFreqTimes(new String[] {
+ "0:1010000000 2020000000 3030000000:4040000000",
+ "1:101000000 202000000 303000000:404000000",
+ });
+
+ // The second invocation gets the actual delta
+ systemServiceCpuThreadTimes = reader.readDelta();
+
+ assertThat(systemServiceCpuThreadTimes.threadCpuTimesUs)
+ .isEqualTo(new long[] {11000, 22000, 33000, 44000});
+ assertThat(systemServiceCpuThreadTimes.binderThreadCpuTimesUs)
+ .isEqualTo(new long[] {1000, 2000, 3000, 4000});
}
- private void setupDirectory(int pid, int[] threadIds, int[] cpuFrequencies, int[][] cpuTimes,
- int[] processCpuTimes)
- throws IOException {
+ public static class MockCpuTimeInStateReader implements
+ KernelSingleProcessCpuThreadReader.CpuTimeInStateReader {
+ private final int mCpuFrequencyCount;
+ private String[] mAggregatedTaskCpuFreqTimes;
- assertTrue(mProcDirectory.toPath().resolve("self").toFile().mkdirs());
-
- try (OutputStream timeInStateStream =
- Files.newOutputStream(
- mProcDirectory.toPath().resolve("self").resolve("time_in_state"))) {
- for (int i = 0; i < cpuFrequencies.length; i++) {
- final String line = cpuFrequencies[i] + " 0\n";
- timeInStateStream.write(line.getBytes());
- }
+ MockCpuTimeInStateReader(int frequencyCount) {
+ mCpuFrequencyCount = frequencyCount;
}
- Path processPath = mProcDirectory.toPath().resolve(String.valueOf(pid));
- // Make /proc/$PID
- assertTrue(processPath.toFile().mkdirs());
-
- // Write /proc/$PID/stat. Only the fields 14-17 matter.
- try (OutputStream timeInStateStream = Files.newOutputStream(processPath.resolve("stat"))) {
- timeInStateStream.write(
- (pid + " (test) S 4 5 6 7 8 9 10 11 12 13 "
- + processCpuTimes[0] + " "
- + processCpuTimes[1] + " "
- + "16 17 18 19 20 ...").getBytes());
+ @Override
+ public int getCpuFrequencyCount() {
+ return mCpuFrequencyCount;
}
- // Make /proc/$PID/task
- final Path selfThreadsPath = processPath.resolve("task");
- assertTrue(selfThreadsPath.toFile().mkdirs());
+ @Override
+ public boolean startTrackingProcessCpuTimes(int tgid) {
+ return true;
+ }
- // Make thread directories
- for (int i = 0; i < threadIds.length; i++) {
- // Make /proc/$PID/task/$TID
- final Path threadPath = selfThreadsPath.resolve(String.valueOf(threadIds[i]));
- assertTrue(threadPath.toFile().mkdirs());
+ public boolean startAggregatingTaskCpuTimes(int pid, int aggregationKey) {
+ return true;
+ }
- // Make /proc/$PID/task/$TID/time_in_state
- try (OutputStream timeInStateStream =
- Files.newOutputStream(threadPath.resolve("time_in_state"))) {
- for (int j = 0; j < cpuFrequencies.length; j++) {
- final String line = cpuFrequencies[j] + " " + cpuTimes[i][j] + "\n";
- timeInStateStream.write(line.getBytes());
- }
- }
+ public void setAggregatedTaskCpuFreqTimes(String[] mAggregatedTaskCpuFreqTimes) {
+ this.mAggregatedTaskCpuFreqTimes = mAggregatedTaskCpuFreqTimes;
+ }
+
+ public String[] getAggregatedTaskCpuFreqTimes(int pid) {
+ return mAggregatedTaskCpuFreqTimes;
}
}
}
diff --git a/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java
index a5cafb9..24741fe 100644
--- a/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/SystemServicePowerCalculatorTest.java
@@ -62,7 +62,6 @@
public void testCalculateApp() {
// Test Power Profile has two CPU clusters with 3 and 4 speeds, thus 7 freq times total
mMockSystemServerCpuThreadReader.setCpuTimes(
- new long[] {10000, 15000, 20000, 25000, 30000, 35000, 40000},
new long[] {30000, 40000, 50000, 60000, 70000, 80000, 90000},
new long[] {20000, 30000, 40000, 50000, 60000, 70000, 80000});
@@ -144,9 +143,7 @@
super(null);
}
- public void setCpuTimes(long[] processCpuTimesUs, long[] threadCpuTimesUs,
- long[] binderThreadCpuTimesUs) {
- mThreadTimes.processCpuTimesUs = processCpuTimesUs;
+ public void setCpuTimes(long[] threadCpuTimesUs, long[] binderThreadCpuTimesUs) {
mThreadTimes.threadCpuTimesUs = threadCpuTimesUs;
mThreadTimes.binderThreadCpuTimesUs = binderThreadCpuTimesUs;
}
diff --git a/data/etc/com.android.emergency.xml b/data/etc/com.android.emergency.xml
index 28f99dd..734561c 100644
--- a/data/etc/com.android.emergency.xml
+++ b/data/etc/com.android.emergency.xml
@@ -19,5 +19,6 @@
<!-- Required to place emergency calls from emergency info screen. -->
<permission name="android.permission.CALL_PRIVILEGED"/>
<permission name="android.permission.MANAGE_USERS"/>
+ <permission name="android.permission.START_ACTIVITIES_FROM_BACKGROUND"/>
</privapp-permissions>
</permissions>
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 03f8918..f84d947 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -354,6 +354,7 @@
<permission name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<permission name="android.permission.MOVE_PACKAGE"/>
<!-- Needed for test only -->
+ <permission name="android.permission.RESTART_WIFI_SUBSYSTEM"/>
<permission name="android.permission.NETWORK_AIRPLANE_MODE"/>
<permission name="android.permission.OBSERVE_APP_USAGE"/>
<permission name="android.permission.NETWORK_SCAN"/>
diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java
index c8ff95d..bb795cd 100644
--- a/graphics/java/android/graphics/FontListParser.java
+++ b/graphics/java/android/graphics/FontListParser.java
@@ -51,7 +51,8 @@
XmlPullParser parser = Xml.newPullParser();
parser.setInput(in, null);
parser.nextTag();
- return readFamilies(parser, "/system/fonts/", new FontCustomizationParser.Result(), null);
+ return readFamilies(parser, "/system/fonts/", new FontCustomizationParser.Result(), null,
+ 0, 0);
}
/**
@@ -71,7 +72,9 @@
@NonNull String systemFontDir,
@Nullable String oemCustomizationXmlPath,
@Nullable String productFontDir,
- @Nullable Map<String, File> updatableFontMap
+ @Nullable Map<String, File> updatableFontMap,
+ long lastModifiedDate,
+ int configVersion
) throws IOException, XmlPullParserException {
FontCustomizationParser.Result oemCustomization;
if (oemCustomizationXmlPath != null) {
@@ -90,7 +93,8 @@
XmlPullParser parser = Xml.newPullParser();
parser.setInput(is, null);
parser.nextTag();
- return readFamilies(parser, systemFontDir, oemCustomization, updatableFontMap);
+ return readFamilies(parser, systemFontDir, oemCustomization, updatableFontMap,
+ lastModifiedDate, configVersion);
}
}
@@ -98,7 +102,9 @@
@NonNull XmlPullParser parser,
@NonNull String fontDir,
@NonNull FontCustomizationParser.Result customization,
- @Nullable Map<String, File> updatableFontMap)
+ @Nullable Map<String, File> updatableFontMap,
+ long lastModifiedDate,
+ int configVersion)
throws XmlPullParserException, IOException {
List<FontConfig.FontFamily> families = new ArrayList<>();
List<FontConfig.Alias> aliases = new ArrayList<>(customization.getAdditionalAliases());
@@ -126,7 +132,7 @@
}
families.addAll(oemNamedFamilies.values());
- return new FontConfig(families, aliases);
+ return new FontConfig(families, aliases, lastModifiedDate, configVersion);
}
/**
diff --git a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
index ebf7cea..dd64327 100644
--- a/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedImageDrawable.java
@@ -570,6 +570,13 @@
}
}
+ @Override
+ protected void onBoundsChange(Rect bounds) {
+ if (mState.mNativePtr != 0) {
+ nSetBounds(mState.mNativePtr, bounds);
+ }
+ }
+
private static native long nCreate(long nativeImageDecoder,
@Nullable ImageDecoder decoder, int width, int height, long colorSpaceHandle,
@@ -601,4 +608,6 @@
private static native long nNativeByteSize(long nativePtr);
@FastNative
private static native void nSetMirrored(long nativePtr, boolean mirror);
+ @FastNative
+ private static native void nSetBounds(long nativePtr, Rect rect);
}
diff --git a/graphics/java/android/graphics/fonts/SystemFonts.java b/graphics/java/android/graphics/fonts/SystemFonts.java
index a41215f..c166e12 100644
--- a/graphics/java/android/graphics/fonts/SystemFonts.java
+++ b/graphics/java/android/graphics/fonts/SystemFonts.java
@@ -240,10 +240,12 @@
* @hide
*/
public static @NonNull FontConfig getSystemFontConfig(
- @Nullable Map<String, File> updatableFontMap
+ @Nullable Map<String, File> updatableFontMap,
+ long lastModifiedDate,
+ int configVersion
) {
return getSystemFontConfigInternal(FONTS_XML, SYSTEM_FONT_DIR, OEM_XML, OEM_FONT_DIR,
- updatableFontMap);
+ updatableFontMap, lastModifiedDate, configVersion);
}
/**
@@ -251,7 +253,8 @@
* @hide
*/
public static @NonNull FontConfig getSystemPreinstalledFontConfig() {
- return getSystemFontConfigInternal(FONTS_XML, SYSTEM_FONT_DIR, OEM_XML, OEM_FONT_DIR, null);
+ return getSystemFontConfigInternal(FONTS_XML, SYSTEM_FONT_DIR, OEM_XML, OEM_FONT_DIR, null,
+ 0, 0);
}
/* package */ static @NonNull FontConfig getSystemFontConfigInternal(
@@ -259,17 +262,19 @@
@NonNull String systemFontDir,
@Nullable String oemXml,
@Nullable String productFontDir,
- @Nullable Map<String, File> updatableFontMap
+ @Nullable Map<String, File> updatableFontMap,
+ long lastModifiedDate,
+ int configVersion
) {
try {
return FontListParser.parse(fontsXml, systemFontDir, oemXml, productFontDir,
- updatableFontMap);
+ updatableFontMap, lastModifiedDate, configVersion);
} catch (IOException e) {
Log.e(TAG, "Failed to open/read system font configurations.", e);
- return new FontConfig(Collections.emptyList(), Collections.emptyList());
+ return new FontConfig(Collections.emptyList(), Collections.emptyList(), 0, 0);
} catch (XmlPullParserException e) {
Log.e(TAG, "Failed to parse the system font configuration.", e);
- return new FontConfig(Collections.emptyList(), Collections.emptyList());
+ return new FontConfig(Collections.emptyList(), Collections.emptyList(), 0, 0);
}
}
diff --git a/keystore/java/android/security/KeyStore2.java b/keystore/java/android/security/KeyStore2.java
index 92d87aa..f7477bf 100644
--- a/keystore/java/android/security/KeyStore2.java
+++ b/keystore/java/android/security/KeyStore2.java
@@ -23,6 +23,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
+import android.security.keymaster.KeymasterDefs;
import android.system.keystore2.IKeystoreService;
import android.system.keystore2.KeyDescriptor;
import android.system.keystore2.KeyEntryResponse;
@@ -107,7 +108,7 @@
return request.execute(service);
} catch (ServiceSpecificException e) {
Log.e(TAG, "KeyStore exception", e);
- throw new KeyStoreException(e.errorCode, "");
+ throw getKeyStoreException(e.errorCode);
} catch (RemoteException e) {
if (firstTry) {
Log.w(TAG, "Looks like we may have lost connection to the Keystore "
@@ -274,4 +275,40 @@
}
}
+ static KeyStoreException getKeyStoreException(int errorCode) {
+ if (errorCode > 0) {
+ // KeyStore layer error
+ switch (errorCode) {
+ case ResponseCode.LOCKED:
+ return new KeyStoreException(errorCode, "User authentication required");
+ case ResponseCode.UNINITIALIZED:
+ return new KeyStoreException(errorCode, "Keystore not initialized");
+ case ResponseCode.SYSTEM_ERROR:
+ return new KeyStoreException(errorCode, "System error");
+ case ResponseCode.PERMISSION_DENIED:
+ return new KeyStoreException(errorCode, "Permission denied");
+ case ResponseCode.KEY_NOT_FOUND:
+ return new KeyStoreException(errorCode, "Key not found");
+ case ResponseCode.VALUE_CORRUPTED:
+ return new KeyStoreException(errorCode, "Key blob corrupted");
+ case ResponseCode.KEY_PERMANENTLY_INVALIDATED:
+ return new KeyStoreException(errorCode, "Key permanently invalidated");
+ default:
+ return new KeyStoreException(errorCode, String.valueOf(errorCode));
+ }
+ } else {
+ // Keymaster layer error
+ switch (errorCode) {
+ case KeymasterDefs.KM_ERROR_INVALID_AUTHORIZATION_TIMEOUT:
+ // The name of this parameter significantly differs between Keymaster and
+ // framework APIs. Use the framework wording to make life easier for developers.
+ return new KeyStoreException(errorCode,
+ "Invalid user authentication validity duration");
+ default:
+ return new KeyStoreException(errorCode,
+ KeymasterDefs.getErrorMessage(errorCode));
+ }
+ }
+ }
+
}
diff --git a/keystore/java/android/security/KeyStoreOperation.java b/keystore/java/android/security/KeyStoreOperation.java
index 7ea9e14..a6552dd 100644
--- a/keystore/java/android/security/KeyStoreOperation.java
+++ b/keystore/java/android/security/KeyStoreOperation.java
@@ -73,8 +73,7 @@
);
}
default:
- // TODO Human readable string. Use something like KeyStore.getKeyStoreException
- throw new KeyStoreException(e.errorCode, "");
+ throw KeyStore2.getKeyStoreException(e.errorCode);
}
} catch (RemoteException e) {
// Log exception and report invalid operation handle.
diff --git a/keystore/java/android/security/KeyStoreSecurityLevel.java b/keystore/java/android/security/KeyStoreSecurityLevel.java
index 3ef4aa5..372add9 100644
--- a/keystore/java/android/security/KeyStoreSecurityLevel.java
+++ b/keystore/java/android/security/KeyStoreSecurityLevel.java
@@ -52,7 +52,7 @@
try {
return request.execute();
} catch (ServiceSpecificException e) {
- throw new KeyStoreException(e.errorCode, "");
+ throw KeyStore2.getKeyStoreException(e.errorCode);
} catch (RemoteException e) {
// Log exception and report invalid operation handle.
// This should prompt the caller drop the reference to this operation and retry.
@@ -96,25 +96,26 @@
} catch (ServiceSpecificException e) {
switch (e.errorCode) {
case ResponseCode.BACKEND_BUSY: {
+ long backOffHint = (long) (Math.random() * 80 + 20);
if (CompatChanges.isChangeEnabled(
KeyStore2.KEYSTORE_OPERATION_CREATION_MAY_FAIL)) {
// Starting with Android S we inform the caller about the
// backend being busy.
- throw new BackendBusyException();
+ throw new BackendBusyException(backOffHint);
} else {
// Before Android S operation creation must always succeed. So we
// just have to retry. We do so with a randomized back-off between
- // 50 and 250ms.
+ // 20 and 100ms.
// It is a little awkward that we cannot break out of this loop
// by interrupting this thread. But that is the expected behavior.
// There is some comfort in the fact that interrupting a thread
// also does not unblock a thread waiting for a binder transaction.
- interruptedPreservingSleep((long) (Math.random() * 200 + 50));
+ interruptedPreservingSleep(backOffHint);
}
break;
}
default:
- throw new KeyStoreException(e.errorCode, "");
+ throw KeyStore2.getKeyStoreException(e.errorCode);
}
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
diff --git a/keystore/java/android/security/keystore/BackendBusyException.java b/keystore/java/android/security/keystore/BackendBusyException.java
index 1a88469..a813e93 100644
--- a/keystore/java/android/security/keystore/BackendBusyException.java
+++ b/keystore/java/android/security/keystore/BackendBusyException.java
@@ -16,37 +16,66 @@
package android.security.keystore;
+import android.annotation.DurationMillisLong;
import android.annotation.NonNull;
import java.security.ProviderException;
/**
* Indicates a transient error that prevented a key operation from being created.
- * Callers should try again with a back-off period of 10-30 milliseconds.
+ * Callers should try again with a back-off period of {@link #getBackOffHintMillis()}
+ * milliseconds.
*/
public class BackendBusyException extends ProviderException {
+ private final long mBackOffHintMillis;
+
/**
* Constructs a new {@code BackendBusyException} without detail message and cause.
+ *
*/
- public BackendBusyException() {
+ public BackendBusyException(@DurationMillisLong long backOffHintMillis) {
super("The keystore backend has no operation slots available. Retry later.");
+ if (backOffHintMillis < 0) {
+ throw new IllegalArgumentException("Back-off hint cannot be negative.");
+ }
+ mBackOffHintMillis = backOffHintMillis;
}
/**
* Constructs a new {@code BackendBusyException} with the provided detail message and
* no cause.
*/
- public BackendBusyException(@NonNull String message) {
+ public BackendBusyException(@DurationMillisLong long backOffHintMillis,
+ @NonNull String message) {
super(message);
+ if (backOffHintMillis < 0) {
+ throw new IllegalArgumentException("Back-off hint cannot be negative.");
+ }
+ mBackOffHintMillis = backOffHintMillis;
}
/**
* Constructs a new {@code BackendBusyException} with the provided detail message and
* cause.
*/
- public BackendBusyException(@NonNull String message, @NonNull Throwable cause) {
+ public BackendBusyException(@DurationMillisLong long backOffHintMillis,
+ @NonNull String message, @NonNull Throwable cause) {
super(message, cause);
+ if (backOffHintMillis < 0) {
+ throw new IllegalArgumentException("Back-off hint cannot be negative.");
+ }
+ mBackOffHintMillis = backOffHintMillis;
}
+ /**
+ * When retrying to start a Keystore operation after receiving this exception, this can be
+ * used to determine how long to wait before retrying. It is not guaranteed that the operation
+ * will succeeds after this time. Multiple retries may be necessary if the system is congested.
+ *
+ * @return Number of milliseconds to back off before retrying.
+ */
+ public @DurationMillisLong long getBackOffHintMillis() {
+ return mBackOffHintMillis;
+ }
}
diff --git a/keystore/java/android/security/keystore/KeyProperties.java b/keystore/java/android/security/keystore/KeyProperties.java
index 5928540..014d688 100644
--- a/keystore/java/android/security/keystore/KeyProperties.java
+++ b/keystore/java/android/security/keystore/KeyProperties.java
@@ -67,6 +67,7 @@
PURPOSE_SIGN,
PURPOSE_VERIFY,
PURPOSE_WRAP_KEY,
+ PURPOSE_AGREE_KEY,
})
public @interface PurposeEnum {}
@@ -96,6 +97,11 @@
public static final int PURPOSE_WRAP_KEY = 1 << 5;
/**
+ * Purpose of key: creating a shared ECDH secret through key agreement.
+ */
+ public static final int PURPOSE_AGREE_KEY = 1 << 6;
+
+ /**
* @hide
*/
public static abstract class Purpose {
@@ -113,6 +119,8 @@
return KeymasterDefs.KM_PURPOSE_VERIFY;
case PURPOSE_WRAP_KEY:
return KeymasterDefs.KM_PURPOSE_WRAP;
+ case PURPOSE_AGREE_KEY:
+ return KeymasterDefs.KM_PURPOSE_AGREE_KEY;
default:
throw new IllegalArgumentException("Unknown purpose: " + purpose);
}
@@ -130,6 +138,8 @@
return PURPOSE_VERIFY;
case KeymasterDefs.KM_PURPOSE_WRAP:
return PURPOSE_WRAP_KEY;
+ case KeymasterDefs.KM_PURPOSE_AGREE_KEY:
+ return PURPOSE_AGREE_KEY;
default:
throw new IllegalArgumentException("Unknown purpose: " + purpose);
}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyAgreementSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyAgreementSpi.java
new file mode 100644
index 0000000..fc963a8
--- /dev/null
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyAgreementSpi.java
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.keystore2;
+
+import android.hardware.security.keymint.Algorithm;
+import android.hardware.security.keymint.KeyParameter;
+import android.hardware.security.keymint.KeyPurpose;
+import android.hardware.security.keymint.Tag;
+import android.security.KeyStoreException;
+import android.security.KeyStoreOperation;
+import android.security.keystore.KeyStoreCryptoOperation;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.NoSuchAlgorithmException;
+import java.security.ProviderException;
+import java.security.PublicKey;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.crypto.KeyAgreementSpi;
+import javax.crypto.SecretKey;
+import javax.crypto.ShortBufferException;
+import javax.crypto.spec.SecretKeySpec;
+
+/**
+ * {@link KeyAgreementSpi} which provides an ECDH implementation backed by Android KeyStore.
+ *
+ * @hide
+ */
+public class AndroidKeyStoreKeyAgreementSpi extends KeyAgreementSpi
+ implements KeyStoreCryptoOperation {
+
+ private static final String TAG = "AndroidKeyStoreKeyAgreementSpi";
+
+ /**
+ * ECDH implementation.
+ *
+ * @hide
+ */
+ public static class ECDH extends AndroidKeyStoreKeyAgreementSpi {
+ public ECDH() {
+ super(Algorithm.EC);
+ }
+ }
+
+ private final int mKeymintAlgorithm;
+
+ // Fields below are populated by engineInit and should be preserved after engineDoFinal.
+ private AndroidKeyStorePrivateKey mKey;
+ private PublicKey mOtherPartyKey;
+
+ // Fields below are reset when engineDoFinal succeeds.
+ private KeyStoreOperation mOperation;
+ private long mOperationHandle;
+
+ protected AndroidKeyStoreKeyAgreementSpi(int keymintAlgorithm) {
+ resetAll();
+
+ mKeymintAlgorithm = keymintAlgorithm;
+ }
+
+ @Override
+ protected void engineInit(Key key, SecureRandom random) throws InvalidKeyException {
+ resetAll();
+
+ if (key == null) {
+ throw new InvalidKeyException("key == null");
+ } else if (!(key instanceof AndroidKeyStorePrivateKey)) {
+ throw new InvalidKeyException(
+ "Only Android KeyStore private keys supported. Key: " + key);
+ }
+ // Checking the correct KEY_PURPOSE and algorithm is done by the Keymint implementation in
+ // ensureKeystoreOperationInitialized() below.
+ mKey = (AndroidKeyStorePrivateKey) key;
+
+ boolean success = false;
+ try {
+ ensureKeystoreOperationInitialized();
+ success = true;
+ } finally {
+ if (!success) {
+ resetAll();
+ }
+ }
+ }
+
+ @Override
+ protected void engineInit(Key key, AlgorithmParameterSpec params, SecureRandom random)
+ throws InvalidKeyException, InvalidAlgorithmParameterException {
+ if (params != null) {
+ throw new InvalidAlgorithmParameterException(
+ "Unsupported algorithm parameters: " + params);
+ }
+ engineInit(key, random);
+ }
+
+ @Override
+ protected Key engineDoPhase(Key key, boolean lastPhase)
+ throws InvalidKeyException, IllegalStateException {
+ ensureKeystoreOperationInitialized();
+
+ if (key == null) {
+ throw new InvalidKeyException("key == null");
+ } else if (!(key instanceof PublicKey)) {
+ throw new InvalidKeyException("Only public keys supported. Key: " + key);
+ } else if (!lastPhase) {
+ throw new IllegalStateException(
+ "Only one other party supported. lastPhase must be set to true.");
+ } else if (mOtherPartyKey != null) {
+ throw new IllegalStateException(
+ "Only one other party supported. doPhase() must only be called exactly once.");
+ }
+ // The other party key will be passed as part of the doFinal() call, to prevent an
+ // additional IPC.
+ mOtherPartyKey = (PublicKey) key;
+
+ return null; // No intermediate key
+ }
+
+ @Override
+ protected byte[] engineGenerateSecret() throws IllegalStateException {
+ try {
+ ensureKeystoreOperationInitialized();
+ } catch (InvalidKeyException e) {
+ throw new IllegalStateException("Not initialized", e);
+ }
+
+ if (mOtherPartyKey == null) {
+ throw new IllegalStateException("Other party key not provided. Call doPhase() first.");
+ }
+ byte[] otherPartyKeyEncoded = mOtherPartyKey.getEncoded();
+
+ try {
+ return mOperation.finish(otherPartyKeyEncoded, null);
+ } catch (KeyStoreException e) {
+ throw new ProviderException("Keystore operation failed", e);
+ } finally {
+ resetWhilePreservingInitState();
+ }
+ }
+
+ @Override
+ protected SecretKey engineGenerateSecret(String algorithm)
+ throws IllegalStateException, NoSuchAlgorithmException, InvalidKeyException {
+ byte[] generatedSecret = engineGenerateSecret();
+
+ return new SecretKeySpec(generatedSecret, algorithm);
+ }
+
+ @Override
+ protected int engineGenerateSecret(byte[] sharedSecret, int offset)
+ throws IllegalStateException, ShortBufferException {
+ byte[] generatedSecret = engineGenerateSecret();
+
+ if (generatedSecret.length > sharedSecret.length - offset) {
+ throw new ShortBufferException("Needed: " + generatedSecret.length);
+ }
+ System.arraycopy(generatedSecret, 0, sharedSecret, offset, generatedSecret.length);
+ return generatedSecret.length;
+ }
+
+ @Override
+ public long getOperationHandle() {
+ return mOperationHandle;
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ resetAll();
+ } finally {
+ super.finalize();
+ }
+ }
+
+ private void resetWhilePreservingInitState() {
+ KeyStoreCryptoOperationUtils.abortOperation(mOperation);
+ mOperationHandle = 0;
+ mOperation = null;
+ mOtherPartyKey = null;
+ }
+
+ private void resetAll() {
+ resetWhilePreservingInitState();
+ mKey = null;
+ }
+
+ private void ensureKeystoreOperationInitialized()
+ throws InvalidKeyException, IllegalStateException {
+ if (mKey == null) {
+ throw new IllegalStateException("Not initialized");
+ }
+ if (mOperation != null) {
+ return;
+ }
+
+ // We don't need to explicitly pass in any other parameters here, as they're part of the
+ // private key that is available to Keymint.
+ List<KeyParameter> parameters = new ArrayList<>();
+ parameters.add(KeyStore2ParameterUtils.makeEnum(
+ Tag.PURPOSE, KeyPurpose.AGREE_KEY
+ ));
+
+ try {
+ mOperation =
+ mKey.getSecurityLevel().createOperation(mKey.getKeyIdDescriptor(), parameters);
+ } catch (KeyStoreException keyStoreException) {
+ // If necessary, throw an exception due to KeyStore operation having failed.
+ InvalidKeyException e =
+ KeyStoreCryptoOperationUtils.getInvalidKeyException(mKey, keyStoreException);
+ if (e != null) {
+ throw e;
+ }
+ }
+
+ // Set the operation handle. This will be a random number, or the operation challenge if
+ // user authentication is required. If we got a challenge we check if the authorization can
+ // possibly succeed.
+ mOperationHandle =
+ KeyStoreCryptoOperationUtils.getOrMakeOperationChallenge(mOperation, mKey);
+ }
+}
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
index 403da18..164bc86 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java
@@ -94,6 +94,9 @@
put("KeyGenerator.DESede", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$DESede");
}
+ // javax.crypto.KeyAgreement
+ put("KeyAgreement.ECDH", PACKAGE_NAME + ".AndroidKeyStoreKeyAgreementSpi$ECDH");
+
// java.security.SecretKeyFactory
putSecretKeyFactoryImpl("AES");
if (supports3DES) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
index 3944128..a2cd683 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java
@@ -42,10 +42,11 @@
import androidx.annotation.BinderThread;
import androidx.annotation.VisibleForTesting;
+import com.android.internal.inputmethod.Completable;
+import com.android.internal.inputmethod.ResultCallbacks;
import com.android.internal.view.IInputMethodManager;
import java.util.ArrayList;
-import java.util.Objects;
import java.util.concurrent.Executor;
/**
@@ -207,23 +208,21 @@
}
protected void insetsChanged(InsetsState insetsState) {
- mMainExecutor.execute(() -> {
- if (mInsetsState.equals(insetsState)) {
- return;
- }
+ if (mInsetsState.equals(insetsState)) {
+ return;
+ }
- mImeShowing = insetsState.getSourceOrDefaultVisibility(InsetsState.ITYPE_IME);
+ mImeShowing = insetsState.getSourceOrDefaultVisibility(InsetsState.ITYPE_IME);
- final InsetsSource newSource = insetsState.getSource(InsetsState.ITYPE_IME);
- final Rect newFrame = newSource.getFrame();
- final Rect oldFrame = mInsetsState.getSource(InsetsState.ITYPE_IME).getFrame();
+ final InsetsSource newSource = insetsState.getSource(InsetsState.ITYPE_IME);
+ final Rect newFrame = newSource.getFrame();
+ final Rect oldFrame = mInsetsState.getSource(InsetsState.ITYPE_IME).getFrame();
- mInsetsState.set(insetsState, true /* copySources */);
- if (mImeShowing && !newFrame.equals(oldFrame) && newSource.isVisible()) {
- if (DEBUG) Slog.d(TAG, "insetsChanged when IME showing, restart animation");
- startAnimation(mImeShowing, true /* forceRestart */);
- }
- });
+ mInsetsState.set(insetsState, true /* copySources */);
+ if (mImeShowing && !newFrame.equals(oldFrame) && newSource.isVisible()) {
+ if (DEBUG) Slog.d(TAG, "insetsChanged when IME showing, restart animation");
+ startAnimation(mImeShowing, true /* forceRestart */);
+ }
}
@VisibleForTesting
@@ -236,27 +235,25 @@
continue;
}
if (activeControl.getType() == InsetsState.ITYPE_IME) {
- mMainExecutor.execute(() -> {
- final Point lastSurfacePosition = mImeSourceControl != null
- ? mImeSourceControl.getSurfacePosition() : null;
- final boolean positionChanged =
- !activeControl.getSurfacePosition().equals(lastSurfacePosition);
- final boolean leashChanged =
- !haveSameLeash(mImeSourceControl, activeControl);
- mImeSourceControl = activeControl;
- if (mAnimation != null) {
- if (positionChanged) {
- startAnimation(mImeShowing, true /* forceRestart */);
- }
- } else {
- if (leashChanged) {
- applyVisibilityToLeash();
- }
- if (!mImeShowing) {
- removeImeSurface();
- }
+ final Point lastSurfacePosition = mImeSourceControl != null
+ ? mImeSourceControl.getSurfacePosition() : null;
+ final boolean positionChanged =
+ !activeControl.getSurfacePosition().equals(lastSurfacePosition);
+ final boolean leashChanged =
+ !haveSameLeash(mImeSourceControl, activeControl);
+ mImeSourceControl = activeControl;
+ if (mAnimation != null) {
+ if (positionChanged) {
+ startAnimation(mImeShowing, true /* forceRestart */);
}
- });
+ } else {
+ if (leashChanged) {
+ applyVisibilityToLeash();
+ }
+ if (!mImeShowing) {
+ removeImeSurface();
+ }
+ }
}
}
}
@@ -281,7 +278,7 @@
return;
}
if (DEBUG) Slog.d(TAG, "Got showInsets for ime");
- mMainExecutor.execute(() -> startAnimation(true /* show */, false /* forceRestart */));
+ startAnimation(true /* show */, false /* forceRestart */);
}
@@ -290,7 +287,7 @@
return;
}
if (DEBUG) Slog.d(TAG, "Got hideInsets for ime");
- mMainExecutor.execute(() -> startAnimation(false /* show */, false /* forceRestart */));
+ startAnimation(false /* show */, false /* forceRestart */);
}
public void topFocusedWindowChanged(String packageName) {
@@ -506,7 +503,9 @@
try {
// Remove the IME surface to make the insets invisible for
// non-client controlled insets.
- imms.removeImeSurface();
+ final Completable.Void value = Completable.createVoid();
+ imms.removeImeSurface(ResultCallbacks.of(value));
+ Completable.getResult(value);
} catch (RemoteException e) {
Slog.e(TAG, "Failed to remove IME surface.", e);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
index aeea10d..90992fb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
@@ -469,6 +469,11 @@
getSurfaceTransactionHelper()
.alpha(tx, leash, 1f)
.round(tx, leash, shouldApplyCornerRadius());
+ // TODO(b/178632364): this is a work around for the black background when
+ // entering PiP in buttion navigation mode.
+ if (isInPipDirection(direction)) {
+ tx.setWindowCrop(leash, getStartValue());
+ }
tx.show(leash);
tx.apply();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
index f839727..a8961ea 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsAlgorithm.java
@@ -30,6 +30,8 @@
import android.view.DisplayInfo;
import android.view.Gravity;
+import com.android.wm.shell.common.DisplayLayout;
+
import java.io.PrintWriter;
/**
@@ -190,9 +192,9 @@
size = adjustSizeToAspectRatio(overrideMinSize, aspectRatio);
} else {
// Calculate the default size using the display size and default min edge size.
- final DisplayInfo displayInfo = mPipBoundsState.getDisplayInfo();
+ final DisplayLayout displayLayout = mPipBoundsState.getDisplayLayout();
size = getSizeForAspectRatio(aspectRatio, mDefaultMinSize,
- displayInfo.logicalWidth, displayInfo.logicalHeight);
+ displayLayout.width(), displayLayout.height());
}
}
@@ -232,7 +234,7 @@
final Size defaultSize;
final Rect insetBounds = new Rect();
getInsetBounds(insetBounds);
- final DisplayInfo displayInfo = mPipBoundsState.getDisplayInfo();
+ final DisplayLayout displayLayout = mPipBoundsState.getDisplayLayout();
final Size overrideMinSize = mPipBoundsState.getOverrideMinSize();
if (overrideMinSize != null) {
// The override minimal size is set, use that as the default size making sure it's
@@ -241,7 +243,7 @@
} else {
// Calculate the default size using the display size and default min edge size.
defaultSize = getSizeForAspectRatio(mDefaultAspectRatio,
- mDefaultMinSize, displayInfo.logicalWidth, displayInfo.logicalHeight);
+ mDefaultMinSize, displayLayout.width(), displayLayout.height());
}
// Now that we have the default size, apply the snap fraction if valid or position the
@@ -264,12 +266,12 @@
* Populates the bounds on the screen that the PIP can be visible in.
*/
public void getInsetBounds(Rect outRect) {
- final DisplayInfo displayInfo = mPipBoundsState.getDisplayInfo();
+ final DisplayLayout displayLayout = mPipBoundsState.getDisplayLayout();
Rect insets = mPipBoundsState.getDisplayLayout().stableInsets();
outRect.set(insets.left + mScreenEdgeInsets.x,
insets.top + mScreenEdgeInsets.y,
- displayInfo.logicalWidth - insets.right - mScreenEdgeInsets.x,
- displayInfo.logicalHeight - insets.bottom - mScreenEdgeInsets.y);
+ displayLayout.width() - insets.right - mScreenEdgeInsets.x,
+ displayLayout.height() - insets.bottom - mScreenEdgeInsets.y);
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
index 9595b5a..b112c51 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
@@ -24,6 +24,7 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.util.Size;
+import android.view.Display;
import android.view.DisplayInfo;
import com.android.internal.annotations.VisibleForTesting;
@@ -68,7 +69,7 @@
private int mStashOffset;
private @Nullable PipReentryState mPipReentryState;
private @Nullable ComponentName mLastPipComponentName;
- private final @NonNull DisplayInfo mDisplayInfo = new DisplayInfo();
+ private int mDisplayId = Display.DEFAULT_DISPLAY;
private final @NonNull DisplayLayout mDisplayLayout = new DisplayLayout();
/** The current minimum edge size of PIP. */
private int mMinEdgeSize;
@@ -238,26 +239,20 @@
return mLastPipComponentName;
}
- /** Get the current display info. */
- @NonNull
- public DisplayInfo getDisplayInfo() {
- return mDisplayInfo;
+ /** Get the current display id. */
+ public int getDisplayId() {
+ return mDisplayId;
}
- /** Update the display info. */
- public void setDisplayInfo(@NonNull DisplayInfo displayInfo) {
- mDisplayInfo.copyFrom(displayInfo);
- }
-
- /** Set the rotation of the display. */
- public void setDisplayRotation(int rotation) {
- mDisplayInfo.rotation = rotation;
+ /** Set the current display id for the associated display layout. */
+ public void setDisplayId(int displayId) {
+ mDisplayId = displayId;
}
/** Returns the display's bounds. */
@NonNull
public Rect getDisplayBounds() {
- return new Rect(0, 0, mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight);
+ return new Rect(0, 0, mDisplayLayout.width(), mDisplayLayout.height());
}
/** Update the display layout. */
@@ -474,7 +469,7 @@
pw.println(innerPrefix + "mExpandedMovementBounds=" + mExpandedMovementBounds);
pw.println(innerPrefix + "mLastPipComponentName=" + mLastPipComponentName);
pw.println(innerPrefix + "mAspectRatio=" + mAspectRatio);
- pw.println(innerPrefix + "mDisplayInfo=" + mDisplayInfo);
+ pw.println(innerPrefix + "mDisplayId=" + mDisplayId);
pw.println(innerPrefix + "mDisplayLayout=" + mDisplayLayout);
pw.println(innerPrefix + "mStashedState=" + mStashedState);
pw.println(innerPrefix + "mStashOffset=" + mStashOffset);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index b80f285..b7958b7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -55,6 +55,7 @@
import android.util.Log;
import android.util.Rational;
import android.util.Size;
+import android.view.Display;
import android.view.SurfaceControl;
import android.window.TaskOrganizer;
import android.window.WindowContainerToken;
@@ -324,7 +325,7 @@
mPipUiEventLoggerLogger.log(
PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_EXPAND_TO_FULLSCREEN);
final boolean orientationDiffers = initialConfig.windowConfiguration.getRotation()
- != mPipBoundsState.getDisplayInfo().rotation;
+ != mPipBoundsState.getDisplayLayout().rotation();
final WindowContainerTransaction wct = new WindowContainerTransaction();
final Rect destinationBounds = initialConfig.windowConfiguration.getBounds();
final int direction = syncWithSplitScreenBounds(destinationBounds)
@@ -437,7 +438,7 @@
// If the displayId of the task is different than what PipBoundsHandler has, then update
// it. This is possible if we entered PiP on an external display.
- if (info.displayId != mPipBoundsState.getDisplayInfo().displayId
+ if (info.displayId != mPipBoundsState.getDisplayId()
&& mOnDisplayIdChangeCallback != null) {
mOnDisplayIdChangeCallback.accept(info.displayId);
}
@@ -605,6 +606,10 @@
mState = State.UNDEFINED;
mPipUiEventLoggerLogger.setTaskInfo(null);
mPipMenuController.detach();
+
+ if (info.displayId != Display.DEFAULT_DISPLAY && mOnDisplayIdChangeCallback != null) {
+ mOnDisplayIdChangeCallback.accept(Display.DEFAULT_DISPLAY);
+ }
}
@Override
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 cefeb939..c06f9d0 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
@@ -19,8 +19,6 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
-import static android.view.Surface.ROTATION_0;
-import static android.view.Surface.ROTATION_180;
import static android.view.WindowManager.INPUT_CONSUMER_PIP;
import static com.android.wm.shell.pip.PipAnimationController.isOutPipDirection;
@@ -86,7 +84,6 @@
private PipTouchHandler mTouchHandler;
protected final PipImpl mImpl = new PipImpl();
- private final DisplayInfo mTmpDisplayInfo = new DisplayInfo();
private final Rect mTmpInsetBounds = new Rect();
private boolean mIsInFixedRotation;
@@ -145,7 +142,7 @@
}
};
- private final DisplayController.OnDisplaysChangedListener mFixedRotationListener =
+ private final DisplayController.OnDisplaysChangedListener mDisplaysChangedListener =
new DisplayController.OnDisplaysChangedListener() {
@Override
public void onFixedRotationStarted(int displayId, int newRotation) {
@@ -159,8 +156,20 @@
@Override
public void onDisplayAdded(int displayId) {
- mPipBoundsState.setDisplayLayout(
- mDisplayController.getDisplayLayout(displayId));
+ if (displayId != mPipBoundsState.getDisplayId()) {
+ return;
+ }
+ onDisplayChanged(mDisplayController.getDisplayLayout(displayId),
+ false /* saveRestoreSnapFraction */);
+ }
+
+ @Override
+ public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {
+ if (displayId != mPipBoundsState.getDisplayId()) {
+ return;
+ }
+ onDisplayChanged(mDisplayController.getDisplayLayout(displayId),
+ true /* saveRestoreSnapFraction */);
}
};
@@ -261,12 +270,9 @@
INPUT_CONSUMER_PIP, mainExecutor);
mPipTaskOrganizer.registerPipTransitionCallback(this);
mPipTaskOrganizer.registerOnDisplayIdChangeCallback((int displayId) -> {
- final DisplayInfo newDisplayInfo = new DisplayInfo();
- displayController.getDisplay(displayId).getDisplayInfo(newDisplayInfo);
- mPipBoundsState.setDisplayInfo(newDisplayInfo);
- updateMovementBounds(null /* toBounds */, false /* fromRotation */,
- false /* fromImeAdjustment */, false /* fromShelfAdjustment */,
- null /* wct */);
+ mPipBoundsState.setDisplayId(displayId);
+ onDisplayChanged(displayController.getDisplayLayout(displayId),
+ false /* saveRestoreSnapFraction */);
});
mPipBoundsState.setOnMinimalSizeChangeCallback(
() -> {
@@ -291,13 +297,12 @@
mPipInputConsumer.setRegistrationListener(mTouchHandler::onRegistrationChanged);
}
displayController.addDisplayChangingController(mRotationController);
- displayController.addDisplayWindowListener(mFixedRotationListener);
+ displayController.addDisplayWindowListener(mDisplaysChangedListener);
// Ensure that we have the display info in case we get calls to update the bounds before the
// listener calls back
- final DisplayInfo displayInfo = new DisplayInfo();
- context.getDisplay().getDisplayInfo(displayInfo);
- mPipBoundsState.setDisplayInfo(displayInfo);
+ mPipBoundsState.setDisplayId(context.getDisplayId());
+ mPipBoundsState.setDisplayLayout(new DisplayLayout(context, context.getDisplay()));
try {
mWindowManagerShellWrapper.addPinnedStackListener(mPinnedStackListener);
@@ -363,11 +368,42 @@
}
private void onOverlayChanged() {
- mPipBoundsState.setDisplayLayout(new DisplayLayout(mContext, mContext.getDisplay()));
- updateMovementBounds(null /* toBounds */,
- false /* fromRotation */, false /* fromImeAdjustment */,
- false /* fromShelfAdjustment */,
- null /* windowContainerTransaction */);
+ onDisplayChanged(new DisplayLayout(mContext, mContext.getDisplay()),
+ false /* saveRestoreSnapFraction */);
+ }
+
+ private void onDisplayChanged(DisplayLayout layout, boolean saveRestoreSnapFraction) {
+ Runnable updateDisplayLayout = () -> {
+ mPipBoundsState.setDisplayLayout(layout);
+ updateMovementBounds(null /* toBounds */,
+ false /* fromRotation */, false /* fromImeAdjustment */,
+ false /* fromShelfAdjustment */,
+ null /* windowContainerTransaction */);
+ };
+
+ if (saveRestoreSnapFraction) {
+ // Calculate the snap fraction of the current stack along the old movement bounds
+ final PipSnapAlgorithm pipSnapAlgorithm = mPipBoundsAlgorithm.getSnapAlgorithm();
+ final Rect postChangeStackBounds = new Rect(mPipBoundsState.getBounds());
+ final float snapFraction = pipSnapAlgorithm.getSnapFraction(postChangeStackBounds,
+ mPipBoundsAlgorithm.getMovementBounds(postChangeStackBounds),
+ mPipBoundsState.getStashedState());
+
+ updateDisplayLayout.run();
+
+ // Calculate the stack bounds in the new orientation based on same fraction along the
+ // rotated movement bounds.
+ final Rect postChangeMovementBounds = mPipBoundsAlgorithm.getMovementBounds(
+ postChangeStackBounds, false /* adjustForIme */);
+ pipSnapAlgorithm.applySnapFraction(postChangeStackBounds, postChangeMovementBounds,
+ snapFraction, mPipBoundsState.getStashedState(),
+ mPipBoundsState.getStashOffset(),
+ mPipBoundsState.getDisplayBounds());
+
+ mTouchHandler.getMotionHelper().movePip(postChangeStackBounds);
+ } else {
+ updateDisplayLayout.run();
+ }
}
private void registerSessionListenerForCurrentUser() {
@@ -500,7 +536,7 @@
// Populate inset / normal bounds and DisplayInfo from mPipBoundsHandler before
// passing to mTouchHandler/mPipTaskOrganizer
final Rect outBounds = new Rect(toBounds);
- mTmpDisplayInfo.copyFrom(mPipBoundsState.getDisplayInfo());
+ final int rotation = mPipBoundsState.getDisplayLayout().rotation();
mPipBoundsAlgorithm.getInsetBounds(mTmpInsetBounds);
mPipBoundsState.setNormalBounds(mPipBoundsAlgorithm.getNormalBounds());
@@ -512,8 +548,7 @@
mPipTaskOrganizer.onMovementBoundsChanged(outBounds, fromRotation, fromImeAdjustment,
fromShelfAdjustment, wct);
mTouchHandler.onMovementBoundsChanged(mTmpInsetBounds, mPipBoundsState.getNormalBounds(),
- outBounds, fromImeAdjustment, fromShelfAdjustment,
- mTmpDisplayInfo.rotation);
+ outBounds, fromImeAdjustment, fromShelfAdjustment, rotation);
}
/**
@@ -525,13 +560,6 @@
// Update the display layout, note that we have to do this on every rotation even if we
// aren't in PIP since we need to update the display layout to get the right resources
mPipBoundsState.getDisplayLayout().rotateTo(context.getResources(), toRotation);
-
- // Populate the new {@link #mDisplayInfo}.
- // The {@link DisplayInfo} queried from DisplayManager would be the one before rotation,
- // therefore, the width/height may require a swap first.
- // Moving forward, we should get the new dimensions after rotation from DisplayLayout.
- mPipBoundsState.setDisplayRotation(toRotation);
- updateDisplayInfoIfNeeded();
}
/**
@@ -543,9 +571,8 @@
private boolean onDisplayRotationChanged(Context context, Rect outBounds, Rect oldBounds,
Rect outInsetBounds,
int displayId, int fromRotation, int toRotation, WindowContainerTransaction t) {
- // Bail early if the event is not sent to current {@link #mDisplayInfo}
- if ((displayId != mPipBoundsState.getDisplayInfo().displayId)
- || (fromRotation == toRotation)) {
+ // Bail early if the event is not sent to current display
+ if ((displayId != mPipBoundsState.getDisplayId()) || (fromRotation == toRotation)) {
return false;
}
@@ -570,13 +597,6 @@
// Update the display layout
mPipBoundsState.getDisplayLayout().rotateTo(context.getResources(), toRotation);
- // Populate the new {@link #mDisplayInfo}.
- // The {@link DisplayInfo} queried from DisplayManager would be the one before rotation,
- // therefore, the width/height may require a swap first.
- // Moving forward, we should get the new dimensions after rotation from DisplayLayout.
- mPipBoundsState.getDisplayInfo().rotation = toRotation;
- updateDisplayInfoIfNeeded();
-
// Calculate the stack bounds in the new orientation based on same fraction along the
// rotated movement bounds.
final Rect postChangeMovementBounds = mPipBoundsAlgorithm.getMovementBounds(
@@ -591,21 +611,6 @@
return true;
}
- private void updateDisplayInfoIfNeeded() {
- final DisplayInfo displayInfo = mPipBoundsState.getDisplayInfo();
- final boolean updateNeeded;
- if ((displayInfo.rotation == ROTATION_0) || (displayInfo.rotation == ROTATION_180)) {
- updateNeeded = (displayInfo.logicalWidth > displayInfo.logicalHeight);
- } else {
- updateNeeded = (displayInfo.logicalWidth < displayInfo.logicalHeight);
- }
- if (updateNeeded) {
- final int newLogicalHeight = displayInfo.logicalWidth;
- displayInfo.logicalWidth = displayInfo.logicalHeight;
- displayInfo.logicalHeight = newLogicalHeight;
- }
- }
-
private void dump(PrintWriter pw) {
final String innerPrefix = " ";
pw.println(TAG);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
index 61cf22b..75fc9f5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java
@@ -36,6 +36,7 @@
import com.android.wm.shell.R;
import com.android.wm.shell.WindowManagerShellWrapper;
+import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.TaskStackListenerCallback;
import com.android.wm.shell.common.TaskStackListenerImpl;
@@ -138,7 +139,8 @@
mMainExecutor = mainExecutor;
mPipBoundsState = pipBoundsState;
- mPipBoundsState.setDisplayInfo(getDisplayInfo());
+ mPipBoundsState.setDisplayId(context.getDisplayId());
+ mPipBoundsState.setDisplayLayout(new DisplayLayout(context, context.getDisplay()));
mPipBoundsAlgorithm = pipBoundsAlgorithm;
mPipMediaController = pipMediaController;
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreenTest.kt
index ed305a2..e79820f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreenTest.kt
@@ -67,7 +67,8 @@
visibleLayersShownMoreThanOneConsecutiveEntry(
listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName,
nonResizeableApp.defaultWindowName, LETTER_BOX_NAME,
- TOAST_NAME, LIVE_WALLPAPER_PACKAGE_NAME)
+ TOAST_NAME, LIVE_WALLPAPER_PACKAGE_NAME),
+ bugId = 178447631
)
}
windowManagerTrace {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreenTest.kt
index 88dab51..280af5d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreenTest.kt
@@ -68,7 +68,7 @@
listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName,
nonResizeableApp.defaultWindowName, LETTER_BOX_NAME,
TOAST_NAME, LIVE_WALLPAPER_PACKAGE_NAME),
- enabled = false
+ bugId = 178447631
)
}
windowManagerTrace {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipOrientationTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipOrientationTest.kt
index 292d0ef..5e0760c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipOrientationTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipOrientationTest.kt
@@ -127,7 +127,7 @@
hasVisibleRegion(pipApp.defaultWindowName, startingBounds)
isInvisible(testApp.defaultWindowName)
}
- end("testApp layer covers fullscreen") {
+ end("testApp layer covers fullscreen", enabled = false) {
hasVisibleRegion(testApp.defaultWindowName, endingBounds)
}
navBarLayerIsAlwaysVisible(bugId = 140855415)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
index 5e0d518..4cedc48 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/DisplayImeControllerTest.java
@@ -19,11 +19,13 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.InsetsState.ITYPE_IME;
import static android.view.Surface.ROTATION_0;
+import static android.view.WindowInsets.Type.ime;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
@@ -40,18 +42,22 @@
import org.junit.Before;
import org.junit.Test;
+import java.util.concurrent.Executor;
+
@SmallTest
public class DisplayImeControllerTest {
private SurfaceControl.Transaction mT;
private DisplayImeController.PerDisplay mPerDisplay;
private IInputMethodManager mMock;
+ private Executor mExecutor;
@Before
public void setUp() throws Exception {
mT = mock(SurfaceControl.Transaction.class);
mMock = mock(IInputMethodManager.class);
- mPerDisplay = new DisplayImeController(null, null, Runnable::run, new TransactionPool() {
+ mExecutor = spy(Runnable::run);
+ mPerDisplay = new DisplayImeController(null, null, mExecutor, new TransactionPool() {
@Override
public SurfaceControl.Transaction acquire() {
return mT;
@@ -65,10 +71,36 @@
public IInputMethodManager getImms() {
return mMock;
}
+ @Override
+ void removeImeSurface() { }
}.new PerDisplay(DEFAULT_DISPLAY, ROTATION_0);
}
@Test
+ public void insetsControlChanged_schedulesNoWorkOnExecutor() {
+ mPerDisplay.insetsControlChanged(insetsStateWithIme(false), insetsSourceControl());
+ verifyZeroInteractions(mExecutor);
+ }
+
+ @Test
+ public void insetsChanged_schedulesNoWorkOnExecutor() {
+ mPerDisplay.insetsChanged(insetsStateWithIme(false));
+ verifyZeroInteractions(mExecutor);
+ }
+
+ @Test
+ public void showInsets_schedulesNoWorkOnExecutor() {
+ mPerDisplay.showInsets(ime(), true);
+ verifyZeroInteractions(mExecutor);
+ }
+
+ @Test
+ public void hideInsets_schedulesNoWorkOnExecutor() {
+ mPerDisplay.hideInsets(ime(), true);
+ verifyZeroInteractions(mExecutor);
+ }
+
+ @Test
public void reappliesVisibilityToChangedLeash() {
verifyZeroInteractions(mT);
mPerDisplay.mImeShowing = true;
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java
index ef99235..babfc5c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipBoundsAlgorithmTest.java
@@ -24,12 +24,14 @@
import android.testing.TestableLooper;
import android.testing.TestableResources;
import android.util.Size;
+import android.view.Display;
import android.view.DisplayInfo;
import android.view.Gravity;
import androidx.test.filters.SmallTest;
import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.common.DisplayLayout;
import org.junit.Before;
import org.junit.Test;
@@ -62,7 +64,8 @@
mPipBoundsState = new PipBoundsState(mContext);
mPipBoundsAlgorithm = new PipBoundsAlgorithm(mContext, mPipBoundsState);
- mPipBoundsState.setDisplayInfo(mDefaultDisplayInfo);
+ mPipBoundsState.setDisplayLayout(
+ new DisplayLayout(mDefaultDisplayInfo, mContext.getResources(), true, true));
}
private void initializeMockResources() {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
index 245858d..7a810a1 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java
@@ -38,6 +38,7 @@
import android.testing.TestableLooper;
import android.util.Rational;
import android.util.Size;
+import android.view.Display;
import android.view.DisplayInfo;
import android.window.WindowContainerToken;
@@ -45,6 +46,7 @@
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.pip.phone.PhonePipMenuController;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
@@ -196,11 +198,11 @@
private void preparePipTaskOrg() {
final DisplayInfo info = new DisplayInfo();
- mPipBoundsState.setDisplayInfo(info);
+ mPipBoundsState.setDisplayLayout(new DisplayLayout(info,
+ mContext.getResources(), true, true));
when(mMockPipBoundsAlgorithm.getEntryDestinationBounds()).thenReturn(new Rect());
when(mMockPipBoundsAlgorithm.getAdjustedDestinationBounds(any(), anyFloat()))
.thenReturn(new Rect());
- mPipBoundsState.setDisplayInfo(info);
mSpiedPipTaskOrganizer.setOneShotAnimationType(PipAnimationController.ANIM_TYPE_ALPHA);
doNothing().when(mSpiedPipTaskOrganizer).enterPipWithAlphaAnimation(any(), anyLong());
doNothing().when(mSpiedPipTaskOrganizer).scheduleAnimateResizePip(any(), anyInt(), any());
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 1ebc489..1a8d9eb 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -700,11 +700,14 @@
NinePatchUtils::SetLatticeFlags(&lattice, flags.get(), numFlags, chunk, colors.get());
}
+ SkFilterMode filter = paint && paint->isFilterBitmap() ? SkFilterMode::kLinear
+ : SkFilterMode::kNearest;
+
lattice.fBounds = nullptr;
SkRect dst = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
auto image = bitmap.makeImage();
apply_looper(paint, [&](const SkPaint& p) {
- mCanvas->drawImageLattice(image.get(), lattice, dst, &p);
+ mCanvas->drawImageLattice(image.get(), lattice, dst, filter, &p);
});
}
diff --git a/libs/hwui/canvas/CanvasOps.h b/libs/hwui/canvas/CanvasOps.h
index fa0c45b..cceba59 100644
--- a/libs/hwui/canvas/CanvasOps.h
+++ b/libs/hwui/canvas/CanvasOps.h
@@ -408,21 +408,24 @@
const sk_sp<Bitmap>& bitmap,
SkRect dst,
SkCanvas::Lattice lattice,
+ SkFilterMode filter,
SkPaint paint
): dst(dst),
lattice(lattice),
+ filter(filter),
bitmap(bitmap),
image(bitmap->makeImage()),
paint(std::move(paint)) {}
SkRect dst;
SkCanvas::Lattice lattice;
+ SkFilterMode filter;
const sk_sp<Bitmap> bitmap;
const sk_sp<SkImage> image;
SkPaint paint;
void draw(SkCanvas* canvas) const {
- canvas->drawImageLattice(image.get(), lattice, dst, &paint);
+ canvas->drawImageLattice(image.get(), lattice, dst, filter, &paint);
}
ASSERT_DRAWABLE()
};
diff --git a/libs/hwui/hwui/AnimatedImageDrawable.cpp b/libs/hwui/hwui/AnimatedImageDrawable.cpp
index 638de85..0d3d3e3 100644
--- a/libs/hwui/hwui/AnimatedImageDrawable.cpp
+++ b/libs/hwui/hwui/AnimatedImageDrawable.cpp
@@ -20,6 +20,7 @@
#endif
#include "utils/TraceUtils.h"
+#include "pipeline/skia/SkiaUtils.h"
#include <SkPicture.h>
#include <SkRefCnt.h>
@@ -31,6 +32,7 @@
AnimatedImageDrawable::AnimatedImageDrawable(sk_sp<SkAnimatedImage> animatedImage, size_t bytesUsed)
: mSkAnimatedImage(std::move(animatedImage)), mBytesUsed(bytesUsed) {
mTimeToShowNextSnapshot = ms2ns(mSkAnimatedImage->currentFrameDuration());
+ setStagingBounds(mSkAnimatedImage->getBounds());
}
void AnimatedImageDrawable::syncProperties() {
@@ -127,21 +129,38 @@
return snap;
}
+// Update the matrix to map from the intrinsic bounds of the SkAnimatedImage to
+// the bounds specified by Drawable#setBounds.
+static void handleBounds(SkMatrix* matrix, const SkRect& intrinsicBounds, const SkRect& bounds) {
+ matrix->preTranslate(bounds.left(), bounds.top());
+ matrix->preScale(bounds.width() / intrinsicBounds.width(),
+ bounds.height() / intrinsicBounds.height());
+}
+
// Only called on the RenderThread.
void AnimatedImageDrawable::onDraw(SkCanvas* canvas) {
+ // Store the matrix used to handle bounds and mirroring separate from the
+ // canvas. We may need to invert the matrix to determine the proper bounds
+ // to pass to saveLayer, and this matrix (as opposed to, potentially, the
+ // canvas' matrix) only uses scale and translate, so it must be invertible.
+ SkMatrix matrix;
+ SkAutoCanvasRestore acr(canvas, true);
+ handleBounds(&matrix, mSkAnimatedImage->getBounds(), mProperties.mBounds);
+
+ if (mProperties.mMirrored) {
+ matrix.preTranslate(mSkAnimatedImage->getBounds().width(), 0);
+ matrix.preScale(-1, 1);
+ }
+
std::optional<SkPaint> lazyPaint;
- SkAutoCanvasRestore acr(canvas, false);
if (mProperties.mAlpha != SK_AlphaOPAQUE || mProperties.mColorFilter.get()) {
lazyPaint.emplace();
lazyPaint->setAlpha(mProperties.mAlpha);
lazyPaint->setColorFilter(mProperties.mColorFilter);
lazyPaint->setFilterQuality(kLow_SkFilterQuality);
}
- if (mProperties.mMirrored) {
- canvas->save();
- canvas->translate(mSkAnimatedImage->getBounds().width(), 0);
- canvas->scale(-1, 1);
- }
+
+ canvas->concat(matrix);
const bool starting = mStarting;
mStarting = false;
@@ -151,7 +170,11 @@
// The image is not animating, and never was. Draw directly from
// mSkAnimatedImage.
if (lazyPaint) {
- canvas->saveLayer(mSkAnimatedImage->getBounds(), &*lazyPaint);
+ SkMatrix inverse;
+ (void) matrix.invert(&inverse);
+ SkRect r = mProperties.mBounds;
+ inverse.mapRect(&r);
+ canvas->saveLayer(r, &*lazyPaint);
}
std::unique_lock lock{mImageLock};
@@ -211,17 +234,31 @@
}
int AnimatedImageDrawable::drawStaging(SkCanvas* canvas) {
- SkAutoCanvasRestore acr(canvas, false);
+ // Store the matrix used to handle bounds and mirroring separate from the
+ // canvas. We may need to invert the matrix to determine the proper bounds
+ // to pass to saveLayer, and this matrix (as opposed to, potentially, the
+ // canvas' matrix) only uses scale and translate, so it must be invertible.
+ SkMatrix matrix;
+ SkAutoCanvasRestore acr(canvas, true);
+ handleBounds(&matrix, mSkAnimatedImage->getBounds(), mStagingProperties.mBounds);
+
+ if (mStagingProperties.mMirrored) {
+ matrix.preTranslate(mSkAnimatedImage->getBounds().width(), 0);
+ matrix.preScale(-1, 1);
+ }
+
+ canvas->concat(matrix);
+
if (mStagingProperties.mAlpha != SK_AlphaOPAQUE || mStagingProperties.mColorFilter.get()) {
SkPaint paint;
paint.setAlpha(mStagingProperties.mAlpha);
paint.setColorFilter(mStagingProperties.mColorFilter);
- canvas->saveLayer(mSkAnimatedImage->getBounds(), &paint);
- }
- if (mStagingProperties.mMirrored) {
- canvas->save();
- canvas->translate(mSkAnimatedImage->getBounds().width(), 0);
- canvas->scale(-1, 1);
+
+ SkMatrix inverse;
+ (void) matrix.invert(&inverse);
+ SkRect r = mStagingProperties.mBounds;
+ inverse.mapRect(&r);
+ canvas->saveLayer(r, &paint);
}
if (!mRunning) {
@@ -294,4 +331,10 @@
return ns2ms(mTimeToShowNextSnapshot - mCurrentTime);
}
+SkRect AnimatedImageDrawable::onGetBounds() {
+ // This must return a bounds that is valid for all possible states,
+ // including if e.g. the client calls setBounds.
+ return SkRectMakeLargest();
+}
+
} // namespace android
diff --git a/libs/hwui/hwui/AnimatedImageDrawable.h b/libs/hwui/hwui/AnimatedImageDrawable.h
index f81a5a4..8ca3c7e 100644
--- a/libs/hwui/hwui/AnimatedImageDrawable.h
+++ b/libs/hwui/hwui/AnimatedImageDrawable.h
@@ -67,9 +67,10 @@
mStagingProperties.mColorFilter = filter;
}
void setStagingMirrored(bool mirrored) { mStagingProperties.mMirrored = mirrored; }
+ void setStagingBounds(const SkRect& bounds) { mStagingProperties.mBounds = bounds; }
void syncProperties();
- virtual SkRect onGetBounds() override { return mSkAnimatedImage->getBounds(); }
+ SkRect onGetBounds() override;
// Draw to software canvas, and return time to next draw.
// 0 means the animation is not running.
@@ -109,7 +110,7 @@
size_t byteSize() const { return sizeof(*this) + mBytesUsed; }
protected:
- virtual void onDraw(SkCanvas* canvas) override;
+ void onDraw(SkCanvas* canvas) override;
private:
sk_sp<SkAnimatedImage> mSkAnimatedImage;
@@ -145,6 +146,7 @@
int mAlpha = SK_AlphaOPAQUE;
sk_sp<SkColorFilter> mColorFilter;
bool mMirrored = false;
+ SkRect mBounds;
Properties() = default;
Properties(Properties&) = default;
diff --git a/libs/hwui/jni/AnimatedImageDrawable.cpp b/libs/hwui/jni/AnimatedImageDrawable.cpp
index 1ff1565..c9433ec8 100644
--- a/libs/hwui/jni/AnimatedImageDrawable.cpp
+++ b/libs/hwui/jni/AnimatedImageDrawable.cpp
@@ -244,6 +244,14 @@
drawable->setStagingMirrored(mirrored);
}
+static void AnimatedImageDrawable_nSetBounds(JNIEnv* env, jobject /*clazz*/, jlong nativePtr,
+ jobject jrect) {
+ auto* drawable = reinterpret_cast<AnimatedImageDrawable*>(nativePtr);
+ SkRect rect;
+ GraphicsJNI::jrect_to_rect(env, jrect, &rect);
+ drawable->setStagingBounds(rect);
+}
+
static const JNINativeMethod gAnimatedImageDrawableMethods[] = {
{ "nCreate", "(JLandroid/graphics/ImageDecoder;IIJZLandroid/graphics/Rect;)J",(void*) AnimatedImageDrawable_nCreate },
{ "nGetNativeFinalizer", "()J", (void*) AnimatedImageDrawable_nGetNativeFinalizer },
@@ -259,6 +267,7 @@
{ "nSetOnAnimationEndListener", "(JLandroid/graphics/drawable/AnimatedImageDrawable;)V", (void*) AnimatedImageDrawable_nSetOnAnimationEndListener },
{ "nNativeByteSize", "(J)J", (void*) AnimatedImageDrawable_nNativeByteSize },
{ "nSetMirrored", "(JZ)V", (void*) AnimatedImageDrawable_nSetMirrored },
+ { "nSetBounds", "(JLandroid/graphics/Rect;)V", (void*) AnimatedImageDrawable_nSetBounds },
};
int register_android_graphics_drawable_AnimatedImageDrawable(JNIEnv* env) {
diff --git a/libs/hwui/jni/Typeface.cpp b/libs/hwui/jni/Typeface.cpp
index 10c8077..8f455fe 100644
--- a/libs/hwui/jni/Typeface.cpp
+++ b/libs/hwui/jni/Typeface.cpp
@@ -25,12 +25,17 @@
#include <hwui/Typeface.h>
#include <minikin/FontCollection.h>
#include <minikin/FontFamily.h>
+#include <minikin/FontFileParser.h>
#include <minikin/SystemFonts.h>
#include <utils/TraceUtils.h>
#include <mutex>
#include <unordered_map>
+#ifdef __ANDROID__
+#include <sys/stat.h>
+#endif
+
using namespace android;
using android::uirenderer::TraceUtils;
@@ -155,12 +160,43 @@
toTypeface(ptr)->fFontCollection);
}
-static sk_sp<SkData> makeSkDataCached(const std::string& path) {
+#ifdef __ANDROID__
+
+static bool getVerity(const std::string& path) {
+ struct statx out = {};
+ if (statx(AT_FDCWD, path.c_str(), 0 /* flags */, STATX_ALL, &out) != 0) {
+ ALOGE("statx failed for %s, errno = %d", path.c_str(), errno);
+ return false;
+ }
+
+ // Validity check.
+ if ((out.stx_attributes_mask & STATX_ATTR_VERITY) == 0) {
+ // STATX_ATTR_VERITY not supported by kernel.
+ return false;
+ }
+
+ return (out.stx_attributes & STATX_ATTR_VERITY) != 0;
+}
+
+#else
+
+static bool getVerity(const std::string&) {
+ // verity check is not enabled on desktop.
+ return false;
+}
+
+#endif // __ANDROID__
+
+static sk_sp<SkData> makeSkDataCached(const std::string& path, bool hasVerity) {
// We don't clear cache as Typeface objects created by Typeface_readTypefaces() will be stored
// in a static field and will not be garbage collected.
static std::unordered_map<std::string, sk_sp<SkData>> cache;
static std::mutex mutex;
ALOG_ASSERT(!path.empty());
+ if (hasVerity && !getVerity(path)) {
+ LOG_ALWAYS_FATAL("verity bit was removed from %s", path.c_str());
+ return nullptr;
+ }
std::lock_guard lock{mutex};
sk_sp<SkData>& entry = cache[path];
if (entry.get() == nullptr) {
@@ -171,15 +207,34 @@
static std::function<std::shared_ptr<minikin::MinikinFont>()> readMinikinFontSkia(
minikin::BufferReader* reader) {
- std::string_view fontPath = reader->readString();
- int fontIndex = reader->read<int>();
- const minikin::FontVariation* axesPtr;
- uint32_t axesCount;
- std::tie(axesPtr, axesCount) = reader->readArray<minikin::FontVariation>();
- return [fontPath, fontIndex, axesPtr, axesCount]() -> std::shared_ptr<minikin::MinikinFont> {
+ const void* buffer = reader->data();
+ size_t pos = reader->pos();
+ // Advance reader's position.
+ reader->skipString(); // fontPath
+ reader->skip<int>(); // fontIndex
+ reader->skipArray<minikin::FontVariation>(); // axesPtr, axesCount
+ bool hasVerity = static_cast<bool>(reader->read<int8_t>());
+ if (hasVerity) {
+ reader->skip<uint32_t>(); // expectedFontRevision
+ reader->skipString(); // expectedPostScriptName
+ }
+ return [buffer, pos]() -> std::shared_ptr<minikin::MinikinFont> {
+ minikin::BufferReader fontReader(buffer, pos);
+ std::string_view fontPath = fontReader.readString();
std::string path(fontPath.data(), fontPath.size());
ATRACE_FORMAT("Loading font %s", path.c_str());
- sk_sp<SkData> data = makeSkDataCached(path);
+ int fontIndex = fontReader.read<int>();
+ const minikin::FontVariation* axesPtr;
+ uint32_t axesCount;
+ std::tie(axesPtr, axesCount) = fontReader.readArray<minikin::FontVariation>();
+ bool hasVerity = static_cast<bool>(fontReader.read<int8_t>());
+ uint32_t expectedFontRevision;
+ std::string_view expectedPostScriptName;
+ if (hasVerity) {
+ expectedFontRevision = fontReader.read<uint32_t>();
+ expectedPostScriptName = fontReader.readString();
+ }
+ sk_sp<SkData> data = makeSkDataCached(path, hasVerity);
if (data.get() == nullptr) {
// This may happen if:
// 1. When the process failed to open the file (e.g. invalid path or permission).
@@ -189,6 +244,20 @@
}
const void* fontPtr = data->data();
size_t fontSize = data->size();
+ if (hasVerity) {
+ // Verify font metadata if verity is enabled.
+ minikin::FontFileParser parser(fontPtr, fontSize, fontIndex);
+ std::optional<uint32_t> revision = parser.getFontRevision();
+ if (!revision.has_value() || revision.value() != expectedFontRevision) {
+ LOG_ALWAYS_FATAL("Wrong font revision: %s", path.c_str());
+ return nullptr;
+ }
+ std::optional<std::string> psName = parser.getPostScriptName();
+ if (!psName.has_value() || psName.value() != expectedPostScriptName) {
+ LOG_ALWAYS_FATAL("Wrong PostScript name: %s", path.c_str());
+ return nullptr;
+ }
+ }
std::vector<minikin::FontVariation> axes(axesPtr, axesPtr + axesCount);
std::shared_ptr<minikin::MinikinFont> minikinFont =
fonts::createMinikinFontSkia(std::move(data), fontPath, fontPtr, fontSize,
@@ -203,10 +272,24 @@
static void writeMinikinFontSkia(minikin::BufferWriter* writer,
const minikin::MinikinFont* typeface) {
- writer->writeString(typeface->GetFontPath());
+ const std::string& path = typeface->GetFontPath();
+ writer->writeString(path);
writer->write<int>(typeface->GetFontIndex());
const std::vector<minikin::FontVariation>& axes = typeface->GetAxes();
writer->writeArray<minikin::FontVariation>(axes.data(), axes.size());
+ bool hasVerity = getVerity(path);
+ writer->write<int8_t>(static_cast<int8_t>(hasVerity));
+ if (hasVerity) {
+ // Write font metadata for verification only when verity is enabled.
+ minikin::FontFileParser parser(typeface->GetFontData(), typeface->GetFontSize(),
+ typeface->GetFontIndex());
+ std::optional<uint32_t> revision = parser.getFontRevision();
+ LOG_ALWAYS_FATAL_IF(!revision.has_value());
+ writer->write<uint32_t>(revision.value());
+ std::optional<std::string> psName = parser.getPostScriptName();
+ LOG_ALWAYS_FATAL_IF(!psName.has_value());
+ writer->writeString(psName.value());
+ }
}
static jint Typeface_writeTypefaces(JNIEnv *env, jobject, jobject buffer, jlongArray faceHandles) {
diff --git a/libs/hwui/tests/unit/CanvasOpTests.cpp b/libs/hwui/tests/unit/CanvasOpTests.cpp
index a09e742..c9e8d80 100644
--- a/libs/hwui/tests/unit/CanvasOpTests.cpp
+++ b/libs/hwui/tests/unit/CanvasOpTests.cpp
@@ -528,6 +528,7 @@
bitmap,
SkRect::MakeWH(5, 1),
lattice,
+ SkFilterMode::kNearest,
SkPaint{}
}
);
diff --git a/media/java/android/media/session/ISession.aidl b/media/java/android/media/session/ISession.aidl
index 77f7b54..31fb8d0 100644
--- a/media/java/android/media/session/ISession.aidl
+++ b/media/java/android/media/session/ISession.aidl
@@ -16,6 +16,7 @@
package android.media.session;
import android.app.PendingIntent;
+import android.content.ComponentName;
import android.content.pm.ParceledListSlice;
import android.media.AudioAttributes;
import android.media.MediaMetadata;
@@ -35,6 +36,7 @@
void setFlags(int flags);
void setActive(boolean active);
void setMediaButtonReceiver(in PendingIntent mbr);
+ void setMediaButtonBroadcastReceiver(in ComponentName broadcastReceiver);
void setLaunchPendingIntent(in PendingIntent pi);
void destroySession();
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 14b2368..24118b0 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -23,6 +23,7 @@
import android.app.Activity;
import android.app.PendingIntent;
import android.compat.annotation.UnsupportedAppUsage;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.media.AudioAttributes;
@@ -131,6 +132,7 @@
public @interface SessionFlags { }
private final Object mLock = new Object();
+ private Context mContext;
private final int mMaxBitmapSize;
private final Token mSessionToken;
@@ -194,6 +196,7 @@
+ "parcelables");
}
+ mContext = context;
mMaxBitmapSize = context.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.config_mediaMetadataBitmapMaxSize);
mCbStub = new CallbackStub(this);
@@ -277,7 +280,10 @@
*
* @param mbr The {@link PendingIntent} to send the media button event to.
* @see PendingIntent#getActivity
+ *
+ * @deprecated Use {@link #setMediaButtonBroadcastReceiver(ComponentName)} instead.
*/
+ @Deprecated
public void setMediaButtonReceiver(@Nullable PendingIntent mbr) {
try {
mBinder.setMediaButtonReceiver(mbr);
@@ -287,6 +293,32 @@
}
/**
+ * Set the component name of the manifest-declared {@link android.content.BroadcastReceiver}
+ * class that should receive media buttons. This allows restarting playback after the session
+ * has been stopped. If your app is started in this way an {@link Intent#ACTION_MEDIA_BUTTON}
+ * intent will be sent to the broadcast receiver.
+ * <p>
+ * Note: The given {@link android.content.BroadcastReceiver} should belong to the same package
+ * as the context that was given when creating {@link MediaSession}.
+ *
+ * @param broadcastReceiver the component name of the BroadcastReceiver class
+ */
+ public void setMediaButtonBroadcastReceiver(@Nullable ComponentName broadcastReceiver) {
+ try {
+ if (broadcastReceiver != null) {
+ if (!TextUtils.equals(broadcastReceiver.getPackageName(),
+ mContext.getPackageName())) {
+ throw new IllegalArgumentException("broadcastReceiver should belong to the same"
+ + " package as the context given when creating MediaSession.");
+ }
+ }
+ mBinder.setMediaButtonBroadcastReceiver(broadcastReceiver);
+ } catch (RemoteException e) {
+ Log.wtf(TAG, "Failure in setMediaButtonBroadcastReceiver.", e);
+ }
+ }
+
+ /**
* Set any flags for the session.
*
* @param flags The flags to set for this session.
diff --git a/media/java/android/media/tv/tuner/filter/Filter.java b/media/java/android/media/tv/tuner/filter/Filter.java
index 96371e5..9f327c9 100644
--- a/media/java/android/media/tv/tuner/filter/Filter.java
+++ b/media/java/android/media/tv/tuner/filter/Filter.java
@@ -209,7 +209,7 @@
prefix = "MONITOR_EVENT_",
value = {MONITOR_EVENT_SCRAMBLING_STATUS, MONITOR_EVENT_IP_CID_CHANGE})
@Retention(RetentionPolicy.SOURCE)
- public @interface MonitorEventTypeMask {}
+ public @interface MonitorEventMask {}
/**
* Monitor scrambling status change.
@@ -239,7 +239,7 @@
int type, int subType, FilterConfiguration settings);
private native int nativeGetId();
private native long nativeGetId64Bit();
- private native int nativeConfigureMonitorEvent(int monitorEventTypesMask);
+ private native int nativeConfigureMonitorEvent(int monitorEventMask);
private native int nativeSetDataSource(Filter source);
private native int nativeStartFilter();
private native int nativeStopFilter();
@@ -344,19 +344,19 @@
* will cause no-op. Use {@link TunerVersionChecker#getTunerVersion()} to get the version
* information.
*
- * @param monitorEventTypesMask Types of event to be monitored. Set corresponding bit to
- * monitor it. Reset to stop monitoring.
+ * @param monitorEventMask Types of event to be monitored. Set corresponding bit to
+ * monitor it. Reset to stop monitoring.
* @return result status of the operation.
*/
@Result
- public int configureMonitorEvent(@MonitorEventTypeMask int monitorEventTypesMask) {
+ public int setMonitorEventMask(@MonitorEventMask int monitorEventMask) {
synchronized (mLock) {
TunerUtils.checkResourceState(TAG, mIsClosed);
if (!TunerVersionChecker.checkHigherOrEqualVersionTo(
- TunerVersionChecker.TUNER_VERSION_1_1, "configureMonitorEvent")) {
+ TunerVersionChecker.TUNER_VERSION_1_1, "setMonitorEventMask")) {
return Tuner.RESULT_UNAVAILABLE;
}
- return nativeConfigureMonitorEvent(monitorEventTypesMask);
+ return nativeConfigureMonitorEvent(monitorEventMask);
}
}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
index c6c7142..935cb37 100755
--- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
@@ -25,7 +25,10 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
-import android.content.pm.PackageParser;
+import android.content.pm.parsing.ApkLiteParseUtils;
+import android.content.pm.parsing.PackageLite;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.pm.parsing.result.ParseTypeImpl;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
@@ -143,16 +146,21 @@
File file = new File(mPackageURI.getPath());
try {
- PackageParser.PackageLite pkg = PackageParser.parsePackageLite(file, 0);
- params.setAppPackageName(pkg.packageName);
- params.setInstallLocation(pkg.installLocation);
- params.setSize(
- PackageHelper.calculateInstalledSize(pkg, false, params.abiOverride));
- } catch (PackageParser.PackageParserException e) {
- Log.e(LOG_TAG, "Cannot parse package " + file + ". Assuming defaults.");
- Log.e(LOG_TAG,
- "Cannot calculate installed size " + file + ". Try only apk size.");
- params.setSize(file.length());
+ final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
+ final ParseResult<PackageLite> result = ApkLiteParseUtils.parsePackageLite(
+ input.reset(), file, /* flags */ 0);
+ if (result.isError()) {
+ Log.e(LOG_TAG, "Cannot parse package " + file + ". Assuming defaults.");
+ Log.e(LOG_TAG,
+ "Cannot calculate installed size " + file + ". Try only apk size.");
+ params.setSize(file.length());
+ } else {
+ final PackageLite pkg = result.getResult();
+ params.setAppPackageName(pkg.getPackageName());
+ params.setInstallLocation(pkg.getInstallLocation());
+ params.setSize(
+ PackageHelper.calculateInstalledSize(pkg, params.abiOverride));
+ }
} catch (IOException e) {
Log.e(LOG_TAG,
"Cannot calculate installed size " + file + ". Try only apk size.");
diff --git a/packages/SettingsLib/SettingsTheme/res/layout/preference_category_settings.xml b/packages/SettingsLib/SettingsTheme/res/layout/preference_category_settings.xml
index 4b1b255..4a1b089 100644
--- a/packages/SettingsLib/SettingsTheme/res/layout/preference_category_settings.xml
+++ b/packages/SettingsLib/SettingsTheme/res/layout/preference_category_settings.xml
@@ -19,14 +19,13 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingLeft="24dp"
- android:paddingStart="24dp"
android:paddingRight="?android:attr/listPreferredItemPaddingRight"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:background="?android:attr/selectableItemBackground"
android:baselineAligned="false"
android:layout_marginTop="16dp"
- android:gravity="center_vertical">
+ android:gravity="center_vertical"
+ style="@style/PreferenceCategoryStartMargin">
<RelativeLayout
android:layout_width="0dp"
@@ -57,6 +56,5 @@
android:textColor="?android:attr/textColorSecondary"
android:maxLines="10"
style="@style/PreferenceSummaryTextStyle"/>
-
</RelativeLayout>
</LinearLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/values-sw360dp/styles.xml b/packages/SettingsLib/SettingsTheme/res/values-sw360dp/styles.xml
new file mode 100644
index 0000000..4f40256
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/values-sw360dp/styles.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<resources>
+ <style name="PreferenceCategoryStartMargin">
+ <item name="android:paddingLeft">24dp</item>
+ <item name="android:paddingStart">24dp</item>
+ </style>
+</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values/styles.xml b/packages/SettingsLib/SettingsTheme/res/values/styles.xml
index 6b285d5..a6623b0 100644
--- a/packages/SettingsLib/SettingsTheme/res/values/styles.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values/styles.xml
@@ -22,4 +22,9 @@
<!-- 0.8 Spacing, 0.8/11 = 0.072727273 -->
<item name="android:letterSpacing">0.072727273</item>
</style>
+
+ <style name="PreferenceCategoryStartMargin">
+ <item name="android:paddingLeft">?android:attr/listPreferredItemPaddingLeft</item>
+ <item name="android:paddingStart">?android:attr/listPreferredItemPaddingStart</item>
+ </style>
</resources>
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 5d8fc0b..7eab559 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -300,6 +300,9 @@
<!-- Permission needed to test mainline permission module rollback -->
<uses-permission android:name="android.permission.UPGRADE_RUNTIME_PERMISSIONS" />
+ <!-- Permission needed to restart WiFi Subsystem -->
+ <uses-permission android:name="android.permission.RESTART_WIFI_SUBSYSTEM" />
+
<!-- Permission needed to read wifi network credentials for CtsNetTestCases -->
<uses-permission android:name="android.permission.NETWORK_AIRPLANE_MODE" />
diff --git a/packages/SystemUI/res/drawable/stat_sys_roaming_large.xml b/packages/SystemUI/res/drawable/stat_sys_roaming_large.xml
new file mode 100644
index 0000000..1511659
--- /dev/null
+++ b/packages/SystemUI/res/drawable/stat_sys_roaming_large.xml
@@ -0,0 +1,24 @@
+<!--
+ Copyright (C) 2021 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="@dimen/signal_icon_size"
+ android:height="@dimen/signal_icon_size"
+ android:viewportWidth="24.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FF000000"
+ android:pathData="M14.21,12.81c0.36,-0.16 0.69,-0.36 0.97,-0.61c0.41,-0.38 0.72,-0.83 0.94,-1.37c0.21,-0.54 0.32,-1.14 0.32,-1.79c0,-0.92 -0.16,-1.7 -0.49,-2.33c-0.32,-0.64 -0.79,-1.12 -1.43,-1.45c-0.62,-0.33 -1.4,-0.49 -2.32,-0.49H8.23V19h1.8v-5.76h2.5L15.06,19h1.92v-0.12L14.21,12.81zM10.03,11.71V6.32h2.18c0.59,0 1.06,0.11 1.42,0.34c0.36,0.22 0.62,0.54 0.78,0.95c0.16,0.41 0.24,0.89 0.24,1.44c0,0.49 -0.09,0.93 -0.27,1.34c-0.18,0.4 -0.46,0.73 -0.82,0.97c-0.36,0.23 -0.82,0.35 -1.37,0.35H10.03z"/>
+</vector>
diff --git a/packages/SystemUI/res/layout/global_screenshot_preview.xml b/packages/SystemUI/res/layout/global_screenshot_preview.xml
index 5262407..100213b 100644
--- a/packages/SystemUI/res/layout/global_screenshot_preview.xml
+++ b/packages/SystemUI/res/layout/global_screenshot_preview.xml
@@ -25,7 +25,7 @@
android:layout_marginBottom="@dimen/screenshot_offset_y"
android:scaleType="fitEnd"
android:elevation="@dimen/screenshot_preview_elevation"
- android:visibility="gone"
+ android:visibility="invisible"
android:background="@drawable/screenshot_rounded_corners"
android:adjustViewBounds="true"
android:contentDescription="@string/screenshot_edit_label"
diff --git a/packages/SystemUI/res/layout/long_screenshot.xml b/packages/SystemUI/res/layout/long_screenshot.xml
new file mode 100644
index 0000000..fc68a64
--- /dev/null
+++ b/packages/SystemUI/res/layout/long_screenshot.xml
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2021 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<androidx.constraintlayout.widget.ConstraintLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:background="?android:colorBackgroundFloating"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <ImageView
+ android:id="@+id/preview"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginVertical="8dp"
+ android:layout_marginHorizontal="48dp"
+ android:adjustViewBounds="true"
+ app:layout_constrainedHeight="true"
+ app:layout_constrainedWidth="true"
+ app:layout_constraintBottom_toBottomOf="@id/guideline"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ tools:background="?android:colorBackground"
+ tools:minHeight="100dp"
+ tools:minWidth="100dp" />
+
+ <androidx.constraintlayout.widget.Guideline
+ android:id="@+id/guideline"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ app:layout_constraintGuide_percent="0.9" />
+
+ <Button
+ android:id="@+id/close"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="8dp"
+ android:text="Close"
+ app:layout_constraintEnd_toStartOf="@+id/edit"
+ app:layout_constraintHorizontal_bias="0.5"
+ app:layout_constraintHorizontal_chainStyle="packed"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="@+id/guideline" />
+
+ <Button
+ android:id="@+id/edit"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="8dp"
+ android:text="Edit"
+ app:layout_constraintEnd_toStartOf="@+id/share"
+ app:layout_constraintHorizontal_bias="0.5"
+ app:layout_constraintStart_toEndOf="@+id/close"
+ app:layout_constraintTop_toTopOf="@+id/guideline" />
+
+ <Button
+ android:id="@+id/share"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_margin="8dp"
+ android:text="Share"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintHorizontal_bias="0.5"
+ app:layout_constraintStart_toEndOf="@+id/edit"
+ app:layout_constraintTop_toTopOf="@+id/guideline" />
+
+</androidx.constraintlayout.widget.ConstraintLayout>
+
diff --git a/packages/SystemUI/res/layout/mobile_signal_group.xml b/packages/SystemUI/res/layout/mobile_signal_group.xml
index bfd079b..5552020 100644
--- a/packages/SystemUI/res/layout/mobile_signal_group.xml
+++ b/packages/SystemUI/res/layout/mobile_signal_group.xml
@@ -77,4 +77,11 @@
android:contentDescription="@string/data_connection_roaming"
android:visibility="gone" />
</FrameLayout>
+ <ImageView
+ android:id="@+id/mobile_roaming_large"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/stat_sys_roaming_large"
+ android:contentDescription="@string/data_connection_roaming"
+ android:visibility="gone" />
</com.android.keyguard.AlphaOptimizedLinearLayout>
diff --git a/packages/SystemUI/res/layout/status_bar_mobile_signal_group.xml b/packages/SystemUI/res/layout/status_bar_mobile_signal_group.xml
index 42d541e3..10d49b3 100644
--- a/packages/SystemUI/res/layout/status_bar_mobile_signal_group.xml
+++ b/packages/SystemUI/res/layout/status_bar_mobile_signal_group.xml
@@ -85,6 +85,13 @@
android:contentDescription="@string/data_connection_roaming"
android:visibility="gone" />
</FrameLayout>
+ <ImageView
+ android:id="@+id/mobile_roaming_large"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/stat_sys_roaming_large"
+ android:contentDescription="@string/data_connection_roaming"
+ android:visibility="gone" />
</com.android.keyguard.AlphaOptimizedLinearLayout>
</com.android.systemui.statusbar.StatusBarMobileView>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 0a17828..09710d7 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -351,7 +351,7 @@
<bool name="config_showNotificationGear">true</bool>
<!-- Whether or not a background should be drawn behind a notification. -->
- <bool name="config_drawNotificationBackground">false</bool>
+ <bool name="config_drawNotificationBackground">true</bool>
<!-- Whether or the notifications can be shown and dismissed with a drag. -->
<bool name="config_enableNotificationShadeDrag">true</bool>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 79cb236..e510930 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -155,7 +155,7 @@
<dimen name="notification_max_heads_up_height_increased">188dp</dimen>
<!-- Side padding on the lockscreen on the side of notifications -->
- <dimen name="notification_side_paddings">16dp</dimen>
+ <dimen name="notification_side_paddings">4dp</dimen>
<!-- padding between the heads up and the statusbar -->
<dimen name="heads_up_status_bar_padding">8dp</dimen>
@@ -177,7 +177,10 @@
<dimen name="notification_min_interaction_height">40dp</dimen>
<!-- Radius for notifications corners without adjacent notifications -->
- <dimen name="notification_corner_radius">28dp</dimen>
+ <dimen name="notification_corner_radius">8dp</dimen>
+
+ <!-- Radius for notifications corners with adjacent notifications -->
+ <dimen name="notification_corner_radius_small">0dp</dimen>
<!-- the padding of the shelf icon container -->
<dimen name="shelf_icon_container_padding">13dp</dimen>
@@ -619,7 +622,7 @@
<dimen name="z_distance_between_notifications">0.5dp</dimen>
<!-- The height of the divider between the individual notifications. -->
- <dimen name="notification_divider_height">2dp</dimen>
+ <dimen name="notification_divider_height">1dp</dimen>
<!-- The corner radius of the shadow behind the notification. -->
<dimen name="notification_shadow_radius">0dp</dimen>
@@ -632,7 +635,7 @@
<dimen name="notification_children_container_divider_height">0.5dp</dimen>
<!-- The horizontal margin of the content in the notification shade -->
- <dimen name="notification_shade_content_margin_horizontal">16dp</dimen>
+ <dimen name="notification_shade_content_margin_horizontal">4dp</dimen>
<!-- The top margin for the notification children container in its non-expanded form. -->
<dimen name="notification_children_container_margin_top">
diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml
index 905a575..49f9109 100644
--- a/packages/SystemUI/res/values/flags.xml
+++ b/packages/SystemUI/res/values/flags.xml
@@ -16,10 +16,12 @@
-->
<resources>
- <bool name="are_flags_overrideable">true</bool>
+ <bool name="are_flags_overrideable">false</bool>
<bool name="flag_notification_pipeline2">false</bool>
<bool name="flag_notification_pipeline2_rendering">false</bool>
+ <bool name="flag_notif_updates">false</bool>
+
<bool name="flag_shade_is_opaque">false</bool>
<!-- b/171917882 -->
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInitializer.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInitializer.java
index c3815e4..42bc1d0 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInitializer.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInitializer.java
@@ -37,4 +37,9 @@
* Called from {@link PluginManagerImpl#handleWtfs()}.
*/
void handleWtfs();
+
+ /**
+ * Returns if pluging manager should run in debug mode.
+ */
+ boolean isDebuggable();
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
index ee7030a8..1a4e2d1 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java
@@ -28,7 +28,6 @@
import android.content.pm.ResolveInfo;
import android.content.res.Resources;
import android.net.Uri;
-import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -72,7 +71,7 @@
PluginInstanceManager(Context context, String action, PluginListener<T> listener,
boolean allowMultiple, Looper looper, VersionInfo version, PluginManagerImpl manager) {
this(context, context.getPackageManager(), action, listener, allowMultiple, looper, version,
- manager, Build.IS_DEBUGGABLE, manager.getWhitelistedPlugins());
+ manager, manager.isDebuggable(), manager.getWhitelistedPlugins());
}
@VisibleForTesting
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
index 6d67f21..f5ed9da 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java
@@ -64,8 +64,6 @@
private static final String TAG = PluginManagerImpl.class.getSimpleName();
static final String DISABLE_PLUGIN = "com.android.systemui.action.DISABLE_PLUGIN";
- private static PluginManager sInstance;
-
private final ArrayMap<PluginListener<?>, PluginInstanceManager> mPluginMap
= new ArrayMap<>();
private final Map<String, ClassLoader> mClassLoaders = new ArrayMap<>();
@@ -73,7 +71,7 @@
private final ArraySet<String> mWhitelistedPlugins = new ArraySet<>();
private final Context mContext;
private final PluginInstanceManagerFactory mFactory;
- private final boolean isDebuggable;
+ private final boolean mIsDebuggable;
private final PluginPrefs mPluginPrefs;
private final PluginEnabler mPluginEnabler;
private final PluginInitializer mPluginInitializer;
@@ -83,7 +81,7 @@
private Looper mLooper;
public PluginManagerImpl(Context context, PluginInitializer initializer) {
- this(context, new PluginInstanceManagerFactory(), Build.IS_DEBUGGABLE,
+ this(context, new PluginInstanceManagerFactory(), initializer.isDebuggable(),
Thread.getUncaughtExceptionPreHandler(), initializer);
}
@@ -93,7 +91,7 @@
mContext = context;
mFactory = factory;
mLooper = initializer.getBgLooper();
- isDebuggable = debuggable;
+ mIsDebuggable = debuggable;
mWhitelistedPlugins.addAll(Arrays.asList(initializer.getWhitelistedPlugins(mContext)));
mPluginPrefs = new PluginPrefs(mContext);
mPluginEnabler = initializer.getPluginEnabler(mContext);
@@ -111,6 +109,10 @@
});
}
+ public boolean isDebuggable() {
+ return mIsDebuggable;
+ }
+
public String[] getWhitelistedPlugins() {
return mWhitelistedPlugins.toArray(new String[0]);
}
@@ -297,7 +299,7 @@
/** Returns class loader specific for the given plugin. */
public ClassLoader getClassLoader(ApplicationInfo appInfo) {
- if (!isDebuggable && !isPluginPackageWhitelisted(appInfo.packageName)) {
+ if (!mIsDebuggable && !isPluginPackageWhitelisted(appInfo.packageName)) {
Log.w(TAG, "Cannot get class loader for non-whitelisted plugin. Src:"
+ appInfo.sourceDir + ", pkg: " + appInfo.packageName);
return null;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
index e5c4bf3..9164137 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityManagerWrapper.java
@@ -24,6 +24,7 @@
import android.annotation.NonNull;
import android.app.Activity;
+import android.app.ActivityClient;
import android.app.ActivityManager;
import android.app.ActivityManager.RecentTaskInfo;
import android.app.ActivityManager.RunningTaskInfo;
@@ -140,8 +141,9 @@
*/
public void invalidateHomeTaskSnapshot(final Activity homeActivity) {
try {
- getService().invalidateHomeTaskSnapshot(homeActivity.getActivityToken());
- } catch (RemoteException e) {
+ ActivityClient.getInstance().invalidateHomeTaskSnapshot(
+ homeActivity.getActivityToken());
+ } catch (Throwable e) {
Log.w(TAG, "Failed to invalidate home snapshot", e);
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 2d972e0..6eb54c2 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -192,7 +192,7 @@
statusAreaLP.removeRule(RelativeLayout.ALIGN_PARENT_START);
statusAreaLP.removeRule(RelativeLayout.START_OF);
statusAreaLP.addRule(RelativeLayout.BELOW, R.id.clock_view);
- statusAreaLP.width = ViewGroup.LayoutParams.WRAP_CONTENT;
+ statusAreaLP.width = ViewGroup.LayoutParams.MATCH_PARENT;
}
requestLayout();
diff --git a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
index 8ebcb20..756d610 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
@@ -156,9 +156,14 @@
}
@Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- mAnimator.updateMargin((ViewGroup.MarginLayoutParams) getLayoutParams());
+ public void setLayoutParams(ViewGroup.LayoutParams params) {
+ mAnimator.updateMargin((ViewGroup.MarginLayoutParams) params);
+ super.setLayoutParams(params);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
measureChildren(widthMeasureSpec, heightMeasureSpec);
diff --git a/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java b/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java
index 02f34ac..5384ddf 100644
--- a/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java
@@ -16,7 +16,7 @@
package com.android.systemui;
-import android.app.ActivityTaskManager;
+import android.app.ActivityClient;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.Color;
@@ -27,7 +27,6 @@
import android.hardware.display.DisplayManager;
import android.inputmethodservice.InputMethodService;
import android.os.IBinder;
-import android.os.RemoteException;
import android.util.Log;
import android.util.SparseArray;
import android.view.Display;
@@ -237,12 +236,7 @@
@Override
public void onClick(View v) {
- try {
- ActivityTaskManager.getService().restartActivityProcessIfVisible(
- mLastActivityToken);
- } catch (RemoteException e) {
- Log.w(TAG, "Unable to restart activity", e);
- }
+ ActivityClient.getInstance().restartActivityProcessIfVisible(mLastActivityToken);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index dc09fa7..b373cff 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -47,6 +47,7 @@
import com.android.internal.BrightnessSynchronizer;
import com.android.internal.annotations.VisibleForTesting;
import com.android.systemui.R;
+import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.DozeReceiver;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -71,7 +72,8 @@
* {@code sensorId} parameters.
*/
@SuppressWarnings("deprecation")
-class UdfpsController implements DozeReceiver {
+@SysUISingleton
+public class UdfpsController implements DozeReceiver {
private static final String TAG = "UdfpsController";
// Gamma approximation for the sRGB color space.
private static final float DISPLAY_GAMMA = 2.2f;
@@ -177,7 +179,7 @@
};
@Inject
- UdfpsController(@NonNull Context context,
+ public UdfpsController(@NonNull Context context,
@Main Resources resources,
LayoutInflater inflater,
@Nullable FingerprintManager fingerprintManager,
@@ -464,7 +466,7 @@
onFingerUp();
}
- private void onFingerDown(int x, int y, float minor, float major) {
+ protected void onFingerDown(int x, int y, float minor, float major) {
if (mHbmSupported) {
try {
FileWriter fw = new FileWriter(mHbmPath);
@@ -482,7 +484,7 @@
mView.showScrimAndDot();
}
- private void onFingerUp() {
+ protected void onFingerUp() {
mFingerprintManager.onPointerUp(mSensorProps.sensorId);
// Hiding the scrim before disabling HBM results in less noticeable flicker.
mView.hideScrimAndDot();
@@ -521,4 +523,8 @@
}
return normalizedBacklight;
}
+
+ protected UdfpsView getView() {
+ return mView;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
index 265703e..96ecc7b 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java
@@ -27,6 +27,7 @@
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
+import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.RectF;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
@@ -34,20 +35,20 @@
import android.util.AttributeSet;
import android.util.Log;
import android.view.Surface;
-import android.view.View;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
import android.view.ViewTreeObserver;
import com.android.systemui.R;
import com.android.systemui.doze.DozeReceiver;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.phone.ScrimController;
/**
* A full screen view with a configurable illumination dot and scrim.
*/
-public class UdfpsView extends View implements DozeReceiver,
- StatusBarStateController.StateListener, ScrimController.ScrimChangedListener{
+public class UdfpsView extends SurfaceView implements DozeReceiver,
+ StatusBarStateController.StateListener, ScrimController.ScrimChangedListener {
private static final String TAG = "UdfpsView";
// Values in pixels.
@@ -86,6 +87,29 @@
// The runnable is reset to null after it's executed once.
@Nullable private Runnable mRunAfterShowingScrimAndDot;
+ @NonNull private final SurfaceHolder.Callback mSurfaceCallback = new SurfaceHolder.Callback() {
+ @Override
+ public void surfaceCreated(@NonNull SurfaceHolder holder) {
+ Log.d(TAG, "Surface created");
+ // SurfaceView sets this to true by default. We must set it to false to allow
+ // onDraw to be called
+ setWillNotDraw(false);
+ }
+
+ @Override
+ public void surfaceChanged(@NonNull SurfaceHolder holder, int format,
+ int width, int height) {
+
+ }
+
+ @Override
+ public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
+ Log.d(TAG, "Surface destroyed");
+ // Must not draw when the surface is destroyed
+ setWillNotDraw(true);
+ }
+ };
+
public UdfpsView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -102,6 +126,8 @@
a.recycle();
}
+ getHolder().addCallback(mSurfaceCallback);
+ getHolder().setFormat(PixelFormat.TRANSLUCENT);
mScrimRect = new Rect();
mScrimPaint = new Paint(0 /* flags */);
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java
index 95029c0..7f01d6f 100644
--- a/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java
@@ -15,6 +15,7 @@
package com.android.systemui.plugins;
import android.content.Context;
+import android.os.Build;
import android.os.Looper;
import android.util.Log;
@@ -67,4 +68,9 @@
});
}
}
+
+ @Override
+ public boolean isDebuggable() {
+ return Build.IS_DEBUGGABLE;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java
index 39b92dc..7d8d86f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrier.java
@@ -20,6 +20,7 @@
import android.content.res.ColorStateList;
import android.text.TextUtils;
import android.util.AttributeSet;
+import android.util.FeatureFlagUtils;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
@@ -65,10 +66,13 @@
super.onFinishInflate();
mDualToneHandler = new DualToneHandler(getContext());
mMobileGroup = findViewById(R.id.mobile_combo);
+ if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) {
+ mMobileRoaming = findViewById(R.id.mobile_roaming_large);
+ } else {
+ mMobileRoaming = findViewById(R.id.mobile_roaming);
+ }
mMobileSignal = findViewById(R.id.mobile_signal);
- mMobileRoaming = findViewById(R.id.mobile_roaming);
mCarrierText = findViewById(R.id.qs_carrier_text);
-
mMobileSignal.setImageDrawable(new SignalDrawable(mContext));
int colorForeground = Utils.getColorAttrDefaultColor(mContext,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
index ebdcc00..77200cc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/carrier/QSCarrierGroupController.java
@@ -71,7 +71,7 @@
boolean activityIn, boolean activityOut,
CharSequence typeContentDescription,
CharSequence typeContentDescriptionHtml, CharSequence description,
- boolean isWide, int subId, boolean roaming) {
+ boolean isWide, int subId, boolean roaming, boolean showTriangle) {
int slotIndex = getSlotIndex(subId);
if (slotIndex >= SIM_SLOTS) {
Log.w(TAG, "setMobileDataIndicators - slot: " + slotIndex);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
index f742752..720c5dc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CellularTile.java
@@ -268,7 +268,7 @@
int qsType, boolean activityIn, boolean activityOut,
CharSequence typeContentDescription,
CharSequence typeContentDescriptionHtml, CharSequence description,
- boolean isWide, int subId, boolean roaming) {
+ boolean isWide, int subId, boolean roaming, boolean showTriangle) {
if (qsIcon == null) {
// Not data sim, don't display.
return;
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 1523278..1b2ad4c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
@@ -34,6 +34,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settingslib.graph.SignalDrawable;
+import com.android.settingslib.mobile.TelephonyIcons;
import com.android.settingslib.net.DataUsageController;
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Background;
@@ -178,6 +179,7 @@
CharSequence mDataSubscriptionName;
CharSequence mDataContentDescription;
int mMobileSignalIconId;
+ int mQsTypeIcon;
boolean mActivityIn;
boolean mActivityOut;
boolean mNoSim;
@@ -194,6 +196,7 @@
.append(",mDataSubscriptionName=").append(mDataSubscriptionName)
.append(",mDataContentDescription=").append(mDataContentDescription)
.append(",mMobileSignalIconId=").append(mMobileSignalIconId)
+ .append(",mQsTypeIcon=").append(mQsTypeIcon)
.append(",mActivityIn=").append(mActivityIn)
.append(",mActivityOut=").append(mActivityOut)
.append(",mNoSim=").append(mNoSim)
@@ -249,7 +252,7 @@
int qsType, boolean activityIn, boolean activityOut,
CharSequence typeContentDescription,
CharSequence typeContentDescriptionHtml, CharSequence description,
- boolean isWide, int subId, boolean roaming) {
+ boolean isWide, int subId, boolean roaming, boolean showTriangle) {
if (DEBUG) {
Log.d(TAG, "setMobileDataIndicators: "
+ "statusIcon = " + (statusIcon == null ? "" : statusIcon.toString()) + ","
@@ -263,7 +266,8 @@
+ "description = " + description + ","
+ "isWide = " + isWide + ","
+ "subId = " + subId + ","
- + "roaming = " + roaming);
+ + "roaming = " + roaming + ","
+ + "showTriangle = " + showTriangle);
}
if (qsIcon == null) {
// Not data sim, don't display.
@@ -274,6 +278,7 @@
mCellularInfo.mDataContentDescription =
(description != null) ? typeContentDescriptionHtml : null;
mCellularInfo.mMobileSignalIconId = qsIcon.icon;
+ mCellularInfo.mQsTypeIcon = qsType;
mCellularInfo.mActivityIn = activityIn;
mCellularInfo.mActivityOut = activityOut;
mCellularInfo.mRoaming = roaming;
@@ -292,6 +297,7 @@
if (mCellularInfo.mNoSim) {
// Make sure signal gets cleared out when no sims.
mCellularInfo.mMobileSignalIconId = 0;
+ mCellularInfo.mQsTypeIcon = 0;
}
refreshState(mCellularInfo);
}
@@ -374,6 +380,7 @@
state.label = r.getString(R.string.quick_settings_internet_label);
if (cb.mAirplaneModeEnabled) {
if (!state.value) {
+ state.state = Tile.STATE_INACTIVE;
state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_airplane);
state.secondaryLabel = r.getString(R.string.status_bar_airplane);
} else if (!wifiConnected) {
@@ -443,7 +450,8 @@
state.activityOut = mobileDataEnabled && cb.mActivityOut;
state.expandedAccessibilityClassName = Switch.class.getName();
- if (cb.mAirplaneModeEnabled && cb.mNoDefaultNetwork) {
+ if (cb.mAirplaneModeEnabled && cb.mQsTypeIcon != TelephonyIcons.ICON_CWF) {
+ state.state = Tile.STATE_INACTIVE;
state.icon = ResourceIcon.get(R.drawable.ic_qs_no_internet_airplane);
state.secondaryLabel = r.getString(R.string.status_bar_airplane);
} else if (cb.mNoDefaultNetwork && cb.mNoNetworksAvailable) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageTile.java b/packages/SystemUI/src/com/android/systemui/screenshot/ImageTile.java
index 143121a..212e6c8 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ImageTile.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageTile.java
@@ -19,6 +19,7 @@
import static java.util.Objects.requireNonNull;
+import android.annotation.NonNull;
import android.graphics.Bitmap;
import android.graphics.ColorSpace;
import android.graphics.RecordingCanvas;
@@ -50,14 +51,13 @@
* @param image an image containing a hardware buffer
* @param location the captured area represented by image tile (virtual coordinates)
*/
- ImageTile(Image image, Rect location) {
+ ImageTile(@NonNull Image image, @NonNull Rect location) {
mImage = requireNonNull(image, "image");
- mLocation = location;
-
+ mLocation = requireNonNull(location);
requireNonNull(mImage.getHardwareBuffer(), "image must be a hardware image");
}
- RenderNode getDisplayList() {
+ synchronized RenderNode getDisplayList() {
if (mNode == null) {
mNode = new RenderNode("Tile{" + Integer.toHexString(mImage.hashCode()) + "}");
}
@@ -69,7 +69,6 @@
mNode.setPosition(0, 0, w, h);
RecordingCanvas canvas = mNode.beginRecording(w, h);
- Rect rect = new Rect(0, 0, w, h);
canvas.save();
canvas.clipRect(0, 0, mLocation.right, mLocation.bottom);
canvas.drawBitmap(Bitmap.wrapHardwareBuffer(mImage.getHardwareBuffer(), COLOR_SPACE),
@@ -100,9 +99,11 @@
}
@Override
- public void close() {
+ public synchronized void close() {
mImage.close();
- mNode.discardDisplayList();
+ if (mNode != null) {
+ mNode.discardDisplayList();
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index b71036c..d6413ed 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -49,7 +49,6 @@
import android.hardware.display.DisplayManager;
import android.media.MediaActionSound;
import android.net.Uri;
-import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -67,6 +66,7 @@
import android.view.MotionEvent;
import android.view.SurfaceControl;
import android.view.View;
+import android.view.ViewTreeObserver;
import android.view.Window;
import android.view.WindowInsets;
import android.view.WindowManager;
@@ -80,13 +80,15 @@
import com.android.internal.policy.PhoneWindow;
import com.android.settingslib.applications.InterestingConfigChanges;
import com.android.systemui.R;
-import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.screenshot.ScreenshotController.SavedImageData.ActionTransition;
+import com.android.systemui.screenshot.TakeScreenshotService.RequestCallback;
import com.android.systemui.util.DeviceConfigProxy;
import java.util.List;
import java.util.concurrent.Executor;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
import java.util.function.Consumer;
import java.util.function.Supplier;
@@ -173,7 +175,7 @@
private final UiEventLogger mUiEventLogger;
private final ImageExporter mImageExporter;
private final Executor mMainExecutor;
- private final Executor mBgExecutor;
+ private final ExecutorService mBgExecutor;
private final WindowManager mWindowManager;
private final WindowManager.LayoutParams mWindowLayoutParams;
@@ -182,17 +184,14 @@
private final ScrollCaptureClient mScrollCaptureClient;
private final DeviceConfigProxy mConfigProxy;
private final PhoneWindow mWindow;
- private final View mDecorView;
private final DisplayManager mDisplayManager;
- private final Binder mWindowToken;
private ScreenshotView mScreenshotView;
private Bitmap mScreenBitmap;
private SaveImageInBackgroundTask mSaveInBgTask;
private Animator mScreenshotAnimation;
-
- private Runnable mOnCompleteRunnable;
+ private RequestCallback mCurrentRequestCallback;
private final Handler mScreenshotHandler = new Handler(Looper.getMainLooper()) {
@Override
@@ -229,15 +228,14 @@
UiEventLogger uiEventLogger,
DeviceConfigProxy configProxy,
ImageExporter imageExporter,
- @Main Executor mainExecutor,
- @Background Executor bgExecutor) {
+ @Main Executor mainExecutor) {
mScreenshotSmartActions = screenshotSmartActions;
mNotificationsController = screenshotNotificationsController;
mScrollCaptureClient = scrollCaptureClient;
mUiEventLogger = uiEventLogger;
mImageExporter = imageExporter;
mMainExecutor = mainExecutor;
- mBgExecutor = bgExecutor;
+ mBgExecutor = Executors.newSingleThreadExecutor();
mDisplayManager = requireNonNull(context.getSystemService(DisplayManager.class));
final Context displayContext = context.createDisplayContext(getDefaultDisplay());
@@ -247,9 +245,6 @@
mAccessibilityManager = AccessibilityManager.getInstance(mContext);
mConfigProxy = configProxy;
- mWindowToken = new Binder("ScreenshotController");
- mScrollCaptureClient.setHostWindowToken(mWindowToken);
-
// Setup the window that we are going to use
mWindowLayoutParams = new WindowManager.LayoutParams(
MATCH_PARENT, MATCH_PARENT, /* xpos */ 0, /* ypos */ 0, TYPE_SCREENSHOT,
@@ -263,7 +258,6 @@
mWindowLayoutParams.setTitle("ScreenshotAnimation");
mWindowLayoutParams.layoutInDisplayCutoutMode =
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
- mWindowLayoutParams.token = mWindowToken;
// This is needed to let touches pass through outside the touchable areas
mWindowLayoutParams.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
@@ -272,8 +266,8 @@
mWindow.requestFeature(Window.FEATURE_NO_TITLE);
mWindow.requestFeature(Window.FEATURE_ACTIVITY_TRANSITIONS);
mWindow.setBackgroundDrawableResource(android.R.color.transparent);
- mDecorView = mWindow.getDecorView();
+ mConfigChanges.applyNewConfig(context.getResources());
reloadAssets();
// Setup the Camera shutter sound
@@ -281,9 +275,8 @@
mCameraSound.load(MediaActionSound.SHUTTER_CLICK);
}
- void takeScreenshotFullscreen(Consumer<Uri> finisher, Runnable onComplete) {
- mOnCompleteRunnable = onComplete;
-
+ void takeScreenshotFullscreen(Consumer<Uri> finisher, RequestCallback requestCallback) {
+ mCurrentRequestCallback = requestCallback;
DisplayMetrics displayMetrics = new DisplayMetrics();
getDefaultDisplay().getRealMetrics(displayMetrics);
takeScreenshotInternal(
@@ -293,16 +286,14 @@
void handleImageAsScreenshot(Bitmap screenshot, Rect screenshotScreenBounds,
Insets visibleInsets, int taskId, int userId, ComponentName topComponent,
- Consumer<Uri> finisher, Runnable onComplete) {
+ Consumer<Uri> finisher, RequestCallback requestCallback) {
// TODO: use task Id, userId, topComponent for smart handler
- mOnCompleteRunnable = onComplete;
if (screenshot == null) {
Log.e(TAG, "Got null bitmap from screenshot message");
mNotificationsController.notifyScreenshotError(
R.string.screenshot_failed_to_capture_text);
- finisher.accept(null);
- mOnCompleteRunnable.run();
+ requestCallback.reportError();
return;
}
@@ -312,17 +303,19 @@
visibleInsets = Insets.NONE;
screenshotScreenBounds.set(0, 0, screenshot.getWidth(), screenshot.getHeight());
}
+ mCurrentRequestCallback = requestCallback;
saveScreenshot(screenshot, finisher, screenshotScreenBounds, visibleInsets, showFlash);
}
/**
* Displays a screenshot selector
*/
- void takeScreenshotPartial(final Consumer<Uri> finisher, Runnable onComplete) {
- dismissScreenshot(true);
- mOnCompleteRunnable = onComplete;
+ void takeScreenshotPartial(final Consumer<Uri> finisher, RequestCallback requestCallback) {
+ mScreenshotView.reset();
+ mCurrentRequestCallback = requestCallback;
- mWindowManager.addView(mScreenshotView, mWindowLayoutParams);
+ attachWindow();
+ mWindow.setContentView(mScreenshotView);
mScreenshotView.takePartialScreenshot(
rect -> takeScreenshotInternal(finisher, rect));
@@ -343,9 +336,9 @@
}
return;
}
- mScreenshotHandler.removeMessages(MESSAGE_CORNER_TIMEOUT);
+ cancelTimeout();
if (immediate) {
- resetScreenshotView();
+ finishDismiss();
} else {
mScreenshotView.animateDismissal();
}
@@ -360,6 +353,8 @@
*/
void releaseContext() {
mContext.release();
+ mCameraSound.release();
+ mBgExecutor.shutdownNow();
}
/**
@@ -369,12 +364,8 @@
if (DEBUG_UI) {
Log.d(TAG, "reloadAssets()");
}
- boolean wasAttached = mDecorView.isAttachedToWindow();
- if (wasAttached) {
- if (DEBUG_WINDOW) {
- Log.d(TAG, "Removing screenshot window");
- }
- mWindowManager.removeView(mDecorView);
+ if (mScreenshotView != null && mScreenshotView.isAttachedToWindow()) {
+ mWindow.clearContentView(); // Is there a simpler way to say "remove screenshotView?"
}
// respect the display cutout in landscape (since we'd otherwise overlap) but not portrait
@@ -382,12 +373,6 @@
mWindowLayoutParams.setFitInsetsTypes(
orientation == ORIENTATION_PORTRAIT ? 0 : WindowInsets.Type.displayCutout());
- // ignore system bar insets for the purpose of window layout
- mDecorView.setOnApplyWindowInsetsListener((v, insets) -> v.onApplyWindowInsets(
- new WindowInsets.Builder(insets)
- .setInsets(WindowInsets.Type.all(), Insets.NONE)
- .build()));
-
// Inflate the screenshot layout
mScreenshotView = (ScreenshotView)
LayoutInflater.from(mContext).inflate(R.layout.global_screenshot, null);
@@ -399,12 +384,18 @@
@Override
public void onDismiss() {
- resetScreenshotView();
+ finishDismiss();
}
});
+ // ignore system bar insets for the purpose of window layout
+ mScreenshotView.setOnApplyWindowInsetsListener((v, insets) -> v.onApplyWindowInsets(
+ new WindowInsets.Builder(insets)
+ .setInsets(WindowInsets.Type.all(), Insets.NONE)
+ .build()));
+
// TODO(159460485): Remove this when focus is handled properly in the system
- mDecorView.setOnTouchListener((v, event) -> {
+ mScreenshotView.setOnTouchListener((v, event) -> {
if (event.getActionMasked() == MotionEvent.ACTION_OUTSIDE) {
if (DEBUG_INPUT) {
Log.d(TAG, "onTouch: ACTION_OUTSIDE");
@@ -426,8 +417,10 @@
return false;
});
- // view is added to window manager in startAnimation
- mWindow.setContentView(mScreenshotView, mWindowLayoutParams);
+ if (DEBUG_WINDOW) {
+ Log.d(TAG, "adding OnComputeInternalInsetsListener");
+ }
+ mScreenshotView.getViewTreeObserver().addOnComputeInternalInsetsListener(mScreenshotView);
}
/**
@@ -464,14 +457,9 @@
Log.e(TAG, "takeScreenshotInternal: Screenshot bitmap was null");
mNotificationsController.notifyScreenshotError(
R.string.screenshot_failed_to_capture_text);
- if (DEBUG_CALLBACK) {
- Log.d(TAG, "Supplying null to Consumer<Uri>");
+ if (mCurrentRequestCallback != null) {
+ mCurrentRequestCallback.reportError();
}
- finisher.accept(null);
- if (DEBUG_CALLBACK) {
- Log.d(TAG, "Calling mOnCompleteRunnable.run()");
- }
- mOnCompleteRunnable.run();
return;
}
@@ -488,6 +476,13 @@
mAccessibilityManager.sendAccessibilityEvent(event);
}
+ if (mConfigChanges.applyNewConfig(mContext.getResources())) {
+ if (DEBUG_UI) {
+ Log.d(TAG, "saveScreenshot: reloading assets");
+ }
+ reloadAssets();
+ }
+
if (mScreenshotView.isAttachedToWindow()) {
// if we didn't already dismiss for another reason
if (!mScreenshotView.isDismissing()) {
@@ -514,33 +509,112 @@
mScreenBitmap.setHasAlpha(false);
mScreenBitmap.prepareToDraw();
- if (mConfigChanges.applyNewConfig(mContext.getResources())) {
- if (DEBUG_UI) {
- Log.d(TAG, "saveScreenshot: reloading assets");
- }
- reloadAssets();
- }
+ saveScreenshotInWorkerThread(finisher, this::showUiOnActionsReady);
// The window is focusable by default
setWindowFocusable(true);
- // Start the post-screenshot animation
- startAnimation(finisher, screenRect, screenInsets, showFlash);
-
if (mConfigProxy.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
SystemUiDeviceConfigFlags.SCREENSHOT_SCROLLING_ENABLED, false)) {
- mScrollCaptureClient.request(DEFAULT_DISPLAY, (connection) ->
- mScreenshotView.showScrollChip(() ->
- runScrollCapture(connection,
- () -> mScreenshotHandler.post(
- () -> dismissScreenshot(false)))));
+ View decorView = mWindow.getDecorView();
+
+ // Wait until this window is attached to request because it is
+ // the reference used to locate the target window (below).
+ withWindowAttached(() -> {
+ mScrollCaptureClient.setHostWindowToken(decorView.getWindowToken());
+ mScrollCaptureClient.request(DEFAULT_DISPLAY,
+ /* onConnection */
+ (connection) -> mScreenshotHandler.post(() ->
+ mScreenshotView.showScrollChip(() ->
+ /* onClick */
+ runScrollCapture(connection))));
+ });
+ }
+
+
+ attachWindow();
+ if (DEBUG_WINDOW) {
+ Log.d(TAG, "setContentView: " + mScreenshotView);
+ }
+ mScreenshotView.getViewTreeObserver().addOnPreDrawListener(
+ new ViewTreeObserver.OnPreDrawListener() {
+ @Override
+ public boolean onPreDraw() {
+ if (DEBUG_WINDOW) {
+ Log.d(TAG, "onPreDraw: startAnimation");
+ }
+ mScreenshotView.getViewTreeObserver().removeOnPreDrawListener(this);
+ startAnimation(screenRect, showFlash);
+ return true;
+ }
+ });
+ mScreenshotView.setScreenshot(mScreenBitmap, screenInsets);
+ setContentView(mScreenshotView);
+ cancelTimeout(); // restarted after animation
+ }
+
+ private void withWindowAttached(Runnable action) {
+ View decorView = mWindow.getDecorView();
+ if (decorView.isAttachedToWindow()) {
+ action.run();
+ } else {
+ decorView.getViewTreeObserver().addOnWindowAttachListener(
+ new ViewTreeObserver.OnWindowAttachListener() {
+ @Override
+ public void onWindowAttached() {
+ decorView.getViewTreeObserver().removeOnWindowAttachListener(this);
+ action.run();
+ }
+
+ @Override
+ public void onWindowDetached() { }
+ });
+
}
}
- private void runScrollCapture(ScrollCaptureClient.Connection connection, Runnable andThen) {
+ private void setContentView(View contentView) {
+ mWindow.setContentView(contentView);
+ }
+
+ private void attachWindow() {
+ View decorView = mWindow.getDecorView();
+ if (decorView.isAttachedToWindow()) {
+ return;
+ }
+ if (DEBUG_WINDOW) {
+ Log.d(TAG, "attachWindow");
+ }
+ mWindowManager.addView(decorView, mWindowLayoutParams);
+ decorView.requestApplyInsets();
+ }
+
+ void removeWindow() {
+ final View decorView = mWindow.peekDecorView();
+ if (decorView != null && decorView.isAttachedToWindow()) {
+ if (DEBUG_WINDOW) {
+ Log.d(TAG, "Removing screenshot window");
+ }
+ mWindowManager.removeViewImmediate(decorView);
+ }
+ }
+
+ private void runScrollCapture(ScrollCaptureClient.Connection connection) {
+ cancelTimeout();
ScrollCaptureController controller = new ScrollCaptureController(mContext, connection,
mMainExecutor, mBgExecutor, mImageExporter);
- controller.run(andThen);
+ controller.attach(mWindow);
+ controller.start(new TakeScreenshotService.RequestCallback() {
+ @Override
+ public void reportError() {
+ }
+
+ @Override
+ public void onFinish() {
+ Log.d(TAG, "onFinish from ScrollCaptureController");
+ finishDismiss();
+ }
+ });
}
/**
@@ -549,11 +623,11 @@
*/
private void saveScreenshotAndToast(Consumer<Uri> finisher) {
// Play the shutter sound to notify that we've taken a screenshot
- mScreenshotHandler.post(() -> {
- mCameraSound.play(MediaActionSound.SHUTTER_CLICK);
- });
+ mCameraSound.play(MediaActionSound.SHUTTER_CLICK);
- saveScreenshotInWorkerThread(finisher, imageData -> {
+ saveScreenshotInWorkerThread(
+ /* onComplete */ finisher,
+ /* actionsReadyListener */ imageData -> {
if (DEBUG_CALLBACK) {
Log.d(TAG, "returning URI to finisher (Consumer<URI>): " + imageData.uri);
}
@@ -573,55 +647,35 @@
/**
* Starts the animation after taking the screenshot
*/
- private void startAnimation(final Consumer<Uri> finisher, Rect screenRect, Insets screenInsets,
- boolean showFlash) {
- mScreenshotHandler.removeMessages(MESSAGE_CORNER_TIMEOUT);
- mScreenshotHandler.post(() -> {
- if (!mScreenshotView.isAttachedToWindow()) {
- if (DEBUG_WINDOW) {
- Log.d(TAG, "Adding screenshot window");
- }
- mWindowManager.addView(mWindow.getDecorView(), mWindowLayoutParams);
- }
+ private void startAnimation(Rect screenRect, boolean showFlash) {
+ if (mScreenshotAnimation != null && mScreenshotAnimation.isRunning()) {
+ mScreenshotAnimation.cancel();
+ }
- mScreenshotView.prepareForAnimation(mScreenBitmap, screenInsets);
+ mScreenshotAnimation =
+ mScreenshotView.createScreenshotDropInAnimation(screenRect, showFlash);
- mScreenshotHandler.post(() -> {
- if (DEBUG_WINDOW) {
- Log.d(TAG, "adding OnComputeInternalInsetsListener");
- }
- mScreenshotView.getViewTreeObserver().addOnComputeInternalInsetsListener(
- mScreenshotView);
+ // Play the shutter sound to notify that we've taken a screenshot
+ mCameraSound.play(MediaActionSound.SHUTTER_CLICK);
- mScreenshotAnimation =
- mScreenshotView.createScreenshotDropInAnimation(screenRect, showFlash);
-
- saveScreenshotInWorkerThread(finisher, this::showUiOnActionsReady);
-
- // Play the shutter sound to notify that we've taken a screenshot
- mCameraSound.play(MediaActionSound.SHUTTER_CLICK);
-
- if (DEBUG_ANIM) {
- Log.d(TAG, "starting post-screenshot animation");
- }
- mScreenshotAnimation.start();
- });
- });
+ if (DEBUG_ANIM) {
+ Log.d(TAG, "starting post-screenshot animation");
+ }
+ mScreenshotAnimation.start();
}
/** Reset screenshot view and then call onCompleteRunnable */
- private void resetScreenshotView() {
+ private void finishDismiss() {
if (DEBUG_UI) {
- Log.d(TAG, "resetScreenshotView");
+ Log.d(TAG, "finishDismiss");
}
- if (mScreenshotView.isAttachedToWindow()) {
- if (DEBUG_WINDOW) {
- Log.d(TAG, "Removing screenshot window");
- }
- mWindowManager.removeView(mDecorView);
- }
+ cancelTimeout();
+ removeWindow();
mScreenshotView.reset();
- mOnCompleteRunnable.run();
+ if (mCurrentRequestCallback != null) {
+ mCurrentRequestCallback.onFinish();
+ mCurrentRequestCallback = null;
+ }
}
/**
@@ -645,8 +699,12 @@
mSaveInBgTask.execute();
}
- private void resetTimeout() {
+ private void cancelTimeout() {
mScreenshotHandler.removeMessages(MESSAGE_CORNER_TIMEOUT);
+ }
+
+ private void resetTimeout() {
+ cancelTimeout();
AccessibilityManager accessibilityManager = (AccessibilityManager)
mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
@@ -705,7 +763,7 @@
@Override
public void hideSharedElements() {
- resetScreenshotView();
+ finishDismiss();
}
@Override
@@ -753,13 +811,21 @@
if (DEBUG_WINDOW) {
Log.d(TAG, "setWindowFocusable: " + focusable);
}
+ int flags = mWindowLayoutParams.flags;
if (focusable) {
mWindowLayoutParams.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
} else {
mWindowLayoutParams.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
}
- if (mDecorView.isAttachedToWindow()) {
- mWindowManager.updateViewLayout(mDecorView, mWindowLayoutParams);
+ if (mWindowLayoutParams.flags == flags) {
+ if (DEBUG_WINDOW) {
+ Log.d(TAG, "setWindowFocusable: skipping, already " + focusable);
+ }
+ return;
+ }
+ final View decorView = mWindow.peekDecorView();
+ if (decorView != null && decorView.isAttachedToWindow()) {
+ mWindowManager.updateViewLayout(decorView, mWindowLayoutParams);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
index 211f507..bf86b68 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
@@ -316,21 +316,21 @@
mScreenshotSelectorView.requestFocus();
}
- void prepareForAnimation(Bitmap bitmap, Insets screenInsets) {
+ void setScreenshot(Bitmap bitmap, Insets screenInsets) {
mScreenshotPreview.setImageDrawable(createScreenDrawable(mResources, bitmap, screenInsets));
- // make static preview invisible (from gone) so we can query its location on screen
- mScreenshotPreview.setVisibility(View.INVISIBLE);
}
AnimatorSet createScreenshotDropInAnimation(Rect bounds, boolean showFlash) {
- mScreenshotPreview.setLayerType(View.LAYER_TYPE_HARDWARE, null);
- mScreenshotPreview.buildLayer();
+ if (DEBUG_ANIM) {
+ Log.d(TAG, "createAnim: bounds=" + bounds + " showFlash=" + showFlash);
+ }
Rect previewBounds = new Rect();
mScreenshotPreview.getBoundsOnScreen(previewBounds);
- int[] previewLocation = new int[2];
- mScreenshotPreview.getLocationInWindow(previewLocation);
+ Rect targetPosition = new Rect();
+ mScreenshotPreview.getHitRect(targetPosition);
+ // ratio of preview width, end vs. start size
float cornerScale =
mCornerSizeX / (mOrientationPortrait ? bounds.width() : bounds.height());
final float currentScale = 1 / cornerScale;
@@ -358,8 +358,13 @@
// animate from the current location, to the static preview location
final PointF startPos = new PointF(bounds.centerX(), bounds.centerY());
- final PointF finalPos = new PointF(previewLocation[0] + previewBounds.width() / 2f,
- previewLocation[1] + previewBounds.height() / 2f);
+ final PointF finalPos = new PointF(targetPosition.exactCenterX(),
+ targetPosition.exactCenterY());
+
+ if (DEBUG_ANIM) {
+ Log.d(TAG, "toCorner: startPos=" + startPos);
+ Log.d(TAG, "toCorner: finalPos=" + finalPos);
+ }
ValueAnimator toCorner = ValueAnimator.ofFloat(0, 1);
toCorner.setDuration(SCREENSHOT_TO_CORNER_Y_DURATION_MS);
@@ -427,7 +432,7 @@
@Override
public void onAnimationEnd(Animator animation) {
if (DEBUG_ANIM) {
- Log.d(TAG, "drop-in animation completed");
+ Log.d(TAG, "drop-in animation ended");
}
mDismissButton.setOnClickListener(view -> {
if (DEBUG_INPUT) {
@@ -653,13 +658,12 @@
getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
// Clear any references to the bitmap
mScreenshotPreview.setImageDrawable(null);
+ mScreenshotPreview.setVisibility(View.INVISIBLE);
mPendingSharedTransition = false;
mActionsContainerBackground.setVisibility(View.GONE);
mActionsContainer.setVisibility(View.GONE);
mBackgroundProtection.setAlpha(0f);
mDismissButton.setVisibility(View.GONE);
- mScreenshotPreview.setVisibility(View.GONE);
- mScreenshotPreview.setLayerType(View.LAYER_TYPE_NONE, null);
mScreenshotStatic.setTranslationX(0);
mScreenshotPreview.setTranslationY(0);
mScreenshotPreview.setContentDescription(
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
index 54b1b2c..c2c6790 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
@@ -16,16 +16,24 @@
package com.android.systemui.screenshot;
+import android.annotation.IdRes;
+import android.annotation.UiThread;
import android.content.Context;
import android.content.Intent;
-import android.graphics.Bitmap;
import android.net.Uri;
import android.os.UserHandle;
import android.util.Log;
-import android.widget.Toast;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewTreeObserver.InternalInsetsInfo;
+import android.view.ViewTreeObserver.OnComputeInternalInsetsListener;
+import android.view.Window;
+import android.widget.ImageView;
+import com.android.systemui.R;
import com.android.systemui.screenshot.ScrollCaptureClient.Connection;
import com.android.systemui.screenshot.ScrollCaptureClient.Session;
+import com.android.systemui.screenshot.TakeScreenshotService.RequestCallback;
import com.google.common.util.concurrent.ListenableFuture;
@@ -38,11 +46,9 @@
/**
* Interaction controller between the UI and ScrollCaptureClient.
*/
-public class ScrollCaptureController {
+public class ScrollCaptureController implements OnComputeInternalInsetsListener {
private static final String TAG = "ScrollCaptureController";
- private static final boolean USE_TILED_IMAGE = false;
-
public static final int MAX_PAGES = 5;
public static final int MAX_HEIGHT = 12000;
@@ -53,9 +59,19 @@
private final Executor mBgExecutor;
private final ImageExporter mImageExporter;
private final ImageTileSet mImageTileSet;
+ private final LayoutInflater mLayoutInflater;
private ZonedDateTime mCaptureTime;
private UUID mRequestId;
+ private RequestCallback mCallback;
+ private Window mWindow;
+ private ImageView mPreview;
+ private View mClose;
+ private View mEdit;
+ private View mShare;
+
+ private ListenableFuture<ImageExporter.Result> mExportFuture;
+ private Runnable mPendingAction;
public ScrollCaptureController(Context context, Connection connection, Executor uiExecutor,
Executor bgExecutor, ImageExporter exporter) {
@@ -65,20 +81,126 @@
mBgExecutor = bgExecutor;
mImageExporter = exporter;
mImageTileSet = new ImageTileSet();
+ mLayoutInflater = mContext.getSystemService(LayoutInflater.class);
+ }
+
+ /**
+ * @param window the window to display the preview
+ */
+ public void attach(Window window) {
+ mWindow = window;
}
/**
* Run scroll capture!
*
- * @param after action to take after the flow is complete
+ * @param callback request callback to report back to the service
*/
- public void run(final Runnable after) {
+ public void start(RequestCallback callback) {
mCaptureTime = ZonedDateTime.now();
mRequestId = UUID.randomUUID();
- mConnection.start((session) -> startCapture(session, after));
+ mCallback = callback;
+
+ setContentView(R.layout.long_screenshot);
+ mWindow.getDecorView().getViewTreeObserver()
+ .addOnComputeInternalInsetsListener(this);
+ mPreview = findViewById(R.id.preview);
+
+ mClose = findViewById(R.id.close);
+ mEdit = findViewById(R.id.edit);
+ mShare = findViewById(R.id.share);
+
+ mClose.setOnClickListener(this::onClicked);
+ mEdit.setOnClickListener(this::onClicked);
+ mShare.setOnClickListener(this::onClicked);
+
+ mPreview.setImageDrawable(mImageTileSet.getDrawable());
+ mConnection.start(this::startCapture);
}
- private void startCapture(Session session, final Runnable onDismiss) {
+
+ /** Ensure the entire window is touchable */
+ public void onComputeInternalInsets(InternalInsetsInfo inoutInfo) {
+ inoutInfo.setTouchableInsets(InternalInsetsInfo.TOUCHABLE_INSETS_FRAME);
+ }
+
+ void disableButtons() {
+ mClose.setEnabled(false);
+ mEdit.setEnabled(false);
+ mShare.setEnabled(false);
+ }
+
+ private void onClicked(View v) {
+ Log.d(TAG, "button clicked!");
+
+ int id = v.getId();
+ if (id == R.id.close) {
+ v.setPressed(true);
+ disableButtons();
+ finish();
+ } else if (id == R.id.edit) {
+ v.setPressed(true);
+ disableButtons();
+ edit();
+ } else if (id == R.id.share) {
+ v.setPressed(true);
+ disableButtons();
+ share();
+ }
+ }
+
+ private void finish() {
+ if (mExportFuture == null) {
+ doFinish();
+ } else {
+ mExportFuture.addListener(this::doFinish, mUiExecutor);
+ }
+ }
+
+ private void doFinish() {
+ mPreview.setImageDrawable(null);
+ mImageTileSet.clear();
+ mCallback.onFinish();
+ mWindow.getDecorView().getViewTreeObserver()
+ .removeOnComputeInternalInsetsListener(this);
+ }
+
+ private void edit() {
+ sendIntentWhenReady(Intent.ACTION_EDIT);
+ }
+
+ private void share() {
+ sendIntentWhenReady(Intent.ACTION_SEND);
+ }
+
+ void sendIntentWhenReady(String action) {
+ if (mExportFuture != null) {
+ mExportFuture.addListener(() -> {
+ try {
+ ImageExporter.Result result = mExportFuture.get();
+ sendIntent(action, result.uri);
+ mCallback.onFinish();
+ } catch (InterruptedException | ExecutionException e) {
+ Log.e(TAG, "failed to export", e);
+ mCallback.onFinish();
+ }
+
+ }, mUiExecutor);
+ } else {
+ mPendingAction = this::edit;
+ }
+ }
+
+ private void setContentView(@IdRes int id) {
+ mWindow.setContentView(id);
+ }
+
+ <T extends View> T findViewById(@IdRes int res) {
+ return mWindow.findViewById(res);
+ }
+
+ private void startCapture(Session session) {
+ Log.d(TAG, "startCapture");
Consumer<ScrollCaptureClient.CaptureResult> consumer =
new Consumer<ScrollCaptureClient.CaptureResult>() {
@@ -91,17 +213,17 @@
boolean emptyFrame = result.captured.height() == 0;
if (!emptyFrame) {
- mImageTileSet.addTile(new ImageTile(result.image, result.captured));
+ ImageTile tile = new ImageTile(result.image, result.captured);
+ Log.d(TAG, "Adding tile: " + tile);
+ mImageTileSet.addTile(tile);
+ Log.d(TAG, "New dimens: w=" + mImageTileSet.getWidth() + ", "
+ + "h=" + mImageTileSet.getHeight());
}
if (emptyFrame || mFrameCount >= MAX_PAGES
|| mTop + session.getTileHeight() > MAX_HEIGHT) {
- if (!mImageTileSet.isEmpty()) {
- exportToFile(mImageTileSet.toBitmap(), session, onDismiss);
- mImageTileSet.clear();
- } else {
- session.end(onDismiss);
- }
+
+ mUiExecutor.execute(() -> afterCaptureComplete(session));
return;
}
mTop += result.captured.height();
@@ -113,25 +235,24 @@
session.requestTile(0, consumer);
};
- void exportToFile(Bitmap bitmap, Session session, Runnable afterEnd) {
- mImageExporter.setFormat(Bitmap.CompressFormat.PNG);
- mImageExporter.setQuality(6);
- ListenableFuture<ImageExporter.Result> future =
- mImageExporter.export(mBgExecutor, mRequestId, bitmap, mCaptureTime);
- future.addListener(() -> {
- try {
- ImageExporter.Result result = future.get();
- launchViewer(result.uri);
- } catch (InterruptedException | ExecutionException e) {
- Toast.makeText(mContext, "Failed to write image", Toast.LENGTH_SHORT).show();
- Log.e(TAG, "Error storing screenshot to media store", e.getCause());
+ @UiThread
+ void afterCaptureComplete(Session session) {
+ Log.d(TAG, "afterCaptureComplete");
+
+ if (mImageTileSet.isEmpty()) {
+ session.end(mCallback::onFinish);
+ } else {
+ mExportFuture = mImageExporter.export(
+ mBgExecutor, mRequestId, mImageTileSet.toBitmap(), mCaptureTime);
+ // The user chose an action already, link it to the result
+ if (mPendingAction != null) {
+ mExportFuture.addListener(mPendingAction, mUiExecutor);
}
- session.end(afterEnd); // end session, close connection, afterEnd.run()
- }, mUiExecutor);
+ }
}
- void launchViewer(Uri uri) {
- Intent editIntent = new Intent(Intent.ACTION_VIEW);
+ void sendIntent(String action, Uri uri) {
+ Intent editIntent = new Intent(action);
editIntent.setType("image/png");
editIntent.setData(uri);
editIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index 144ad39..daa9d09 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -25,6 +25,7 @@
import static com.android.systemui.screenshot.LogConfig.DEBUG_SERVICE;
import static com.android.systemui.screenshot.LogConfig.logTag;
+import android.annotation.MainThread;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -59,7 +60,8 @@
public class TakeScreenshotService extends Service {
private static final String TAG = logTag(TakeScreenshotService.class);
- private final ScreenshotController mScreenshot;
+ private ScreenshotController mScreenshot;
+
private final UserManager mUserManager;
private final UiEventLogger mUiEventLogger;
private final ScreenshotNotificationsController mNotificationsController;
@@ -79,6 +81,15 @@
}
};
+ /** Informs about coarse grained state of the Controller. */
+ interface RequestCallback {
+ /** Respond to the current request indicating the screenshot request failed.*/
+ void reportError();
+
+ /** The controller has completed handling this request UI has been removed */
+ void onFinish();
+ }
+
@Inject
public TakeScreenshotService(ScreenshotController screenshotController, UserManager userManager,
UiEventLogger uiEventLogger,
@@ -116,7 +127,8 @@
Log.d(TAG, "onUnbind");
}
if (mScreenshot != null) {
- mScreenshot.dismissScreenshot(true);
+ mScreenshot.removeWindow();
+ mScreenshot = null;
}
unregisterReceiver(mCloseSystemDialogs);
return false;
@@ -126,18 +138,39 @@
public void onDestroy() {
super.onDestroy();
if (mScreenshot != null) {
+ mScreenshot.removeWindow();
mScreenshot.releaseContext();
+ mScreenshot = null;
}
if (DEBUG_SERVICE) {
Log.d(TAG, "onDestroy");
}
}
+ static class RequestCallbackImpl implements RequestCallback {
+ private final Messenger mReplyTo;
+
+ RequestCallbackImpl(Messenger replyTo) {
+ mReplyTo = replyTo;
+ }
+
+ public void reportError() {
+ reportUri(mReplyTo, null);
+ sendComplete(mReplyTo);
+ }
+
+ @Override
+ public void onFinish() {
+ sendComplete(mReplyTo);
+ }
+ }
+
/** Respond to incoming Message via Binder (Messenger) */
+ @MainThread
private boolean handleMessage(Message msg) {
final Messenger replyTo = msg.replyTo;
- final Runnable onComplete = () -> sendComplete(replyTo);
final Consumer<Uri> uriConsumer = (uri) -> reportUri(replyTo, uri);
+ RequestCallback requestCallback = new RequestCallbackImpl(replyTo);
// If the storage for this user is locked, we have no place to store
// the screenshot, so skip taking it instead of showing a misleading
@@ -146,14 +179,7 @@
Log.w(TAG, "Skipping screenshot because storage is locked!");
mNotificationsController.notifyScreenshotError(
R.string.screenshot_failed_to_save_user_locked_text);
- if (DEBUG_CALLBACK) {
- Log.d(TAG, "handleMessage: calling uriConsumer.accept(null)");
- }
- uriConsumer.accept(null);
- if (DEBUG_CALLBACK) {
- Log.d(TAG, "handleMessage: calling onComplete.run()");
- }
- onComplete.run();
+ requestCallback.reportError();
return true;
}
@@ -167,13 +193,13 @@
if (DEBUG_SERVICE) {
Log.d(TAG, "handleMessage: TAKE_SCREENSHOT_FULLSCREEN");
}
- mScreenshot.takeScreenshotFullscreen(uriConsumer, onComplete);
+ mScreenshot.takeScreenshotFullscreen(uriConsumer, requestCallback);
break;
case WindowManager.TAKE_SCREENSHOT_SELECTED_REGION:
if (DEBUG_SERVICE) {
Log.d(TAG, "handleMessage: TAKE_SCREENSHOT_SELECTED_REGION");
}
- mScreenshot.takeScreenshotPartial(uriConsumer, onComplete);
+ mScreenshot.takeScreenshotPartial(uriConsumer, requestCallback);
break;
case WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE:
if (DEBUG_SERVICE) {
@@ -186,8 +212,16 @@
int taskId = screenshotRequest.getTaskId();
int userId = screenshotRequest.getUserId();
ComponentName topComponent = screenshotRequest.getTopComponent();
- mScreenshot.handleImageAsScreenshot(screenshot, screenBounds, insets,
- taskId, userId, topComponent, uriConsumer, onComplete);
+
+ if (screenshot == null) {
+ Log.e(TAG, "Got null bitmap from screenshot message");
+ mNotificationsController.notifyScreenshotError(
+ R.string.screenshot_failed_to_capture_text);
+ requestCallback.reportError();
+ } else {
+ mScreenshot.handleImageAsScreenshot(screenshot, screenBounds, insets,
+ taskId, userId, topComponent, uriConsumer, requestCallback);
+ }
break;
default:
Log.w(TAG, "Invalid screenshot option: " + msg.what);
@@ -196,7 +230,7 @@
return true;
};
- private void sendComplete(Messenger target) {
+ private static void sendComplete(Messenger target) {
try {
if (DEBUG_CALLBACK) {
Log.d(TAG, "sendComplete: " + target);
@@ -207,7 +241,7 @@
}
}
- private void reportUri(Messenger target, Uri uri) {
+ private static void reportUri(Messenger target, Uri uri) {
try {
if (DEBUG_CALLBACK) {
Log.d(TAG, "reportUri: " + target + " -> " + uri);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
index fdb793e..924eb26 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/RemoteInputController.java
@@ -131,6 +131,7 @@
*/
public void removeRemoteInput(NotificationEntry entry, Object token) {
Objects.requireNonNull(entry);
+ if (entry.mRemoteEditImeVisible) return;
pruneWeakThenRemoveAndContains(null /* contains */, entry /* remove */, token);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
index 239addd..d562726 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
@@ -27,6 +27,7 @@
import android.graphics.Color;
import android.graphics.Rect;
import android.util.AttributeSet;
+import android.util.FeatureFlagUtils;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
@@ -64,7 +65,6 @@
LayoutInflater inflater = LayoutInflater.from(context);
StatusBarMobileView v = (StatusBarMobileView)
inflater.inflate(R.layout.status_bar_mobile_signal_group, null);
-
v.setSlot(slot);
v.init();
v.setVisibleState(STATE_ICON);
@@ -104,7 +104,11 @@
mMobileGroup = findViewById(R.id.mobile_group);
mMobile = findViewById(R.id.mobile_signal);
mMobileType = findViewById(R.id.mobile_type);
- mMobileRoaming = findViewById(R.id.mobile_roaming);
+ if (FeatureFlagUtils.isEnabled(getContext(), FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) {
+ mMobileRoaming = findViewById(R.id.mobile_roaming_large);
+ } else {
+ mMobileRoaming = findViewById(R.id.mobile_roaming);
+ }
mMobileRoamingSpace = findViewById(R.id.mobile_roaming_space);
mIn = findViewById(R.id.mobile_in);
mOut = findViewById(R.id.mobile_out);
@@ -160,7 +164,7 @@
} else {
mMobileType.setVisibility(View.GONE);
}
-
+ mMobile.setVisibility(mState.showTriangle ? View.VISIBLE : View.GONE);
mMobileRoaming.setVisibility(mState.roaming ? View.VISIBLE : View.GONE);
mMobileRoamingSpace.setVisibility(mState.roaming ? View.VISIBLE : View.GONE);
mIn.setVisibility(mState.activityIn ? View.VISIBLE : View.GONE);
@@ -191,6 +195,7 @@
}
}
+ mMobile.setVisibility(state.showTriangle ? View.VISIBLE : View.GONE);
mMobileRoaming.setVisibility(state.roaming ? View.VISIBLE : View.GONE);
mMobileRoamingSpace.setVisibility(state.roaming ? View.VISIBLE : View.GONE);
mIn.setVisibility(state.activityIn ? View.VISIBLE : View.GONE);
@@ -200,7 +205,8 @@
needsLayout |= state.roaming != mState.roaming
|| state.activityIn != mState.activityIn
- || state.activityOut != mState.activityOut;
+ || state.activityOut != mState.activityOut
+ || state.showTriangle != mState.showTriangle;
mState = state;
return needsLayout;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index bafa4a25..8a22b9f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -179,6 +179,8 @@
private boolean mShelfIconVisible;
private boolean mIsAlerting;
+ public boolean mRemoteEditImeVisible;
+
/**
* @param sbn the StatusBarNotification from system server
* @param ranking also from system server
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
index 596aea0..2b12119 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java
@@ -302,8 +302,12 @@
} else {
mMenuContainer = new FrameLayout(mContext);
}
- final boolean newFlowHideShelf = Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.SHOW_NEW_NOTIF_DISMISS, 1 /* on by default */) == 1;
+ // The setting can win (which is needed for tests) but if not set, then use the flag
+ final int showDismissSetting = Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.SHOW_NEW_NOTIF_DISMISS, -1);
+ final boolean newFlowHideShelf = showDismissSetting == -1
+ ? mContext.getResources().getBoolean(R.bool.flag_notif_updates)
+ : showDismissSetting == 1;
if (newFlowHideShelf) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
index 079cf77..45f5b31 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManager.java
@@ -16,8 +16,10 @@
package com.android.systemui.statusbar.notification.stack;
+import android.content.res.Resources;
import android.util.MathUtils;
+import com.android.systemui.R;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -45,11 +47,6 @@
private ExpandableNotificationRow mTrackedHeadsUp;
private float mAppearFraction;
- // Radius for notification corners WITH adjacent notifications
- // as percent of radius WITHOUT adjacent notifications.
- // TODO(b/175710408) pull from dimens and hide from beta builds.
- static final float SMALL_CORNER_RADIUS = 4f/28;
-
@Inject
NotificationRoundnessManager(
KeyguardBypassController keyguardBypassController,
@@ -128,7 +125,9 @@
if (view.showingPulsing() && !mBypassController.getBypassEnabled()) {
return 1.0f;
}
- return SMALL_CORNER_RADIUS;
+ final Resources resources = view.getResources();
+ return resources.getDimension(R.dimen.notification_corner_radius_small)
+ / resources.getDimension(R.dimen.notification_corner_radius);
}
public void setExpanded(float expandedHeight, float appearFraction) {
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 f07d874..3f3be44 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
@@ -41,8 +41,6 @@
import android.graphics.Outline;
import android.graphics.Paint;
import android.graphics.PointF;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.UserHandle;
@@ -503,7 +501,8 @@
mSections = mSectionsManager.createSectionsForBuckets();
mAmbientState = new AmbientState(context, mSectionsManager);
- mBgColor = Utils.getColorAttr(mContext, android.R.attr.colorBackground).getDefaultColor();
+ mBgColor = Utils.getColorAttr(mContext, android.R.attr.colorBackgroundFloating)
+ .getDefaultColor();
int minHeight = res.getDimensionPixelSize(R.dimen.notification_min_height);
int maxHeight = res.getDimensionPixelSize(R.dimen.notification_max_height);
mExpandHelper = new ExpandHelper(getContext(), mExpandHelperCallback,
@@ -623,7 +622,8 @@
}
void updateBgColor() {
- mBgColor = Utils.getColorAttr(mContext, android.R.attr.colorBackground).getDefaultColor();
+ mBgColor = Utils.getColorAttr(mContext, android.R.attr.colorBackgroundFloating)
+ .getDefaultColor();
updateBackgroundDimming();
mShelf.onUiModeChanged();
}
@@ -2282,7 +2282,8 @@
if (child.getVisibility() != View.GONE
&& !(child instanceof StackScrollerDecorView)
&& child != mShelf
- && mSwipeHelper.getSwipedView() != child) {
+ && (mSwipeHelper.getSwipedView() != child
+ || !child.getResources().getBoolean(R.bool.flag_notif_updates))) {
children.add(child);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
index d11e864..5f90077 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
@@ -221,7 +221,7 @@
int qsType, boolean activityIn, boolean activityOut,
CharSequence typeContentDescription,
CharSequence typeContentDescriptionHtml, CharSequence description,
- boolean isWide, int subId, boolean roaming) {
+ boolean isWide, int subId, boolean roaming, boolean showTriangle) {
if (DEBUG) {
Log.d(TAG, "setMobileDataIndicators: "
+ "statusIcon = " + (statusIcon == null ? "" : statusIcon.toString()) + ","
@@ -235,7 +235,8 @@
+ "description = " + description + ","
+ "isWide = " + isWide + ","
+ "subId = " + subId + ","
- + "roaming = " + roaming);
+ + "roaming = " + roaming + ","
+ + "showTriangle = " + showTriangle);
}
MobileIconState state = getState(subId);
if (state == null) {
@@ -250,6 +251,7 @@
state.typeId = statusType;
state.contentDescription = statusIcon.contentDescription;
state.typeContentDescription = typeContentDescription;
+ state.showTriangle = showTriangle;
state.roaming = roaming;
state.activityIn = activityIn && mActivityEnabled;
state.activityOut = activityOut && mActivityEnabled;
@@ -551,6 +553,7 @@
public int subId;
public int strengthId;
public int typeId;
+ public boolean showTriangle;
public boolean roaming;
public boolean needsLeadingPadding;
public CharSequence typeContentDescription;
@@ -569,20 +572,21 @@
return false;
}
MobileIconState that = (MobileIconState) o;
- return subId == that.subId &&
- strengthId == that.strengthId &&
- typeId == that.typeId &&
- roaming == that.roaming &&
- needsLeadingPadding == that.needsLeadingPadding &&
- Objects.equals(typeContentDescription, that.typeContentDescription);
+ return subId == that.subId
+ && strengthId == that.strengthId
+ && typeId == that.typeId
+ && showTriangle == that.showTriangle
+ && roaming == that.roaming
+ && needsLeadingPadding == that.needsLeadingPadding
+ && Objects.equals(typeContentDescription, that.typeContentDescription);
}
@Override
public int hashCode() {
return Objects
- .hash(super.hashCode(), subId, strengthId, typeId, roaming, needsLeadingPadding,
- typeContentDescription);
+ .hash(super.hashCode(), subId, strengthId, typeId, showTriangle, roaming,
+ needsLeadingPadding, typeContentDescription);
}
public MobileIconState copy() {
@@ -596,6 +600,7 @@
other.subId = subId;
other.strengthId = strengthId;
other.typeId = typeId;
+ other.showTriangle = showTriangle;
other.roaming = roaming;
other.needsLeadingPadding = needsLeadingPadding;
other.typeContentDescription = typeContentDescription;
@@ -613,8 +618,9 @@
}
@Override public String toString() {
- return "MobileIconState(subId=" + subId + ", strengthId=" + strengthId + ", roaming="
- + roaming + ", typeId=" + typeId + ", visible=" + visible + ")";
+ return "MobileIconState(subId=" + subId + ", strengthId=" + strengthId
+ + ", showTriangle=" + showTriangle + ", roaming=" + roaming
+ + ", typeId=" + typeId + ", visible=" + visible + ")";
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java
index 5e88cd5..08a4492 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CallbackHandler.java
@@ -124,12 +124,13 @@
final int statusType, final int qsType, final boolean activityIn,
final boolean activityOut, final CharSequence typeContentDescription,
CharSequence typeContentDescriptionHtml, final CharSequence description,
- final boolean isWide, final int subId, boolean roaming) {
+ final boolean isWide, final int subId, boolean roaming, boolean showTriangle) {
post(() -> {
for (SignalCallback signalCluster : mSignalCallbacks) {
signalCluster.setMobileDataIndicators(statusIcon, qsIcon, statusType, qsType,
activityIn, activityOut, typeContentDescription,
- typeContentDescriptionHtml, description, isWide, subId, roaming);
+ typeContentDescriptionHtml, description, isWide, subId, roaming,
+ showTriangle);
}
});
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
index d097cfa..499d1e4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
@@ -316,6 +316,7 @@
@Override
public void onDarkChanged(Rect area, float darkIntensity, int tint) {
mNonAdaptedColor = DarkIconDispatcher.getTint(area, this, tint);
+ setTextColor(mNonAdaptedColor);
}
// Update text color based when shade scrim changes color.
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index 2f66508..39472de 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -284,13 +284,15 @@
&& !mCurrentState.carrierNetworkChangeMode
&& mCurrentState.activityOut;
showDataIcon &= mCurrentState.dataSim && mCurrentState.isDefault;
+ boolean showTriangle = showDataIcon && !mCurrentState.airplaneMode;
+ int typeIcon = (showDataIcon || mConfig.alwaysShowDataRatIcon) ? icons.dataType : 0;
+ showDataIcon |= mCurrentState.roaming;
IconState statusIcon = new IconState(showDataIcon && !mCurrentState.airplaneMode,
getCurrentIconId(), contentDescription);
- int typeIcon = (showDataIcon || mConfig.alwaysShowDataRatIcon) ? icons.dataType : 0;
callback.setMobileDataIndicators(statusIcon, qsIcon, typeIcon, qsTypeIcon,
activityIn, activityOut, dataContentDescription, dataContentDescriptionHtml,
description, icons.isWide, mSubscriptionInfo.getSubscriptionId(),
- mCurrentState.roaming);
+ mCurrentState.roaming, showTriangle);
} else {
boolean showDataIcon = mCurrentState.dataConnected || dataDisabled;
IconState statusIcon = new IconState(
@@ -316,10 +318,11 @@
&& mCurrentState.activityOut;
showDataIcon &= mCurrentState.isDefault || dataDisabled;
int typeIcon = (showDataIcon || mConfig.alwaysShowDataRatIcon) ? icons.dataType : 0;
+ boolean showTriangle = mCurrentState.enabled && !mCurrentState.airplaneMode;
callback.setMobileDataIndicators(statusIcon, qsIcon, typeIcon, qsTypeIcon,
activityIn, activityOut, dataContentDescription, dataContentDescriptionHtml,
description, icons.isWide, mSubscriptionInfo.getSubscriptionId(),
- mCurrentState.roaming);
+ mCurrentState.roaming, showTriangle);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index f2b0d76..e60d5c5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -66,12 +66,13 @@
* @param isWide //TODO: unused?
* @param subId subscription ID for which to update the UI
* @param roaming indicates roaming
+ * @param showTriangle whether to show the mobile triangle the in status bar
*/
default void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType,
int qsType, boolean activityIn, boolean activityOut,
CharSequence typeContentDescription,
CharSequence typeContentDescriptionHtml, CharSequence description,
- boolean isWide, int subId, boolean roaming) {
+ boolean isWide, int subId, boolean roaming, boolean showTriangle) {
}
default void setSubs(List<SubscriptionInfo> subs) {}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 6c5251b..9380d91 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.policy;
+import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP;
+
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.annotation.Nullable;
@@ -50,6 +52,8 @@
import android.view.View;
import android.view.ViewAnimationUtils;
import android.view.ViewGroup;
+import android.view.WindowInsets;
+import android.view.WindowInsetsAnimation;
import android.view.accessibility.AccessibilityEvent;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.EditorInfo;
@@ -61,6 +65,8 @@
import android.widget.ProgressBar;
import android.widget.TextView;
+import androidx.annotation.NonNull;
+
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.statusbar.IStatusBarService;
@@ -76,6 +82,7 @@
import java.util.Collection;
import java.util.HashMap;
+import java.util.List;
import java.util.function.Consumer;
/**
@@ -135,6 +142,27 @@
mEditText = (RemoteEditText) getChildAt(0);
mEditText.setInnerFocusable(false);
+ mEditText.setWindowInsetsAnimationCallback(
+ new WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) {
+ @NonNull
+ @Override
+ public WindowInsets onProgress(@NonNull WindowInsets insets,
+ @NonNull List<WindowInsetsAnimation> runningAnimations) {
+ return insets;
+ }
+
+ @Override
+ public void onEnd(@NonNull WindowInsetsAnimation animation) {
+ super.onEnd(animation);
+ if (animation.getTypeMask() == WindowInsets.Type.ime()) {
+ mEntry.mRemoteEditImeVisible =
+ mEditText.getRootWindowInsets().isVisible(WindowInsets.Type.ime());
+ if (!mEntry.mRemoteEditImeVisible && !mEditText.mShowImeOnInputConnection) {
+ mController.removeRemoteInput(mEntry, mToken);
+ }
+ }
+ }
+ });
}
protected Intent prepareRemoteInputFromText() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
index 7042e2f..9669522 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
@@ -150,7 +150,7 @@
callback.setMobileDataIndicators(statusIcon, qsIcon, typeIcon, qsTypeIcon,
mCurrentState.activityIn, mCurrentState.activityOut, dataContentDescription,
dataContentDescriptionHtml, description, icons.isWide,
- mCurrentState.subId, /* roaming= */ false);
+ mCurrentState.subId, /* roaming= */ false, /* showTriangle= */ true);
}
private int getCurrentIconIdForCarrierWifi() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
index 8a412bf..b452d3a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/carrier/QSCarrierGroupControllerTest.java
@@ -218,7 +218,7 @@
mSignalCallback.setMobileDataIndicators(
mock(NetworkController.IconState.class),
mock(NetworkController.IconState.class),
- 0, 0, true, true, "", "", "", true, 0, true);
+ 0, 0, true, true, "", "", "", true, 0, true, true);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
index 63bfd6a..919ddcb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationRoundnessManagerTest.java
@@ -16,8 +16,6 @@
package com.android.systemui.statusbar.notification.stack;
-import static com.android.systemui.statusbar.notification.stack.NotificationRoundnessManager.SMALL_CORNER_RADIUS;
-
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -25,12 +23,14 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.content.res.Resources;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import androidx.test.filters.SmallTest;
+import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -61,10 +61,14 @@
private ExpandableNotificationRow mSecond;
@Mock
private KeyguardBypassController mBypassController;
+ private float mSmallRadiusRatio;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
+ final Resources resources = mContext.getResources();
+ mSmallRadiusRatio = resources.getDimension(R.dimen.notification_corner_radius_small)
+ / resources.getDimension(R.dimen.notification_corner_radius);
mRoundnessManager = new NotificationRoundnessManager(
mBypassController,
new NotificationSectionsFeatureManager(new DeviceConfigProxy(), mContext));
@@ -141,7 +145,7 @@
createSection(null, null)
});
Assert.assertEquals(1.0f, mSecond.getCurrentBottomRoundness(), 0.0f);
- Assert.assertEquals(SMALL_CORNER_RADIUS, mSecond.getCurrentTopRoundness(), 0.0f);
+ Assert.assertEquals(mSmallRadiusRatio, mSecond.getCurrentTopRoundness(), 0.0f);
}
@Test
@@ -168,8 +172,8 @@
row.setHeadsUp(false);
mRoundnessManager.updateView(entry.getRow(), false);
- Assert.assertEquals(SMALL_CORNER_RADIUS, row.getCurrentBottomRoundness(), 0.0f);
- Assert.assertEquals(SMALL_CORNER_RADIUS, row.getCurrentTopRoundness(), 0.0f);
+ Assert.assertEquals(mSmallRadiusRatio, row.getCurrentBottomRoundness(), 0.0f);
+ Assert.assertEquals(mSmallRadiusRatio, row.getCurrentTopRoundness(), 0.0f);
}
@Test
@@ -179,7 +183,7 @@
createSection(null, mSecond)
});
Assert.assertEquals(1.0f, mSecond.getCurrentBottomRoundness(), 0.0f);
- Assert.assertEquals(SMALL_CORNER_RADIUS, mSecond.getCurrentTopRoundness(), 0.0f);
+ Assert.assertEquals(mSmallRadiusRatio, mSecond.getCurrentTopRoundness(), 0.0f);
}
@Test
@@ -188,7 +192,7 @@
createSection(mFirst, mFirst),
createSection(mSecond, null)
});
- Assert.assertEquals(SMALL_CORNER_RADIUS, mSecond.getCurrentBottomRoundness(), 0.0f);
+ Assert.assertEquals(mSmallRadiusRatio, mSecond.getCurrentBottomRoundness(), 0.0f);
Assert.assertEquals(1.0f, mSecond.getCurrentTopRoundness(), 0.0f);
}
@@ -198,7 +202,7 @@
createSection(mFirst, null),
createSection(null, null)
});
- Assert.assertEquals(SMALL_CORNER_RADIUS, mFirst.getCurrentBottomRoundness(), 0.0f);
+ Assert.assertEquals(mSmallRadiusRatio, mFirst.getCurrentBottomRoundness(), 0.0f);
Assert.assertEquals(1.0f, mFirst.getCurrentTopRoundness(), 0.0f);
}
@@ -208,8 +212,8 @@
createSection(mSecond, mSecond),
createSection(null, null)
});
- Assert.assertEquals(SMALL_CORNER_RADIUS, mFirst.getCurrentBottomRoundness(), 0.0f);
- Assert.assertEquals(SMALL_CORNER_RADIUS, mFirst.getCurrentTopRoundness(), 0.0f);
+ Assert.assertEquals(mSmallRadiusRatio, mFirst.getCurrentBottomRoundness(), 0.0f);
+ Assert.assertEquals(mSmallRadiusRatio, mFirst.getCurrentTopRoundness(), 0.0f);
}
@Test
@@ -255,8 +259,8 @@
createSection(mSecond, mSecond),
createSection(null, null)
});
- Assert.assertEquals(SMALL_CORNER_RADIUS, mFirst.getCurrentBottomRoundness(), 0.0f);
- Assert.assertEquals(SMALL_CORNER_RADIUS, mFirst.getCurrentTopRoundness(), 0.0f);
+ Assert.assertEquals(mSmallRadiusRatio, mFirst.getCurrentBottomRoundness(), 0.0f);
+ Assert.assertEquals(mSmallRadiusRatio, mFirst.getCurrentTopRoundness(), 0.0f);
}
@Test
@@ -305,8 +309,8 @@
});
mFirst.setHeadsUpAnimatingAway(true);
mFirst.setHeadsUpAnimatingAway(false);
- Assert.assertEquals(SMALL_CORNER_RADIUS, mFirst.getCurrentBottomRoundness(), 0.0f);
- Assert.assertEquals(SMALL_CORNER_RADIUS, mFirst.getCurrentTopRoundness(), 0.0f);
+ Assert.assertEquals(mSmallRadiusRatio, mFirst.getCurrentBottomRoundness(), 0.0f);
+ Assert.assertEquals(mSmallRadiusRatio, mFirst.getCurrentTopRoundness(), 0.0f);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
index ebc45f4..c212cf3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/CallbackHandlerTest.java
@@ -125,7 +125,7 @@
int subId = 5;
boolean roaming = true;
mHandler.setMobileDataIndicators(status, qs, type, qsType, in, out, typeDescription,
- typeDescriptionHtml, description, wide, subId, roaming);
+ typeDescriptionHtml, description, wide, subId, roaming, true);
waitForCallbacks();
ArgumentCaptor<IconState> statusArg = ArgumentCaptor.forClass(IconState.class);
@@ -143,7 +143,7 @@
Mockito.verify(mSignalCallback).setMobileDataIndicators(statusArg.capture(),
qsArg.capture(), typeIconArg.capture(), qsTypeIconArg.capture(), inArg.capture(),
outArg.capture(), typeContentArg.capture(), typeContentHtmlArg.capture(),
- descArg.capture(), wideArg.capture(), subIdArg.capture(), eq(roaming));
+ descArg.capture(), wideArg.capture(), subIdArg.capture(), eq(roaming), eq(true));
assertEquals(status, statusArg.getValue());
assertEquals(qs, qsArg.getValue());
assertEquals(type, (int) typeIconArg.getValue());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index deabcbe..da1f5d3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -489,7 +489,7 @@
anyInt(),
typeIconArg.capture(), dataInArg.capture(), dataOutArg.capture(),
any(CharSequence.class), any(CharSequence.class), any(CharSequence.class),
- anyBoolean(), anyInt(), anyBoolean());
+ anyBoolean(), anyInt(), anyBoolean(), anyBoolean());
IconState iconState = iconArg.getValue();
int state = SignalDrawable.getState(icon, CellSignalStrength.getNumSignalStrengthLevels(),
false);
@@ -523,7 +523,7 @@
typeIconArg.capture(),
anyInt(), anyBoolean(), anyBoolean(),
any(CharSequence.class), any(CharSequence.class), any(),
- anyBoolean(), anyInt(), eq(roaming));
+ anyBoolean(), anyInt(), eq(roaming), anyBoolean());
IconState iconState = iconArg.getValue();
int state = icon == -1 ? 0
: SignalDrawable.getState(icon, CellSignalStrength.getNumSignalStrengthLevels(),
@@ -544,7 +544,7 @@
typeIconArg.capture(),
anyInt(), anyBoolean(), anyBoolean(),
any(CharSequence.class), any(CharSequence.class), any(),
- anyBoolean(), anyInt(), anyBoolean());
+ anyBoolean(), anyInt(), anyBoolean(), anyBoolean());
IconState iconState = iconArg.getValue();
int state = SignalDrawable.getState(
level, CellSignalStrength.getNumSignalStrengthLevels(), !inet);
@@ -591,7 +591,7 @@
dataOutArg.capture(),
typeContentDescriptionArg.capture(),
typeContentDescriptionHtmlArg.capture(),
- any(), anyBoolean(), anyInt(), anyBoolean());
+ any(), anyBoolean(), anyInt(), anyBoolean(), anyBoolean());
IconState iconState = iconArg.getValue();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
index 2b82f66..10166cb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
@@ -134,7 +134,7 @@
// Still be on wifi though.
setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_WIFI, true, true);
setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_CELLULAR, false, false);
- verifyLastMobileDataIndicators(false, DEFAULT_LEVEL, 0, true);
+ verifyLastMobileDataIndicators(true, DEFAULT_LEVEL, 0, true);
}
@Test
diff --git a/services/Android.bp b/services/Android.bp
index b11a2e8..f6bb72a 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -28,6 +28,7 @@
":services.profcollect-sources",
":services.restrictions-sources",
":services.searchui-sources",
+ ":services.speech-sources",
":services.startop.iorap-sources",
":services.systemcaptions-sources",
":services.translation-sources",
@@ -75,6 +76,7 @@
"services.profcollect",
"services.restrictions",
"services.searchui",
+ "services.speech",
"services.startop",
"services.systemcaptions",
"services.translation",
@@ -139,7 +141,7 @@
last_released: {
api_file: ":android.api.system-server.latest",
removed_api_file: ":removed.api.system-server.latest",
- baseline_file: ":system-server-api-incompatibilities-with-last-released"
+ baseline_file: ":android-incompatibilities.api.system-server.latest"
},
api_lint: {
enabled: true,
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 2f3ad19..6de227e 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -87,6 +87,7 @@
":storaged_aidl",
":vold_aidl",
":platform-compat-config",
+ ":platform-compat-overrides",
":display-device-config",
":cec-config",
":device-state-config",
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 74a6e07..d129b9c 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -5658,7 +5658,9 @@
if (ns == null) {
return;
}
- MatchAllNetworkSpecifier.checkNotMatchAllNetworkSpecifier(ns);
+ if (ns instanceof MatchAllNetworkSpecifier) {
+ throw new IllegalArgumentException("A MatchAllNetworkSpecifier is not permitted");
+ }
}
private void ensureValid(NetworkCapabilities nc) {
@@ -6194,7 +6196,7 @@
nai.networkAgentPortalData = lp.getCaptivePortalData();
}
- private void updateLinkProperties(NetworkAgentInfo networkAgent, LinkProperties newLp,
+ private void updateLinkProperties(NetworkAgentInfo networkAgent, @NonNull LinkProperties newLp,
@NonNull LinkProperties oldLp) {
int netId = networkAgent.network.getNetId();
@@ -6203,8 +6205,7 @@
// the LinkProperties for the network are accurate.
networkAgent.clatd.fixupLinkProperties(oldLp, newLp);
- updateInterfaces(newLp, oldLp, netId, networkAgent.networkCapabilities,
- networkAgent.networkInfo.getType());
+ updateInterfaces(newLp, oldLp, netId, networkAgent.networkCapabilities);
// update filtering rules, need to happen after the interface update so netd knows about the
// new interface (the interface name -> index map becomes initialized)
@@ -6343,7 +6344,7 @@
private void updateInterfaces(final @Nullable LinkProperties newLp,
final @Nullable LinkProperties oldLp, final int netId,
- final @Nullable NetworkCapabilities caps, final int legacyType) {
+ final @NonNull NetworkCapabilities caps) {
final CompareResult<String> interfaceDiff = new CompareResult<>(
oldLp != null ? oldLp.getAllInterfaceNames() : null,
newLp != null ? newLp.getAllInterfaceNames() : null);
@@ -6354,7 +6355,7 @@
if (DBG) log("Adding iface " + iface + " to network " + netId);
mNetd.networkAddInterface(netId, iface);
wakeupModifyInterface(iface, caps, true);
- bs.noteNetworkInterfaceType(iface, legacyType);
+ bs.noteNetworkInterfaceForTransports(iface, caps.getTransportTypes());
} catch (Exception e) {
loge("Exception adding interface: " + e);
}
@@ -6626,6 +6627,7 @@
* maintained here that the NetworkAgent is not aware of (e.g., validated, captive portal,
* and foreground status).
*/
+ @NonNull
private NetworkCapabilities mixInCapabilities(NetworkAgentInfo nai, NetworkCapabilities nc) {
// Once a NetworkAgent is connected, complain if some immutable capabilities are removed.
// Don't complain for VPNs since they're not driven by requests and there is no risk of
@@ -6682,6 +6684,25 @@
return newNc;
}
+ private void updateNetworkInfoForRoamingAndSuspended(NetworkAgentInfo nai,
+ NetworkCapabilities prevNc, NetworkCapabilities newNc) {
+ final boolean prevSuspended = !prevNc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED);
+ final boolean suspended = !newNc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED);
+ final boolean prevRoaming = !prevNc.hasCapability(NET_CAPABILITY_NOT_ROAMING);
+ final boolean roaming = !newNc.hasCapability(NET_CAPABILITY_NOT_ROAMING);
+ if (prevSuspended != suspended) {
+ // TODO (b/73132094) : remove this call once the few users of onSuspended and
+ // onResumed have been removed.
+ notifyNetworkCallbacks(nai, suspended ? ConnectivityManager.CALLBACK_SUSPENDED
+ : ConnectivityManager.CALLBACK_RESUMED);
+ }
+ if (prevSuspended != suspended || prevRoaming != roaming) {
+ // updateNetworkInfo will mix in the suspended info from the capabilities and
+ // take appropriate action for the network having possibly changed state.
+ updateNetworkInfo(nai, nai.networkInfo);
+ }
+ }
+
/**
* Update the NetworkCapabilities for {@code nai} to {@code nc}. Specifically:
*
@@ -6713,25 +6734,13 @@
// on this network. We might have been called by rematchNetworkAndRequests when a
// network changed foreground state.
processListenRequests(nai);
- final boolean prevSuspended = !prevNc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED);
- final boolean suspended = !newNc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED);
- final boolean prevRoaming = !prevNc.hasCapability(NET_CAPABILITY_NOT_ROAMING);
- final boolean roaming = !newNc.hasCapability(NET_CAPABILITY_NOT_ROAMING);
- if (prevSuspended != suspended || prevRoaming != roaming) {
- // TODO (b/73132094) : remove this call once the few users of onSuspended and
- // onResumed have been removed.
- notifyNetworkCallbacks(nai, suspended ? ConnectivityManager.CALLBACK_SUSPENDED
- : ConnectivityManager.CALLBACK_RESUMED);
- // updateNetworkInfo will mix in the suspended info from the capabilities and
- // take appropriate action for the network having possibly changed state.
- updateNetworkInfo(nai, nai.networkInfo);
- }
} else {
// If the requestable capabilities have changed or the score changed, we can't have been
// called by rematchNetworkAndRequests, so it's safe to start a rematch.
rematchAllNetworksAndRequests();
notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_CAP_CHANGED);
}
+ updateNetworkInfoForRoamingAndSuspended(nai, prevNc, newNc);
final boolean oldMetered = prevNc.isMetered();
final boolean newMetered = newNc.isMetered();
diff --git a/services/core/java/com/android/server/EventLogTags.logtags b/services/core/java/com/android/server/EventLogTags.logtags
index b595eb1..9f91dd6 100644
--- a/services/core/java/com/android/server/EventLogTags.logtags
+++ b/services/core/java/com/android/server/EventLogTags.logtags
@@ -1,4 +1,4 @@
-# See system/core/logcat/event.logtags for a description of the format of this file.
+# See system/logging/logcat/event.logtags for a description of the format of this file.
option java_package com.android.server
@@ -173,6 +173,10 @@
3120 pm_critical_info (msg|3)
# Disk usage stats for verifying quota correctness
3121 pm_package_stats (manual_time|2|3),(quota_time|2|3),(manual_data|2|2),(quota_data|2|2),(manual_cache|2|2),(quota_cache|2|2)
+# Snapshot statistics
+3130 pm_snapshot_stats (build_count|1|1),(reuse_count|1|1),(big_builds|1|1),(quick_rebuilds|1|1),(max_build_time|1|3),(cumm_build_time|1|3)
+# Snapshot rebuild instance
+3131 pm_snapshot_rebuild (build_time|1|3),(elapsed|1|3)
# ---------------------------
# InputMethodManagerService.java
diff --git a/services/core/java/com/android/server/TestNetworkService.java b/services/core/java/com/android/server/TestNetworkService.java
index e8687e5..a08d066 100644
--- a/services/core/java/com/android/server/TestNetworkService.java
+++ b/services/core/java/com/android/server/TestNetworkService.java
@@ -242,6 +242,7 @@
nc.addTransportType(NetworkCapabilities.TRANSPORT_TEST);
nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED);
nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
+ nc.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
nc.setNetworkSpecifier(new StringNetworkSpecifier(iface));
nc.setAdministratorUids(administratorUids);
if (!isMetered) {
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 3e80709..97e313e 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -102,7 +102,10 @@
private VibrationScaler mVibrationScaler;
private InputDeviceDelegate mInputDeviceDelegate;
- private volatile VibrationThread mThread;
+ @GuardedBy("mLock")
+ private VibrationThread mThread;
+ @GuardedBy("mLock")
+ private VibrationThread mNextVibrationThread;
@GuardedBy("mLock")
private Vibration mCurrentVibration;
@@ -132,6 +135,10 @@
if (mCurrentVibration != null && mCurrentVibration.id == vibrationId) {
mThread = null;
reportFinishVibrationLocked(status);
+ if (mNextVibrationThread != null) {
+ startVibrationThreadLocked(mNextVibrationThread);
+ mNextVibrationThread = null;
+ }
}
}
}
@@ -258,18 +265,14 @@
@VisibleForTesting
public void onVibrationComplete(int vibratorId, long vibrationId) {
synchronized (mLock) {
- if (mCurrentVibration != null && mCurrentVibration.id == vibrationId) {
+ if (mCurrentVibration != null && mCurrentVibration.id == vibrationId
+ && mThread != null) {
if (DEBUG) {
Slog.d(TAG, "Vibration onComplete callback, notifying VibrationThread");
}
- if (mThread != null) {
- // Let the thread playing the vibration handle the callback, since it might be
- // expecting the vibrator to turn off multiple times during a single vibration.
- mThread.vibratorComplete(vibratorId);
- } else {
- // No vibration is playing in the thread, but clean up service just in case.
- doCancelVibrateLocked(Vibration.Status.FINISHED);
- }
+ // Let the thread playing the vibration handle the callback, since it might be
+ // expecting the vibrator to turn off multiple times during a single vibration.
+ mThread.vibratorComplete(vibratorId);
}
}
}
@@ -462,8 +465,10 @@
try {
doCancelVibrateLocked(Vibration.Status.CANCELLED);
startVibrationLocked(vib);
+ boolean isNextVibration = mNextVibrationThread != null
+ && vib.equals(mNextVibrationThread.getVibration());
- if (!vib.hasEnded() && mCurrentVibration.id != vib.id) {
+ if (!vib.hasEnded() && !vib.equals(mCurrentVibration) && !isNextVibration) {
// Vibration was unexpectedly ignored: add to list for debugging
endVibrationLocked(vib, Vibration.Status.IGNORED);
}
@@ -532,6 +537,7 @@
}
final long ident = Binder.clearCallingIdentity();
try {
+ mNextVibrationThread = null;
doCancelVibrateLocked(Vibration.Status.CANCELLED);
} finally {
Binder.restoreCallingIdentity(ident);
@@ -546,16 +552,14 @@
try {
if (mThread != null) {
mThread.cancel();
- mThread = null;
}
+ mInputDeviceDelegate.cancelVibrateIfAvailable();
if (mCurrentExternalVibration != null) {
endVibrationLocked(mCurrentExternalVibration, status);
mCurrentExternalVibration.externalVibration.mute();
mCurrentExternalVibration = null;
mVibratorController.setExternalControl(false);
}
- doVibratorOff();
- reportFinishVibrationLocked(status);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
}
@@ -579,28 +583,30 @@
private void startVibrationInnerLocked(Vibration vib) {
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "startVibrationInnerLocked");
try {
- // Set current vibration before starting it, so callback will work.
- mCurrentVibration = vib;
- VibrationEffect effect = getEffect(vib);
- Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
boolean inputDevicesAvailable = mInputDeviceDelegate.vibrateIfAvailable(
vib.uid, vib.opPkg, vib.getEffect(), vib.reason, vib.attrs);
if (inputDevicesAvailable) {
- // The set current vibration is no longer being played by this service, so drop it.
- mCurrentVibration = null;
endVibrationLocked(vib, Vibration.Status.FORWARDED_TO_INPUT_DEVICES);
+ } else if (mThread == null) {
+ startVibrationThreadLocked(new VibrationThread(vib, mVibratorController, mWakeLock,
+ mBatteryStatsService, mVibrationCallbacks));
} else {
- // mThread better be null here. doCancelVibrate should always be
- // called before startVibrationInnerLocked
- mThread = new VibrationThread(vib, mVibratorController, mWakeLock,
+ mNextVibrationThread = new VibrationThread(vib, mVibratorController, mWakeLock,
mBatteryStatsService, mVibrationCallbacks);
- mThread.start();
}
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
}
}
+ @GuardedBy("mLock")
+ private void startVibrationThreadLocked(VibrationThread thread) {
+ Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
+ mCurrentVibration = thread.getVibration();
+ mThread = thread;
+ mThread.start();
+ }
+
/** Scale the vibration effect by the intensity as appropriate based its intent. */
private void applyVibrationIntensityScalingLocked(Vibration vib) {
vib.updateEffect(mVibrationScaler.scale(vib.getEffect(), vib.attrs.getUsage()));
@@ -665,13 +671,14 @@
@GuardedBy("mLock")
private void reportFinishVibrationLocked(Vibration.Status status) {
- Trace.asyncTraceEnd(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "reportFinishVibrationLocked");
try {
if (mCurrentVibration != null) {
endVibrationLocked(mCurrentVibration, status);
mAppOps.finishOp(AppOpsManager.OP_VIBRATE, mCurrentVibration.uid,
mCurrentVibration.opPkg);
+
+ Trace.asyncTraceEnd(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
mCurrentVibration = null;
}
} finally {
@@ -697,21 +704,6 @@
}
}
- private void doVibratorOff() {
- Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorOff");
- try {
- if (DEBUG) {
- Slog.d(TAG, "Turning vibrator off.");
- }
- boolean inputDevicesAvailable = mInputDeviceDelegate.cancelVibrateIfAvailable();
- if (!inputDevicesAvailable) {
- mVibratorController.off();
- }
- } finally {
- Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
- }
- }
-
private boolean isSystemHapticFeedback(Vibration vib) {
if (vib.attrs.getUsage() != VibrationAttributes.USAGE_TOUCH) {
return false;
@@ -844,6 +836,7 @@
// haptic feedback as part of the transition. So we don't cancel
// system vibrations.
if (mCurrentVibration != null && !isSystemHapticFeedback(mCurrentVibration)) {
+ mNextVibrationThread = null;
doCancelVibrateLocked(Vibration.Status.CANCELLED);
}
}
@@ -910,6 +903,8 @@
return IExternalVibratorService.SCALE_MUTE;
}
+ VibrationThread cancelingVibration = null;
+ int scale;
synchronized (mLock) {
if (mCurrentExternalVibration != null
&& mCurrentExternalVibration.externalVibration.equals(vib)) {
@@ -920,11 +915,9 @@
if (mCurrentExternalVibration == null) {
// If we're not under external control right now, then cancel any normal
// vibration that may be playing and ready the vibrator for external control.
- if (DEBUG) {
- Slog.d(TAG, "Vibrator going under external control.");
- }
+ mNextVibrationThread = null;
doCancelVibrateLocked(Vibration.Status.CANCELLED);
- mVibratorController.setExternalControl(true);
+ cancelingVibration = mThread;
} else {
endVibrationLocked(mCurrentExternalVibration, Vibration.Status.CANCELLED);
}
@@ -941,11 +934,24 @@
vib.linkToDeath(mCurrentExternalDeathRecipient);
mCurrentExternalVibration.scale = mVibrationScaler.getExternalVibrationScale(
vib.getVibrationAttributes().getUsage());
- if (DEBUG) {
- Slog.e(TAG, "Playing external vibration: " + vib);
- }
- return mCurrentExternalVibration.scale;
+ scale = mCurrentExternalVibration.scale;
}
+ if (cancelingVibration != null) {
+ try {
+ cancelingVibration.join();
+ } catch (InterruptedException e) {
+ Slog.w("Interrupted while waiting current vibration to be cancelled before "
+ + "starting external vibration", e);
+ }
+ }
+ if (DEBUG) {
+ Slog.d(TAG, "Vibrator going under external control.");
+ }
+ mVibratorController.setExternalControl(true);
+ if (DEBUG) {
+ Slog.e(TAG, "Playing external vibration: " + vib);
+ }
+ return scale;
}
@Override
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index b1cbb4a..c287240 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -254,6 +254,8 @@
mStats.setRadioScanningTimeoutLocked(mContext.getResources().getInteger(
com.android.internal.R.integer.config_radioScanningTimeout) * 1000L);
mStats.setPowerProfileLocked(new PowerProfile(context));
+ mStats.startTrackingSystemServerCpuTime();
+
mBatteryUsageStatsProvider = new BatteryUsageStatsProvider(context, mStats);
}
@@ -1674,11 +1676,11 @@
}
@Override
- public void noteNetworkInterfaceType(final String iface, final int networkType) {
+ public void noteNetworkInterfaceForTransports(final String iface, int[] transportTypes) {
enforceCallingPermission();
synchronized (mLock) {
mHandler.post(() -> {
- mStats.noteNetworkInterfaceType(iface, networkType);
+ mStats.noteNetworkInterfaceForTransports(iface, transportTypes);
});
}
}
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index dd09a1c..27a238d 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -80,20 +80,22 @@
// Phenotype sends int configurations and we map them to the strings we'll use on device,
// preventing a weird string value entering the kernel.
+ private static final int COMPACT_ACTION_NONE = 0;
+ private static final int COMPACT_ACTION_FILE = 1;
+ private static final int COMPACT_ACTION_ANON = 2;
+ private static final int COMPACT_ACTION_FULL = 3;
+
+ private static final String COMPACT_ACTION_STRING[] = {"", "file", "anon", "all"};
+
+ // Keeps these flags in sync with services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
private static final int COMPACT_ACTION_FILE_FLAG = 1;
private static final int COMPACT_ACTION_ANON_FLAG = 2;
- private static final int COMPACT_ACTION_FULL_FLAG = 3;
- private static final int COMPACT_ACTION_NONE_FLAG = 4;
- private static final String COMPACT_ACTION_NONE = "";
- private static final String COMPACT_ACTION_FILE = "file";
- private static final String COMPACT_ACTION_ANON = "anon";
- private static final String COMPACT_ACTION_FULL = "all";
// Defaults for phenotype flags.
@VisibleForTesting static final Boolean DEFAULT_USE_COMPACTION = false;
@VisibleForTesting static final Boolean DEFAULT_USE_FREEZER = false;
- @VisibleForTesting static final int DEFAULT_COMPACT_ACTION_1 = COMPACT_ACTION_FILE_FLAG;
- @VisibleForTesting static final int DEFAULT_COMPACT_ACTION_2 = COMPACT_ACTION_FULL_FLAG;
+ @VisibleForTesting static final int DEFAULT_COMPACT_ACTION_1 = COMPACT_ACTION_FILE;
+ @VisibleForTesting static final int DEFAULT_COMPACT_ACTION_2 = COMPACT_ACTION_FULL;
@VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_1 = 5_000;
@VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_2 = 10_000;
@VisibleForTesting static final long DEFAULT_COMPACT_THROTTLE_3 = 500;
@@ -232,6 +234,8 @@
@VisibleForTesting
Handler mCompactionHandler;
private Handler mFreezeHandler;
+ @GuardedBy("mAm")
+ private boolean mFreezerOverride = false;
// Maps process ID to last compaction statistics for processes that we've fully compacted. Used
// when evaluating throttles that we only consider for "full" compaction, so we don't store
@@ -406,6 +410,14 @@
private native void compactSystem();
/**
+ * Compacts a process or app
+ * @param pid pid of process to compact
+ * @param compactionFlags selects the compaction type as defined by COMPACT_ACTION_{TYPE}_FLAG
+ * constants
+ */
+ static private native void compactProcess(int pid, int compactionFlags);
+
+ /**
* Reads the flag value from DeviceConfig to determine whether app compaction
* should be enabled, and starts the freeze/compaction thread if needed.
*/
@@ -454,21 +466,35 @@
}
}
- try {
- enableFreezerInternal(enable);
- return true;
- } catch (java.lang.RuntimeException e) {
- if (enable) {
- mFreezerDisableCount = 0;
- } else {
- mFreezerDisableCount = 1;
- }
+ // Override is applied immediately, restore is delayed
+ synchronized (mAm) {
+ int processCount = mAm.mProcessList.mLruProcesses.size();
- Slog.e(TAG_AM, "Exception handling freezer state (enable: " + enable + "): "
- + e.toString());
+ mFreezerOverride = !enable;
+ Slog.d(TAG_AM, "freezer override set to " + mFreezerOverride);
+
+ for (int i = 0; i < processCount; i++) {
+ ProcessRecord process = mAm.mProcessList.mLruProcesses.get(i);
+
+ if (process == null) {
+ continue;
+ }
+
+ if (enable && process.freezerOverride) {
+ freezeAppAsync(process);
+ process.freezerOverride = false;
+ }
+
+ if (!enable && process.frozen) {
+ unfreezeAppLocked(process);
+
+ // Set freezerOverride *after* calling unfreezeAppLocked (it resets the flag)
+ process.freezerOverride = true;
+ }
+ }
}
- return false;
+ return true;
}
/**
@@ -515,7 +541,7 @@
FileReader fr = null;
try {
- fr = new FileReader("/sys/fs/cgroup/freezer/cgroup.freeze");
+ fr = new FileReader("/sys/fs/cgroup/uid_0/cgroup.freeze");
char state = (char) fr.read();
if (state == '1' || state == '0') {
@@ -706,18 +732,11 @@
@VisibleForTesting
static String compactActionIntToString(int action) {
- switch(action) {
- case COMPACT_ACTION_NONE_FLAG:
- return COMPACT_ACTION_NONE;
- case COMPACT_ACTION_FILE_FLAG:
- return COMPACT_ACTION_FILE;
- case COMPACT_ACTION_ANON_FLAG:
- return COMPACT_ACTION_ANON;
- case COMPACT_ACTION_FULL_FLAG:
- return COMPACT_ACTION_FULL;
- default:
- return COMPACT_ACTION_NONE;
+ if (action < 0 || action >= COMPACT_ACTION_STRING.length) {
+ return "";
}
+
+ return COMPACT_ACTION_STRING[action];
}
// This will ensure app will be out of the freezer for at least FREEZE_TIMEOUT_MS
@@ -744,6 +763,8 @@
void unfreezeAppLocked(ProcessRecord app) {
mFreezeHandler.removeMessages(SET_FROZEN_PROCESS_MSG, app);
+ app.freezerOverride = false;
+
if (!app.frozen) {
if (DEBUG_FREEZER) {
Slog.d(TAG_AM,
@@ -753,6 +774,8 @@
return;
}
+ // Unfreeze the binder interface first, to avoid transactions triggered by timers fired
+ // right after unfreezing the process to fail
boolean processKilled = false;
try {
@@ -950,11 +973,11 @@
action = mCompactActionFull;
break;
default:
- action = COMPACT_ACTION_NONE;
+ action = COMPACT_ACTION_STRING[COMPACT_ACTION_NONE];
break;
}
- if (COMPACT_ACTION_NONE.equals(action)) {
+ if (COMPACT_ACTION_STRING[COMPACT_ACTION_NONE].equals(action)) {
return;
}
@@ -978,7 +1001,8 @@
return;
}
- if (action.equals(COMPACT_ACTION_FULL) || action.equals(COMPACT_ACTION_ANON)) {
+ if (action.equals(COMPACT_ACTION_STRING[COMPACT_ACTION_FULL])
+ || action.equals(COMPACT_ACTION_STRING[COMPACT_ACTION_ANON])) {
if (mFullAnonRssThrottleKb > 0L
&& anonRssBefore < mFullAnonRssThrottleKb) {
if (DEBUG_COMPACTION) {
@@ -1054,8 +1078,8 @@
proc.lastCompactTime = end;
proc.lastCompactAction = pendingAction;
}
- if (action.equals(COMPACT_ACTION_FULL)
- || action.equals(COMPACT_ACTION_ANON)) {
+ if (action.equals(COMPACT_ACTION_STRING[COMPACT_ACTION_FULL])
+ || action.equals(COMPACT_ACTION_STRING[COMPACT_ACTION_ANON])) {
// Remove entry and insert again to update insertion order.
mLastCompactionStats.remove(pid);
mLastCompactionStats.put(pid, new LastCompactionStats(rssAfter));
@@ -1131,12 +1155,31 @@
return;
}
+ if (mFreezerOverride) {
+ proc.freezerOverride = true;
+ Slog.d(TAG_AM, "Skipping freeze for process " + pid
+ + " " + name + " curAdj = " + proc.curAdj
+ + "(override)");
+ return;
+ }
+
if (pid == 0 || proc.frozen) {
// Already frozen or not a real process, either one being
// launched or one being killed
return;
}
+ // Freeze binder interface before the process, to flush any
+ // transactions that might be pending.
+ try {
+ freezeBinder(pid, true);
+ } catch (RuntimeException e) {
+ Slog.e(TAG_AM, "Unable to freeze binder for " + pid + " " + name);
+ proc.kill("Unable to freeze binder interface",
+ ApplicationExitInfo.REASON_OTHER,
+ ApplicationExitInfo.SUBREASON_INVALID_STATE, true);
+ }
+
long unfreezeTime = proc.freezeUnfreezeTime;
try {
@@ -1163,15 +1206,6 @@
EventLog.writeEvent(EventLogTags.AM_FREEZE, pid, name);
- try {
- freezeBinder(pid, true);
- } catch (RuntimeException e) {
- Slog.e(TAG_AM, "Unable to freeze binder for " + pid + " " + name);
- proc.kill("Unable to freeze binder interface",
- ApplicationExitInfo.REASON_OTHER,
- ApplicationExitInfo.SUBREASON_INVALID_STATE, true);
- }
-
// See above for why we're not taking mPhenotypeFlagLock here
if (mRandom.nextFloat() < mFreezerStatsdSampleRate) {
FrameworkStatsLog.write(FrameworkStatsLog.APP_FREEZE_CHANGED,
@@ -1229,8 +1263,12 @@
// Compact process.
@Override
public void performCompaction(String action, int pid) throws IOException {
- try (FileOutputStream fos = new FileOutputStream("/proc/" + pid + "/reclaim")) {
- fos.write(action.getBytes());
+ if (action.equals(COMPACT_ACTION_STRING[COMPACT_ACTION_FULL])) {
+ compactProcess(pid, COMPACT_ACTION_FILE_FLAG | COMPACT_ACTION_ANON_FLAG);
+ } else if (action.equals(COMPACT_ACTION_STRING[COMPACT_ACTION_FILE])) {
+ compactProcess(pid, COMPACT_ACTION_FILE_FLAG);
+ } else if (action.equals(COMPACT_ACTION_STRING[COMPACT_ACTION_ANON])) {
+ compactProcess(pid, COMPACT_ACTION_ANON_FLAG);
}
}
}
diff --git a/services/core/java/com/android/server/am/PhantomProcessList.java b/services/core/java/com/android/server/am/PhantomProcessList.java
index 5167c57..37b1741 100644
--- a/services/core/java/com/android/server/am/PhantomProcessList.java
+++ b/services/core/java/com/android/server/am/PhantomProcessList.java
@@ -38,6 +38,7 @@
import libcore.io.IoUtils;
+import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
@@ -109,10 +110,23 @@
private final ActivityManagerService mService;
private final Handler mKillHandler;
+ private static final int CGROUP_V1 = 0;
+ private static final int CGROUP_V2 = 1;
+ private static final String[] CGROUP_PATH_PREFIXES = {
+ "/acct/uid_" /* cgroup v1 */,
+ "/sys/fs/cgroup/uid_" /* cgroup v2 */
+ };
+ private static final String CGROUP_PID_PREFIX = "/pid_";
+ private static final String CGROUP_PROCS = "/cgroup.procs";
+
+ @VisibleForTesting
+ int mCgroupVersion = CGROUP_V1;
+
PhantomProcessList(final ActivityManagerService service) {
mService = service;
mKillHandler = service.mProcessList.sKillHandler;
mInjector = new Injector();
+ probeCgroupVersion();
}
@VisibleForTesting
@@ -190,9 +204,18 @@
}
}
+ private void probeCgroupVersion() {
+ for (int i = CGROUP_PATH_PREFIXES.length - 1; i >= 0; i--) {
+ if ((new File(CGROUP_PATH_PREFIXES[i] + Process.SYSTEM_UID)).exists()) {
+ mCgroupVersion = i;
+ break;
+ }
+ }
+ }
+
@VisibleForTesting
- static String getCgroupFilePath(int uid, int pid) {
- return "/acct/uid_" + uid + "/pid_" + pid + "/cgroup.procs";
+ String getCgroupFilePath(int uid, int pid) {
+ return CGROUP_PATH_PREFIXES[mCgroupVersion] + uid + CGROUP_PID_PREFIX + pid + CGROUP_PROCS;
}
static String getProcessName(int pid) {
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 520a28b..63195d3 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -179,6 +179,7 @@
int reqCompactAction; // The most recent compaction action requested for this app.
int lastCompactAction; // The most recent compaction action performed for this app.
boolean frozen; // True when the process is frozen.
+ boolean freezerOverride; // An override on the freeze state is in progress.
long freezeUnfreezeTime; // Last time the app was (un)frozen, 0 for never
boolean shouldNotFreeze; // True if a process has a WPRI binding from an unfrozen process
private int mCurSchedGroup; // Currently desired scheduling class
diff --git a/services/core/java/com/android/server/compat/CompatChange.java b/services/core/java/com/android/server/compat/CompatChange.java
index 9ba957e..e3757df 100644
--- a/services/core/java/com/android/server/compat/CompatChange.java
+++ b/services/core/java/com/android/server/compat/CompatChange.java
@@ -23,8 +23,11 @@
import com.android.internal.compat.CompatibilityChangeInfo;
import com.android.server.compat.config.Change;
+import com.android.server.compat.overrides.ChangeOverrides;
+import com.android.server.compat.overrides.OverrideValue;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
/**
@@ -253,6 +256,71 @@
return mDeferredOverrides != null && mDeferredOverrides.containsKey(packageName);
}
+ /**
+ * Checks whether a change has any package overrides.
+ * @return true if the change has at least one deferred override
+ */
+ boolean hasAnyPackageOverride() {
+ return mDeferredOverrides != null && !mDeferredOverrides.isEmpty();
+ }
+
+ /**
+ * Checks whether a change has any deferred overrides.
+ * @return true if the change has at least one deferred override
+ */
+ boolean hasAnyDeferredOverride() {
+ return mPackageOverrides != null && !mPackageOverrides.isEmpty();
+ }
+
+ void loadOverrides(ChangeOverrides changeOverrides) {
+ if (mDeferredOverrides == null) {
+ mDeferredOverrides = new HashMap<>();
+ }
+ mDeferredOverrides.clear();
+ for (OverrideValue override : changeOverrides.getDeferred().getOverrideValue()) {
+ mDeferredOverrides.put(override.getPackageName(), override.getEnabled());
+ }
+
+ if (mPackageOverrides == null) {
+ mPackageOverrides = new HashMap<>();
+ }
+ mPackageOverrides.clear();
+ for (OverrideValue override : changeOverrides.getValidated().getOverrideValue()) {
+ mPackageOverrides.put(override.getPackageName(), override.getEnabled());
+ }
+ }
+
+ ChangeOverrides saveOverrides() {
+ if (!hasAnyDeferredOverride() && !hasAnyPackageOverride()) {
+ return null;
+ }
+ ChangeOverrides changeOverrides = new ChangeOverrides();
+ changeOverrides.setChangeId(getId());
+ ChangeOverrides.Deferred deferredOverrides = new ChangeOverrides.Deferred();
+ List<OverrideValue> deferredList = deferredOverrides.getOverrideValue();
+ if (mDeferredOverrides != null) {
+ for (Map.Entry<String, Boolean> entry : mDeferredOverrides.entrySet()) {
+ OverrideValue override = new OverrideValue();
+ override.setPackageName(entry.getKey());
+ override.setEnabled(entry.getValue());
+ deferredList.add(override);
+ }
+ }
+ changeOverrides.setDeferred(deferredOverrides);
+ ChangeOverrides.Validated validatedOverrides = new ChangeOverrides.Validated();
+ List<OverrideValue> validatedList = validatedOverrides.getOverrideValue();
+ if (mPackageOverrides != null) {
+ for (Map.Entry<String, Boolean> entry : mPackageOverrides.entrySet()) {
+ OverrideValue override = new OverrideValue();
+ override.setPackageName(entry.getKey());
+ override.setEnabled(entry.getValue());
+ validatedList.add(override);
+ }
+ }
+ changeOverrides.setValidated(validatedOverrides);
+ return changeOverrides;
+ }
+
@Override
public String toString() {
StringBuilder sb = new StringBuilder("ChangeId(")
diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java
index 69686a2..6b77b9d 100644
--- a/services/core/java/com/android/server/compat/CompatConfig.java
+++ b/services/core/java/com/android/server/compat/CompatConfig.java
@@ -34,7 +34,10 @@
import com.android.internal.compat.IOverrideValidator;
import com.android.internal.compat.OverrideAllowedState;
import com.android.server.compat.config.Change;
-import com.android.server.compat.config.XmlParser;
+import com.android.server.compat.config.Config;
+import com.android.server.compat.overrides.ChangeOverrides;
+import com.android.server.compat.overrides.Overrides;
+import com.android.server.compat.overrides.XmlWriter;
import com.android.server.pm.ApexManager;
import org.xmlpull.v1.XmlPullParserException;
@@ -60,11 +63,14 @@
final class CompatConfig {
private static final String TAG = "CompatConfig";
+ private static final String APP_COMPAT_DATA_DIR = "/data/misc/appcompat";
+ private static final String OVERRIDES_FILE = "compat_framework_overrides.xml";
@GuardedBy("mChanges")
private final LongSparseArray<CompatChange> mChanges = new LongSparseArray<>();
private final OverrideValidatorImpl mOverrideValidator;
+ private File mOverridesFile;
@VisibleForTesting
CompatConfig(AndroidBuildClassifier androidBuildClassifier, Context context) {
@@ -83,6 +89,8 @@
config.initConfigFromLib(Environment.buildPath(
apex.apexDirectory, "etc", "compatconfig"));
}
+ File overridesFile = new File(APP_COMPAT_DATA_DIR, OVERRIDES_FILE);
+ config.initOverrides(overridesFile);
config.invalidateCache();
return config;
}
@@ -202,6 +210,17 @@
* @throws IllegalStateException if overriding is not allowed
*/
boolean addOverride(long changeId, String packageName, boolean enabled) {
+ boolean alreadyKnown = addOverrideUnsafe(changeId, packageName, enabled);
+ saveOverrides();
+ invalidateCache();
+ return alreadyKnown;
+ }
+
+ /**
+ * Unsafe version of {@link #addOverride(long, String, boolean)}.
+ * It does not invalidate the cache nor save the overrides.
+ */
+ private boolean addOverrideUnsafe(long changeId, String packageName, boolean enabled) {
boolean alreadyKnown = true;
OverrideAllowedState allowedState =
mOverrideValidator.getOverrideAllowedState(changeId, packageName);
@@ -224,7 +243,6 @@
throw new IllegalStateException("Should only be able to override changes that "
+ "are allowed or can be deferred.");
}
- invalidateCache();
}
return alreadyKnown;
}
@@ -282,6 +300,17 @@
* @return {@code true} if an override existed;
*/
boolean removeOverride(long changeId, String packageName) {
+ boolean overrideExists = removeOverrideUnsafe(changeId, packageName);
+ saveOverrides();
+ invalidateCache();
+ return overrideExists;
+ }
+
+ /**
+ * Unsafe version of {@link #removeOverride(long, String)}.
+ * It does not invalidate the cache nor save the overrides.
+ */
+ private boolean removeOverrideUnsafe(long changeId, String packageName) {
boolean overrideExists = false;
synchronized (mChanges) {
CompatChange c = mChanges.get(changeId);
@@ -300,7 +329,6 @@
}
}
}
- invalidateCache();
return overrideExists;
}
@@ -315,12 +343,13 @@
void addOverrides(CompatibilityChangeConfig overrides, String packageName) {
synchronized (mChanges) {
for (Long changeId : overrides.enabledChanges()) {
- addOverride(changeId, packageName, true);
+ addOverrideUnsafe(changeId, packageName, true);
}
for (Long changeId : overrides.disabledChanges()) {
- addOverride(changeId, packageName, false);
+ addOverrideUnsafe(changeId, packageName, false);
}
+ saveOverrides();
invalidateCache();
}
}
@@ -337,8 +366,9 @@
synchronized (mChanges) {
for (int i = 0; i < mChanges.size(); ++i) {
CompatChange change = mChanges.valueAt(i);
- removeOverride(change.getId(), packageName);
+ removeOverrideUnsafe(change.getId(), packageName);
}
+ saveOverrides();
invalidateCache();
}
}
@@ -372,8 +402,10 @@
int enableTargetSdkChangesForPackage(String packageName, int targetSdkVersion) {
long[] changes = getAllowedChangesSinceTargetSdkForPackage(packageName, targetSdkVersion);
for (long changeId : changes) {
- addOverride(changeId, packageName, true);
+ addOverrideUnsafe(changeId, packageName, true);
}
+ saveOverrides();
+ invalidateCache();
return changes.length;
}
@@ -386,8 +418,10 @@
int disableTargetSdkChangesForPackage(String packageName, int targetSdkVersion) {
long[] changes = getAllowedChangesSinceTargetSdkForPackage(packageName, targetSdkVersion);
for (long changeId : changes) {
- addOverride(changeId, packageName, false);
+ addOverrideUnsafe(changeId, packageName, false);
}
+ saveOverrides();
+ invalidateCache();
return changes.length;
}
@@ -494,7 +528,8 @@
private void readConfig(File configFile) {
try (InputStream in = new BufferedInputStream(new FileInputStream(configFile))) {
- for (Change change : XmlParser.read(in).getCompatChange()) {
+ Config config = com.android.server.compat.config.XmlParser.read(in);
+ for (Change change : config.getCompatChange()) {
Slog.d(TAG, "Adding: " + change.toString());
addChange(new CompatChange(change));
}
@@ -503,6 +538,65 @@
}
}
+ void initOverrides(File overridesFile) {
+ if (!overridesFile.exists()) {
+ mOverridesFile = overridesFile;
+ // There have not been any overrides added yet.
+ return;
+ }
+
+ try (InputStream in = new BufferedInputStream(new FileInputStream(overridesFile))) {
+ Overrides overrides = com.android.server.compat.overrides.XmlParser.read(in);
+ for (ChangeOverrides changeOverrides : overrides.getChangeOverrides()) {
+ long changeId = changeOverrides.getChangeId();
+ CompatChange compatChange = mChanges.get(changeId);
+ if (compatChange == null) {
+ Slog.w(TAG, "Change ID " + changeId + " not found. "
+ + "Skipping overrides for it.");
+ continue;
+ }
+ compatChange.loadOverrides(changeOverrides);
+ }
+ } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) {
+ Slog.w(TAG, "Error processing " + overridesFile + " " + e.toString());
+ return;
+ }
+ mOverridesFile = overridesFile;
+ }
+
+ /**
+ * Persist compat framework overrides to /data/misc/appcompat/compat_framework_overrides.xml
+ */
+ void saveOverrides() {
+ if (mOverridesFile == null) {
+ return;
+ }
+ synchronized (mChanges) {
+ // Create the file if it doesn't already exist
+ try {
+ mOverridesFile.createNewFile();
+ } catch (IOException e) {
+ Slog.e(TAG, "Could not create override config file: " + e.toString());
+ return;
+ }
+ try (PrintWriter out = new PrintWriter(mOverridesFile)) {
+ XmlWriter writer = new XmlWriter(out);
+ Overrides overrides = new Overrides();
+ List<ChangeOverrides> changeOverridesList = overrides.getChangeOverrides();
+ for (int idx = 0; idx < mChanges.size(); ++idx) {
+ CompatChange c = mChanges.valueAt(idx);
+ ChangeOverrides changeOverrides = c.saveOverrides();
+ if (changeOverrides != null) {
+ changeOverridesList.add(changeOverrides);
+ }
+ }
+ XmlWriter.write(writer, overrides);
+ } catch (IOException e) {
+ Slog.e(TAG, e.toString());
+ }
+ }
+ }
+
IOverrideValidator getOverrideValidator() {
return mOverrideValidator;
}
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index ab0360b..b282484 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -329,7 +329,7 @@
private final QosCallbackTracker mQosCallbackTracker;
public NetworkAgentInfo(INetworkAgent na, Network net, NetworkInfo info,
- LinkProperties lp, NetworkCapabilities nc, int score, Context context,
+ @NonNull LinkProperties lp, @NonNull NetworkCapabilities nc, int score, Context context,
Handler handler, NetworkAgentConfig config, ConnectivityService connService, INetd netd,
IDnsResolver dnsResolver, INetworkManagementService nms, int factorySerialNumber,
int creatorUid, QosCallbackTracker qosCallbackTracker) {
diff --git a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java b/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
index a7be657..5e6b9f3 100644
--- a/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
+++ b/services/core/java/com/android/server/connectivity/NetworkDiagnostics.java
@@ -686,7 +686,7 @@
mHostname = hostname;
mMeasurement.description = "DNS TLS dst{" + mTarget.getHostAddress() + "} hostname{"
- + TextUtils.emptyIfNull(mHostname) + "}";
+ + (mHostname == null ? "" : mHostname) + "}";
}
private SSLSocket setupSSLSocket() throws IOException {
diff --git a/services/core/java/com/android/server/display/DisplayGroup.java b/services/core/java/com/android/server/display/DisplayGroup.java
index 2ba8758..a11a745 100644
--- a/services/core/java/com/android/server/display/DisplayGroup.java
+++ b/services/core/java/com/android/server/display/DisplayGroup.java
@@ -26,8 +26,6 @@
*/
public class DisplayGroup {
- public static final int DEFAULT = 0;
-
private final List<LogicalDisplay> mDisplays = new ArrayList<>();
private final int mGroupId;
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 55103ca..c3f8d8c 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -1357,7 +1357,7 @@
// Scan supported modes returned by display.getInfo() to find a mode with the same
// size as the default display mode but with the specified refresh rate instead.
requestedModeId = display.getDisplayInfoLocked().findDefaultModeByRefreshRate(
- requestedRefreshRate);
+ requestedRefreshRate).getModeId();
}
mDisplayModeDirector.getAppRequestObserver().setAppRequestedMode(
displayId, requestedModeId);
@@ -1538,6 +1538,14 @@
}
}
+ void setDisplayModeDirectorLoggingEnabled(boolean enabled) {
+ synchronized (mSyncRoot) {
+ if (mDisplayModeDirector != null) {
+ mDisplayModeDirector.setLoggingEnabled(enabled);
+ }
+ }
+ }
+
void setAmbientColorTemperatureOverride(float cct) {
synchronized (mSyncRoot) {
final DisplayPowerController displayPowerController = mDisplayPowerControllers.get(
diff --git a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
index 111664a..aaea15a 100644
--- a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
+++ b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
@@ -54,6 +54,10 @@
return setDisplayWhiteBalanceLoggingEnabled(true);
case "dwb-logging-disable":
return setDisplayWhiteBalanceLoggingEnabled(false);
+ case "dmd-logging-enable":
+ return setDisplayModeDirectorLoggingEnabled(true);
+ case "dmd-logging-disable":
+ return setDisplayModeDirectorLoggingEnabled(false);
case "dwb-set-cct":
return setAmbientColorTemperatureOverride();
case "set-fold":
@@ -82,6 +86,10 @@
pw.println(" Enable display white-balance logging.");
pw.println(" dwb-logging-disable");
pw.println(" Disable display white-balance logging.");
+ pw.println(" dmd-logging-enable");
+ pw.println(" Enable display mode director logging.");
+ pw.println(" dmd-logging-disable");
+ pw.println(" Disable display mode director logging.");
pw.println(" dwb-set-cct CCT");
pw.println(" Sets the ambient color temperature override to CCT (use -1 to disable).");
pw.println(" set-fold [fold|unfold|reset]");
@@ -136,6 +144,11 @@
return 0;
}
+ private int setDisplayModeDirectorLoggingEnabled(boolean enabled) {
+ mService.setDisplayModeDirectorLoggingEnabled(enabled);
+ return 0;
+ }
+
private int setAmbientColorTemperatureOverride() {
String cctText = getNextArg();
if (cctText == null) {
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index 006f875..dce6bd8 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -67,7 +67,7 @@
*/
public class DisplayModeDirector {
private static final String TAG = "DisplayModeDirector";
- private static final boolean DEBUG = false;
+ private boolean mLoggingEnabled;
private static final int MSG_REFRESH_RATE_RANGE_CHANGED = 1;
private static final int MSG_LOW_BRIGHTNESS_THRESHOLDS_CHANGED = 2;
@@ -155,6 +155,14 @@
}
}
+ public void setLoggingEnabled(boolean loggingEnabled) {
+ if (mLoggingEnabled == loggingEnabled) {
+ return;
+ }
+ mLoggingEnabled = loggingEnabled;
+ mBrightnessObserver.setLoggingEnabled(loggingEnabled);
+ }
+
@NonNull
private SparseArray<Vote> getVotesLocked(int displayId) {
SparseArray<Vote> displayVotes = mVotesByDisplay.get(displayId);
@@ -269,7 +277,7 @@
availableModes = filterModes(modes, primarySummary);
if (availableModes.length > 0) {
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.w(TAG, "Found available modes=" + Arrays.toString(availableModes)
+ " with lowest priority considered "
+ Vote.priorityToString(lowestConsideredPriority)
@@ -282,7 +290,7 @@
break;
}
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.w(TAG, "Couldn't find available modes with lowest priority set to "
+ Vote.priorityToString(lowestConsideredPriority)
+ " and with the following constraints: "
@@ -307,7 +315,7 @@
Math.min(appRequestSummary.minRefreshRate, primarySummary.minRefreshRate);
appRequestSummary.maxRefreshRate =
Math.max(appRequestSummary.maxRefreshRate, primarySummary.maxRefreshRate);
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.i(TAG,
String.format("App request range: [%.0f %.0f]",
appRequestSummary.minRefreshRate,
@@ -357,7 +365,7 @@
for (Display.Mode mode : supportedModes) {
if (mode.getPhysicalWidth() != summary.width
|| mode.getPhysicalHeight() != summary.height) {
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.w(TAG, "Discarding mode " + mode.getModeId() + ", wrong size"
+ ": desiredWidth=" + summary.width
+ ": desiredHeight=" + summary.height
@@ -372,7 +380,7 @@
// comparison.
if (refreshRate < (summary.minRefreshRate - FLOAT_TOLERANCE)
|| refreshRate > (summary.maxRefreshRate + FLOAT_TOLERANCE)) {
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.w(TAG, "Discarding mode " + mode.getModeId()
+ ", outside refresh rate bounds"
+ ": minRefreshRate=" + summary.minRefreshRate
@@ -516,7 +524,7 @@
}
private void updateVoteLocked(int displayId, int priority, Vote vote) {
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.i(TAG, "updateVoteLocked(displayId=" + displayId
+ ", priority=" + Vote.priorityToString(priority)
+ ", vote=" + vote + ")");
@@ -537,7 +545,7 @@
}
if (votes.size() == 0) {
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.i(TAG, "No votes left for display " + displayId + ", removing.");
}
mVotesByDisplay.remove(displayId);
@@ -1287,6 +1295,7 @@
private boolean mShouldObserveAmbientLowChange;
private boolean mShouldObserveDisplayHighChange;
private boolean mShouldObserveAmbientHighChange;
+ private boolean mLoggingEnabled;
private SensorManager mSensorManager;
private Sensor mLightSensor;
@@ -1303,7 +1312,6 @@
// changeable and low power mode off. After initialization, these states will
// be updated from the same handler thread.
private int mDefaultDisplayState = Display.STATE_UNKNOWN;
- private boolean mIsDeviceActive = false;
private boolean mRefreshRateChangeable = false;
private boolean mLowPowerModeEnabled = false;
@@ -1415,6 +1423,14 @@
mDeviceConfigDisplaySettings.startListening();
}
+ public void setLoggingEnabled(boolean loggingEnabled) {
+ if (mLoggingEnabled == loggingEnabled) {
+ return;
+ }
+ mLoggingEnabled = loggingEnabled;
+ mLightSensorListener.setLoggingEnabled(loggingEnabled);
+ }
+
public void onRefreshRateSettingChangedLocked(float min, float max) {
boolean changeable = (max - min > 1f && max > 60f);
if (mRefreshRateChangeable != changeable) {
@@ -1485,7 +1501,6 @@
pw.println(" mAmbientLux: " + mAmbientLux);
pw.println(" mBrightness: " + mBrightness);
pw.println(" mDefaultDisplayState: " + mDefaultDisplayState);
- pw.println(" mIsDeviceActive: " + mIsDeviceActive);
pw.println(" mLowPowerModeEnabled: " + mLowPowerModeEnabled);
pw.println(" mRefreshRateChangeable: " + mRefreshRateChangeable);
pw.println(" mShouldObserveDisplayLowChange: " + mShouldObserveDisplayLowChange);
@@ -1691,7 +1706,7 @@
vote = Vote.forRefreshRates(mRefreshRateInHighZone, mRefreshRateInHighZone);
}
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "Display brightness " + mBrightness + ", ambient lux " + mAmbientLux
+ ", Vote " + vote);
}
@@ -1720,6 +1735,11 @@
@VisibleForTesting
public void setDefaultDisplayState(int state) {
+ if (mLoggingEnabled) {
+ Slog.d(TAG, "setDefaultDisplayState: mDefaultDisplayState = "
+ + mDefaultDisplayState + ", state = " + state);
+ }
+
if (mDefaultDisplayState != state) {
mDefaultDisplayState = state;
updateSensorStatus();
@@ -1731,36 +1751,58 @@
return;
}
+ if (mLoggingEnabled) {
+ Slog.d(TAG, "updateSensorStatus: mShouldObserveAmbientLowChange = "
+ + mShouldObserveAmbientLowChange + ", mShouldObserveAmbientHighChange = "
+ + mShouldObserveAmbientHighChange);
+ Slog.d(TAG, "updateSensorStatus: mLowPowerModeEnabled = "
+ + mLowPowerModeEnabled + ", mRefreshRateChangeable = "
+ + mRefreshRateChangeable);
+ }
+
if ((mShouldObserveAmbientLowChange || mShouldObserveAmbientHighChange)
&& isDeviceActive() && !mLowPowerModeEnabled && mRefreshRateChangeable) {
mSensorManager.registerListener(mLightSensorListener,
mLightSensor, LIGHT_SENSOR_RATE_MS * 1000, mHandler);
+ if (mLoggingEnabled) {
+ Slog.d(TAG, "updateSensorStatus: registerListener");
+ }
} else {
mLightSensorListener.removeCallbacks();
mSensorManager.unregisterListener(mLightSensorListener);
+ if (mLoggingEnabled) {
+ Slog.d(TAG, "updateSensorStatus: unregisterListener");
+ }
}
}
private boolean isDeviceActive() {
- mIsDeviceActive = mInjector.isDeviceInteractive(mContext);
- return (mDefaultDisplayState == Display.STATE_ON)
- && mIsDeviceActive;
+ return mDefaultDisplayState == Display.STATE_ON;
}
private final class LightSensorEventListener implements SensorEventListener {
final private static int INJECT_EVENTS_INTERVAL_MS = LIGHT_SENSOR_RATE_MS;
private float mLastSensorData;
private long mTimestamp;
+ private boolean mLoggingEnabled;
public void dumpLocked(PrintWriter pw) {
pw.println(" mLastSensorData: " + mLastSensorData);
pw.println(" mTimestamp: " + formatTimestamp(mTimestamp));
}
+
+ public void setLoggingEnabled(boolean loggingEnabled) {
+ if (mLoggingEnabled == loggingEnabled) {
+ return;
+ }
+ mLoggingEnabled = loggingEnabled;
+ }
+
@Override
public void onSensorChanged(SensorEvent event) {
mLastSensorData = event.values[0];
- if (DEBUG) {
+ if (mLoggingEnabled) {
Slog.d(TAG, "On sensor changed: " + mLastSensorData);
}
@@ -2009,8 +2051,6 @@
void registerPeakRefreshRateObserver(@NonNull ContentResolver cr,
@NonNull ContentObserver observer);
-
- boolean isDeviceInteractive(@NonNull Context context);
}
@VisibleForTesting
@@ -2041,11 +2081,6 @@
cr.registerContentObserver(PEAK_REFRESH_RATE_URI, false /*notifyDescendants*/,
observer, UserHandle.USER_SYSTEM);
}
-
- @Override
- public boolean isDeviceInteractive(@NonNull Context ctx) {
- return ctx.getSystemService(PowerManager.class).isInteractive();
- }
}
}
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index 5bf83db..86de159 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -71,6 +71,9 @@
private final int mDisplayId;
private final int mLayerStack;
+
+ private int mDisplayGroupId = Display.INVALID_DISPLAY_GROUP;
+
/**
* Override information set by the window manager. Will be reported instead of {@link #mInfo}
* if not null.
@@ -265,6 +268,19 @@
}
/**
+ * Updates the {@link DisplayGroup} to which the logical display belongs.
+ *
+ * @param groupId Identifier for the {@link DisplayGroup}.
+ */
+ public void updateDisplayGroupIdLocked(int groupId) {
+ if (groupId != mDisplayGroupId) {
+ mDisplayGroupId = groupId;
+ mBaseDisplayInfo.displayGroupId = groupId;
+ mInfo.set(null);
+ }
+ }
+
+ /**
* Updates the state of the logical display based on the available display devices.
* The logical display might become invalid if it is attached to a display device
* that no longer exists.
@@ -365,6 +381,7 @@
(deviceInfo.flags & DisplayDeviceInfo.FLAG_MASK_DISPLAY_CUTOUT) != 0;
mBaseDisplayInfo.displayCutout = maskCutout ? null : deviceInfo.displayCutout;
mBaseDisplayInfo.displayId = mDisplayId;
+ mBaseDisplayInfo.displayGroupId = mDisplayGroupId;
updateFrameRateOverrides(deviceInfo);
mBaseDisplayInfo.brightnessMinimum = deviceInfo.brightnessMinimum;
mBaseDisplayInfo.brightnessMaximum = deviceInfo.brightnessMaximum;
diff --git a/services/core/java/com/android/server/display/LogicalDisplayMapper.java b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
index bb2fbed..e738878 100644
--- a/services/core/java/com/android/server/display/LogicalDisplayMapper.java
+++ b/services/core/java/com/android/server/display/LogicalDisplayMapper.java
@@ -96,7 +96,7 @@
private final SparseArray<LogicalDisplay> mLogicalDisplays =
new SparseArray<LogicalDisplay>();
private int mNextNonDefaultDisplayId = Display.DEFAULT_DISPLAY + 1;
- private int mNextNonDefaultGroupId = DisplayGroup.DEFAULT + 1;
+ private int mNextNonDefaultGroupId = Display.DEFAULT_DISPLAY_GROUP + 1;
/** A mapping from logical display id to display group. */
private final SparseArray<DisplayGroup> mDisplayGroups = new SparseArray<>();
@@ -313,7 +313,18 @@
final int displayId = assignDisplayIdLocked(isDefault);
final int layerStack = assignLayerStackLocked(displayId);
+ final DisplayGroup displayGroup;
+ final boolean addNewDisplayGroup =
+ isDefault || (deviceInfo.flags & DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP) != 0;
+ if (addNewDisplayGroup) {
+ final int groupId = assignDisplayGroupIdLocked(isDefault);
+ displayGroup = new DisplayGroup(groupId);
+ } else {
+ displayGroup = mDisplayGroups.get(Display.DEFAULT_DISPLAY);
+ }
+
LogicalDisplay display = new LogicalDisplay(displayId, layerStack, device);
+ display.updateDisplayGroupIdLocked(displayGroup.getGroupId());
display.updateLocked(mDisplayDeviceRepo);
if (!display.isValidLocked()) {
// This should never happen currently.
@@ -324,13 +335,6 @@
mLogicalDisplays.put(displayId, display);
- final DisplayGroup displayGroup;
- if (isDefault || (deviceInfo.flags & DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP) != 0) {
- final int groupId = assignDisplayGroupIdLocked(isDefault);
- displayGroup = new DisplayGroup(groupId);
- } else {
- displayGroup = mDisplayGroups.get(Display.DEFAULT_DISPLAY);
- }
displayGroup.addDisplay(display);
mDisplayGroups.append(displayId, displayGroup);
@@ -369,6 +373,7 @@
final DisplayGroup displayGroup = new DisplayGroup(groupId);
displayGroup.addDisplay(display);
mDisplayGroups.append(display.getDisplayIdLocked(), displayGroup);
+ display.updateDisplayGroupIdLocked(groupId);
}
} else {
// The display should be a part of the default DisplayGroup.
@@ -377,6 +382,7 @@
displayGroup.removeDisplay(display);
defaultDisplayGroup.addDisplay(display);
mDisplayGroups.put(displayId, defaultDisplayGroup);
+ display.updateDisplayGroupIdLocked(defaultDisplayGroup.getGroupId());
}
}
@@ -406,7 +412,7 @@
}
private int assignDisplayGroupIdLocked(boolean isDefault) {
- return isDefault ? DisplayGroup.DEFAULT : mNextNonDefaultGroupId++;
+ return isDefault ? Display.DEFAULT_DISPLAY_GROUP : mNextNonDefaultGroupId++;
}
private int assignLayerStackLocked(int displayId) {
diff --git a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
index bda4240..5b3db01 100644
--- a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
+++ b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
@@ -22,12 +22,15 @@
import android.graphics.Typeface;
import android.graphics.fonts.FontFamily;
import android.graphics.fonts.FontFileUtil;
+import android.graphics.fonts.FontManager;
import android.graphics.fonts.SystemFonts;
-import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
+import android.os.ResultReceiver;
import android.os.SharedMemory;
+import android.os.ShellCallback;
import android.system.ErrnoException;
import android.text.FontConfig;
+import android.util.AndroidException;
import android.util.IndentingPrintWriter;
import android.util.Slog;
@@ -48,6 +51,7 @@
import java.nio.NioUtils;
import java.nio.channels.FileChannel;
import java.util.Arrays;
+import java.util.Collections;
import java.util.Map;
/** A service for managing system fonts. */
@@ -55,8 +59,6 @@
public final class FontManagerService extends IFontManager.Stub {
private static final String TAG = "FontManagerService";
- // TODO: make this a DeviceConfig flag.
- private static final boolean ENABLE_FONT_UPDATES = false;
private static final String FONT_FILES_DIR = "/data/fonts/files";
@Override
@@ -64,6 +66,24 @@
return getCurrentFontSettings().getSystemFontConfig();
}
+ /* package */ static class SystemFontException extends AndroidException {
+ private final int mErrorCode;
+
+ SystemFontException(@FontManager.ErrorCode int errorCode, String msg, Throwable cause) {
+ super(msg, cause);
+ mErrorCode = errorCode;
+ }
+
+ SystemFontException(int errorCode, String msg) {
+ super(msg);
+ mErrorCode = errorCode;
+ }
+
+ @FontManager.ErrorCode int getErrorCode() {
+ return mErrorCode;
+ }
+ }
+
/** Class to manage FontManagerService's lifecycle. */
public static final class Lifecycle extends SystemService {
private final FontManagerService mService;
@@ -151,7 +171,6 @@
@Nullable
private static UpdatableFontDir createUpdatableFontDir() {
- if (!ENABLE_FONT_UPDATES) return null;
// If apk verity is supported, fs-verity should be available.
if (!FileIntegrityService.isApkVeritySupported()) return null;
return new UpdatableFontDir(new File(FONT_FILES_DIR),
@@ -178,19 +197,34 @@
}
}
- // TODO(b/173619554): Expose as API.
- private boolean installFontFile(FileDescriptor fd, byte[] pkcs7Signature) {
- if (mUpdatableFontDir == null) return false;
+ /* package */ void installFontFile(FileDescriptor fd, byte[] pkcs7Signature)
+ throws SystemFontException {
+ if (mUpdatableFontDir == null) {
+ throw new SystemFontException(
+ FontManager.ERROR_CODE_FONT_UPDATER_DISABLED,
+ "The font updater is disabled.");
+ }
synchronized (FontManagerService.this) {
- try {
- mUpdatableFontDir.installFontFile(fd, pkcs7Signature);
- } catch (IOException e) {
- Slog.w(TAG, "Failed to install font file");
- return false;
- }
+ mUpdatableFontDir.installFontFile(fd, pkcs7Signature);
// Create updated font map in the next getSerializedSystemFontMap() call.
mCurrentFontSettings = null;
- return true;
+ }
+ }
+
+ /* package */ void clearUpdates() throws SystemFontException {
+ if (mUpdatableFontDir == null) {
+ throw new SystemFontException(
+ FontManager.ERROR_CODE_FONT_UPDATER_DISABLED,
+ "The font updater is disabled.");
+ }
+ mUpdatableFontDir.clearUpdates();
+ }
+
+ /* package */ Map<String, File> getFontFileMap() {
+ if (mUpdatableFontDir == null) {
+ return Collections.emptyMap();
+ } else {
+ return mUpdatableFontDir.getFontFileMap();
}
}
@@ -202,11 +236,13 @@
}
@Override
- public int handleShellCommand(@NonNull ParcelFileDescriptor in,
- @NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err,
- @NonNull String[] args) {
- return new FontManagerShellCommand(this).exec(this,
- in.getFileDescriptor(), out.getFileDescriptor(), err.getFileDescriptor(), args);
+ public void onShellCommand(@Nullable FileDescriptor in,
+ @Nullable FileDescriptor out,
+ @Nullable FileDescriptor err,
+ @NonNull String[] args,
+ @Nullable ShellCallback callback,
+ @NonNull ResultReceiver result) throws RemoteException {
+ new FontManagerShellCommand(this).exec(this, in, out, err, args, callback, result);
}
/* package */ static class SystemFontSettings {
@@ -245,8 +281,7 @@
public static @Nullable SystemFontSettings create(
@Nullable UpdatableFontDir updatableFontDir) {
if (updatableFontDir != null) {
- final FontConfig fontConfig = SystemFonts.getSystemFontConfig(
- updatableFontDir.getFontFileMap());
+ final FontConfig fontConfig = updatableFontDir.getSystemFontConfig();
final Map<String, FontFamily[]> fallback =
SystemFonts.buildSystemFallback(fontConfig);
final Map<String, Typeface> typefaceMap =
@@ -274,4 +309,5 @@
return null;
}
}
+
}
diff --git a/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java b/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java
index acb5826..fd5c020 100644
--- a/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java
+++ b/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java
@@ -16,19 +16,33 @@
package com.android.server.graphics.fonts;
+import static com.android.server.graphics.fonts.FontManagerService.SystemFontException;
+
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
import android.graphics.fonts.Font;
import android.graphics.fonts.FontFamily;
+import android.graphics.fonts.FontManager;
import android.graphics.fonts.FontVariationAxis;
+import android.os.Binder;
+import android.os.ParcelFileDescriptor;
+import android.os.Process;
import android.os.ShellCommand;
import android.text.FontConfig;
import android.util.IndentingPrintWriter;
+import android.util.Slog;
import com.android.internal.util.DumpUtils;
import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
import java.io.PrintWriter;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.time.format.DateTimeFormatter;
+import java.util.Arrays;
import java.util.List;
import java.util.Map;
@@ -38,6 +52,13 @@
public class FontManagerShellCommand extends ShellCommand {
private static final String TAG = "FontManagerShellCommand";
+ /**
+ * The maximum size of signature file. This is just to avoid potential abuse.
+ *
+ * This is copied from VerityUtils.java.
+ */
+ private static final int MAX_SIGNATURE_FILE_SIZE_BYTES = 8192;
+
@NonNull private final FontManagerService mService;
FontManagerShellCommand(@NonNull FontManagerService service) {
@@ -46,12 +67,31 @@
@Override
public int onCommand(String cmd) {
+ final int callingUid = Binder.getCallingUid();
+ if (callingUid != Process.ROOT_UID && callingUid != Process.SHELL_UID) {
+ // Do not change this string since this string is expected in the CTS.
+ getErrPrintWriter().println("Only shell or root user can execute font command.");
+ return 1;
+ }
return execCommand(this, cmd);
}
@Override
public void onHelp() {
- dumpHelp(getOutPrintWriter());
+ PrintWriter w = getOutPrintWriter();
+ w.println("Font service (font) commands");
+ w.println("help");
+ w.println(" Print this help text.");
+ w.println();
+ w.println("dump [family name]");
+ w.println(" Dump all font files in the specified family name.");
+ w.println(" Dump current system font configuration if no family name was specified.");
+ w.println();
+ w.println("update [font file path] [signature file path]");
+ w.println(" Update installed font files with new font file.");
+ w.println();
+ w.println("clear");
+ w.println(" Remove all installed font files and reset to the initial state.");
}
/* package */ void dumpAll(@NonNull IndentingPrintWriter w) {
@@ -165,16 +205,6 @@
w.decreaseIndent();
}
- private static void dumpHelp(@NonNull PrintWriter w) {
- w.println("Font service (font) commands");
- w.println("help");
- w.println(" Print this help text.");
- w.println();
- w.println("dump [family name]");
- w.println(" Dump all font files in the specified family name.");
- w.println(" Dump current system font configuration if no family name was specified.");
- }
-
private void dumpFallback(@NonNull IndentingPrintWriter writer,
@NonNull FontFamily[] families) {
for (FontFamily family : families) {
@@ -233,37 +263,143 @@
writer.println(sb.toString());
}
- private int execCommand(@NonNull ShellCommand shell, @NonNull String cmd) {
+ private void writeCommandResult(ShellCommand shell, SystemFontException e) {
+ // Print short summary to the stderr.
+ PrintWriter pw = shell.getErrPrintWriter();
+ pw.println(e.getErrorCode());
+ pw.println(e.getMessage());
+
+ // Dump full stack trace to logcat.
+
+ Slog.e(TAG, "Command failed: " + Arrays.toString(shell.getAllArgs()), e);
+ }
+
+ private int dump(ShellCommand shell) {
final Context ctx = mService.getContext();
+ final FontManagerService.SystemFontSettings settings =
+ mService.getCurrentFontSettings();
+ if (!DumpUtils.checkDumpPermission(ctx, TAG, shell.getErrPrintWriter())) {
+ return 1;
+ }
+ final IndentingPrintWriter writer =
+ new IndentingPrintWriter(shell.getOutPrintWriter(), " ");
+ String nextArg = shell.getNextArg();
+ if (nextArg == null) {
+ dumpFontConfig(writer, settings.getSystemFontConfig());
+ } else {
+ final Map<String, FontFamily[]> fallbackMap =
+ settings.getSystemFallbackMap();
+ FontFamily[] families = fallbackMap.get(nextArg);
+ if (families == null) {
+ writer.println("Font Family \"" + nextArg + "\" not found");
+ } else {
+ dumpFallback(writer, families);
+ }
+ }
+ return 0;
+ }
+
+ private int update(ShellCommand shell) throws SystemFontException {
+ String fontPath = shell.getNextArg();
+ if (fontPath == null) {
+ throw new SystemFontException(
+ FontManager.ERROR_CODE_INVALID_SHELL_ARGUMENT,
+ "Font file path argument is required.");
+ }
+ String signaturePath = shell.getNextArg();
+ if (signaturePath == null) {
+ throw new SystemFontException(
+ FontManager.ERROR_CODE_INVALID_SHELL_ARGUMENT,
+ "Signature file argument is required.");
+ }
+
+ ParcelFileDescriptor fontFd = shell.openFileForSystem(fontPath, "r");
+ if (fontFd == null) {
+ throw new SystemFontException(
+ FontManager.ERROR_CODE_FAILED_TO_OPEN_FONT_FILE,
+ "Failed to open font file");
+ }
+
+ ParcelFileDescriptor sigFd = shell.openFileForSystem(signaturePath, "r");
+ if (sigFd == null) {
+ throw new SystemFontException(
+ FontManager.ERROR_CODE_FAILED_TO_OPEN_SIGNATURE_FILE,
+ "Failed to open signature file");
+ }
+
+ try (FileInputStream sigFis = new FileInputStream(sigFd.getFileDescriptor())) {
+ try (FileInputStream fontFis = new FileInputStream(fontFd.getFileDescriptor())) {
+ int len = sigFis.available();
+ if (len > MAX_SIGNATURE_FILE_SIZE_BYTES) {
+ throw new SystemFontException(
+ FontManager.ERROR_CODE_SIGNATURE_TOO_LARGE,
+ "Signature file is too large");
+ }
+ byte[] signature = new byte[len];
+ if (sigFis.read(signature, 0, len) != len) {
+ throw new SystemFontException(
+ FontManager.ERROR_CODE_INVALID_SIGNATURE_FILE,
+ "Invalid read length");
+ }
+ mService.installFontFile(fontFis.getFD(), signature);
+ } catch (IOException e) {
+ throw new SystemFontException(
+ FontManager.ERROR_CODE_INVALID_SIGNATURE_FILE,
+ "Failed to read signature file.", e);
+ }
+ } catch (IOException e) {
+ throw new SystemFontException(
+ FontManager.ERROR_CODE_INVALID_FONT_FILE,
+ "Failed to read font files.", e);
+ }
+
+ shell.getOutPrintWriter().println("Success"); // TODO: Output more details.
+ return 0;
+ }
+
+ private int clear(ShellCommand shell) throws SystemFontException {
+ mService.clearUpdates();
+ shell.getOutPrintWriter().println("Success");
+ return 0;
+ }
+
+ private int status(ShellCommand shell) throws SystemFontException {
+ final FontManagerService.SystemFontSettings settings = mService.getCurrentFontSettings();
+ final IndentingPrintWriter writer =
+ new IndentingPrintWriter(shell.getOutPrintWriter(), " ");
+ FontConfig config = settings.getSystemFontConfig();
+
+ writer.println("Current Version: " + config.getConfigVersion());
+ LocalDateTime dt = LocalDateTime.ofEpochSecond(config.getLastModifiedDate(), 0,
+ ZoneOffset.UTC);
+ writer.println("Last Modified Date: " + dt.format(DateTimeFormatter.ISO_DATE_TIME));
+
+ Map<String, File> fontFileMap = mService.getFontFileMap();
+ writer.println("Number of updated font files: " + fontFileMap.size());
+ return 0;
+ }
+
+ private int execCommand(@NonNull ShellCommand shell, @Nullable String cmd) {
if (cmd == null) {
return shell.handleDefaultCommands(null);
}
- final FontManagerService.SystemFontSettings settings = mService.getCurrentFontSettings();
-
- switch (cmd) {
- case "dump":
- if (!DumpUtils.checkDumpPermission(ctx, TAG, shell.getErrPrintWriter())) {
- return 1;
- }
- final IndentingPrintWriter writer =
- new IndentingPrintWriter(shell.getOutPrintWriter(), " ");
- String nextArg = shell.getNextArg();
- if (nextArg == null) {
- dumpFontConfig(writer, settings.getSystemFontConfig());
- } else {
- final Map<String, FontFamily[]> fallbackMap = settings.getSystemFallbackMap();
- FontFamily[] families = fallbackMap.get(nextArg);
- if (families == null) {
- writer.println("Font Family \"" + nextArg + "\" not found");
- } else {
- dumpFallback(writer, families);
- }
- }
- return 0;
- default:
- shell.handleDefaultCommands(cmd);
+ try {
+ switch (cmd) {
+ case "dump":
+ return dump(shell);
+ case "update":
+ return update(shell);
+ case "clear":
+ return clear(shell);
+ case "status":
+ return status(shell);
+ default:
+ return shell.handleDefaultCommands(cmd);
+ }
+ } catch (SystemFontException e) {
+ writeCommandResult(shell, e);
+ return 1;
}
- return 0;
}
}
diff --git a/services/core/java/com/android/server/graphics/fonts/PersistentSystemFontConfig.java b/services/core/java/com/android/server/graphics/fonts/PersistentSystemFontConfig.java
new file mode 100644
index 0000000..f0d14ba
--- /dev/null
+++ b/services/core/java/com/android/server/graphics/fonts/PersistentSystemFontConfig.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.graphics.fonts;
+
+import android.annotation.NonNull;
+import android.text.TextUtils;
+import android.util.Slog;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
+import android.util.Xml;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/* package */ class PersistentSystemFontConfig {
+ private static final String TAG = "PersistentSystemFontConfig";
+
+ private static final String TAG_ROOT = "fontConfig";
+ private static final String TAG_LAST_MODIFIED_DATE = "lastModifiedDate";
+ private static final String TAG_VALUE = "value";
+
+ /* package */ static class Config {
+ public long lastModifiedDate;
+
+ public void reset() {
+ lastModifiedDate = 0;
+ }
+
+ public void copyTo(@NonNull Config out) {
+ out.lastModifiedDate = lastModifiedDate;
+ }
+ }
+
+ /**
+ * Read config XML and write to out argument.
+ */
+ public static void loadFromXml(@NonNull InputStream is, @NonNull Config out)
+ throws XmlPullParserException, IOException {
+ out.reset();
+ TypedXmlPullParser parser = Xml.resolvePullParser(is);
+
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
+ if (type != XmlPullParser.START_TAG) {
+ continue;
+ }
+ final int depth = parser.getDepth();
+ final String tag = parser.getName();
+ if (depth == 1) {
+ if (!TAG_ROOT.equals(tag)) {
+ Slog.e(TAG, "Invalid root tag: " + tag);
+ return;
+ }
+ } else if (depth == 2) {
+ switch (tag) {
+ case TAG_LAST_MODIFIED_DATE:
+ out.lastModifiedDate = parseLongAttribute(parser, TAG_VALUE, 0);
+ break;
+ default:
+ Slog.w(TAG, "Skipping unknown tag: " + tag);
+ }
+ }
+ }
+
+ }
+
+ /**
+ * Write config to OutputStream as XML file.
+ */
+ public static void writeToXml(@NonNull OutputStream os, @NonNull Config config)
+ throws IOException {
+ TypedXmlSerializer out = Xml.resolveSerializer(os);
+ out.startDocument(null /* encoding */, true /* standalone */);
+
+ out.startTag(null, TAG_ROOT);
+ out.startTag(null, TAG_LAST_MODIFIED_DATE);
+ out.attribute(null, TAG_VALUE, Long.toString(config.lastModifiedDate));
+ out.endTag(null, TAG_LAST_MODIFIED_DATE);
+ out.endTag(null, TAG_ROOT);
+
+ out.endDocument();
+ }
+
+ private static long parseLongAttribute(TypedXmlPullParser parser, String attr, long defValue) {
+ final String value = parser.getAttributeValue(null /* namespace */, attr);
+ if (TextUtils.isEmpty(value)) {
+ return defValue;
+ }
+ try {
+ return Long.parseLong(value);
+ } catch (NumberFormatException e) {
+ return defValue;
+ }
+ }
+
+}
diff --git a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
index 8da579f..720105d 100644
--- a/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
+++ b/services/core/java/com/android/server/graphics/fonts/UpdatableFontDir.java
@@ -16,18 +16,30 @@
package com.android.server.graphics.fonts;
+import static com.android.server.graphics.fonts.FontManagerService.SystemFontException;
+
+import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.graphics.fonts.FontManager;
+import android.graphics.fonts.SystemFonts;
import android.os.FileUtils;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.text.FontConfig;
import android.util.Base64;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
+import org.xmlpull.v1.XmlPullParserException;
+
import java.io.File;
import java.io.FileDescriptor;
+import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.SecureRandom;
+import java.time.Instant;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -39,6 +51,8 @@
// TODO: Support .otf
private static final String ALLOWED_EXTENSION = ".ttf";
+ private static final String CONFIG_XML_FILE = "/data/fonts/config/config.xml";
+
/** Interface to mock font file access in tests. */
interface FontFileParser {
String getPostScriptName(File file) throws IOException;
@@ -55,6 +69,15 @@
boolean rename(File src, File dest);
}
+ /** Interface to mock persistent configuration */
+ interface PersistentConfig {
+ void loadFromXml(PersistentSystemFontConfig.Config out)
+ throws XmlPullParserException, IOException;
+ void writeToXml(PersistentSystemFontConfig.Config config)
+ throws IOException;
+ boolean rename(File src, File dest);
+ }
+
/** Data class to hold font file path and revision. */
private static final class FontFileInfo {
private final File mFile;
@@ -87,6 +110,16 @@
private final List<File> mPreinstalledFontDirs;
private final FontFileParser mParser;
private final FsverityUtil mFsverityUtil;
+ private final File mConfigFile;
+ private final File mTmpConfigFile;
+
+ @GuardedBy("UpdatableFontDir.this")
+ private final PersistentSystemFontConfig.Config mConfig =
+ new PersistentSystemFontConfig.Config();
+
+ @GuardedBy("UpdatableFontDir.this")
+ private int mConfigVersion = 1;
+
/**
* A mutable map containing mapping from font file name (e.g. "NotoColorEmoji.ttf") to {@link
* FontFileInfo}. All files in this map are validated, and have higher revision numbers than
@@ -101,6 +134,20 @@
mPreinstalledFontDirs = preinstalledFontDirs;
mParser = parser;
mFsverityUtil = fsverityUtil;
+ mConfigFile = new File(CONFIG_XML_FILE);
+ mTmpConfigFile = new File(CONFIG_XML_FILE + ".tmp");
+ loadFontFileMap();
+ }
+
+ // For unit testing
+ UpdatableFontDir(File filesDir, List<File> preinstalledFontDirs, FontFileParser parser,
+ FsverityUtil fsverityUtil, File configFile) {
+ mFilesDir = filesDir;
+ mPreinstalledFontDirs = preinstalledFontDirs;
+ mParser = parser;
+ mFsverityUtil = fsverityUtil;
+ mConfigFile = configFile;
+ mTmpConfigFile = new File(configFile.getAbsoluteFile() + ".tmp");
loadFontFileMap();
}
@@ -108,6 +155,13 @@
// TODO: SIGBUS crash protection
synchronized (UpdatableFontDir.this) {
boolean success = false;
+
+ try (FileInputStream fis = new FileInputStream(mConfigFile)) {
+ PersistentSystemFontConfig.loadFromXml(fis, mConfig);
+ } catch (IOException | XmlPullParserException e) {
+ mConfig.reset();
+ }
+
mFontFileInfoMap.clear();
try {
File[] dirs = mFilesDir.listFiles();
@@ -117,13 +171,13 @@
File[] files = dir.listFiles();
if (files == null || files.length != 1) return;
FontFileInfo fontFileInfo = validateFontFile(files[0]);
- if (fontFileInfo == null) {
- Slog.w(TAG, "Broken file is found. Clearing files.");
- return;
- }
- addFileToMapLocked(fontFileInfo, true /* deleteOldFile */);
+ addFileToMapIfNewerLocked(fontFileInfo, true /* deleteOldFile */);
}
success = true;
+ } catch (Throwable t) {
+ // If something happened during loading system fonts, clear all contents in finally
+ // block. Here, just dumping errors.
+ Slog.e(TAG, "Failed to load font mappings.", t);
} finally {
// Delete all files just in case if we find a problematic file.
if (!success) {
@@ -134,6 +188,24 @@
}
}
+ /* package */ void clearUpdates() throws SystemFontException {
+ synchronized (UpdatableFontDir.this) {
+ mFontFileInfoMap.clear();
+ FileUtils.deleteContents(mFilesDir);
+
+ mConfig.reset();
+ mConfig.lastModifiedDate = Instant.now().getEpochSecond();
+ try (FileOutputStream fos = new FileOutputStream(mConfigFile)) {
+ PersistentSystemFontConfig.writeToXml(fos, mConfig);
+ } catch (Exception e) {
+ throw new SystemFontException(
+ FontManager.ERROR_CODE_FAILED_TO_CREATE_CONFIG_FILE,
+ "Failed to write config XML.", e);
+ }
+ mConfigVersion++;
+ }
+ }
+
/**
* Installs a new font file, or updates an existing font file.
*
@@ -143,38 +215,108 @@
*
* @param fd A file descriptor to the font file.
* @param pkcs7Signature A PKCS#7 detached signature to enable fs-verity for the font file.
+ * @throws SystemFontException if error occurs.
*/
- void installFontFile(FileDescriptor fd, byte[] pkcs7Signature) throws IOException {
+ void installFontFile(FileDescriptor fd, byte[] pkcs7Signature) throws SystemFontException {
synchronized (UpdatableFontDir.this) {
File newDir = getRandomDir(mFilesDir);
if (!newDir.mkdir()) {
- // TODO: Define and return an error code for API
- throw new IOException("Failed to create a new dir");
+ throw new SystemFontException(
+ FontManager.ERROR_CODE_FAILED_TO_WRITE_FONT_FILE,
+ "Failed to create font directory.");
+ }
+ try {
+ // Make newDir executable so that apps can access font file inside newDir.
+ Os.chmod(newDir.getAbsolutePath(), 0711);
+ } catch (ErrnoException e) {
+ throw new SystemFontException(
+ FontManager.ERROR_CODE_FAILED_TO_WRITE_FONT_FILE,
+ "Failed to change mode to 711", e);
}
boolean success = false;
try {
File tempNewFontFile = new File(newDir, "font.ttf");
try (FileOutputStream out = new FileOutputStream(tempNewFontFile)) {
FileUtils.copy(fd, out.getFD());
+ } catch (IOException e) {
+ throw new SystemFontException(
+ FontManager.ERROR_CODE_FAILED_TO_WRITE_FONT_FILE,
+ "Failed to write font file to storage.", e);
}
- // Do not parse font file before setting up fs-verity.
- // setUpFsverity throws IOException if failed.
- mFsverityUtil.setUpFsverity(tempNewFontFile.getAbsolutePath(), pkcs7Signature);
- String postScriptName = mParser.getPostScriptName(tempNewFontFile);
+ try {
+ // Do not parse font file before setting up fs-verity.
+ // setUpFsverity throws IOException if failed.
+ mFsverityUtil.setUpFsverity(tempNewFontFile.getAbsolutePath(),
+ pkcs7Signature);
+ } catch (IOException e) {
+ throw new SystemFontException(
+ FontManager.ERROR_CODE_VERIFICATION_FAILURE,
+ "Failed to setup fs-verity.", e);
+ }
+ String postScriptName;
+ try {
+ postScriptName = mParser.getPostScriptName(tempNewFontFile);
+ } catch (IOException e) {
+ throw new SystemFontException(
+ FontManager.ERROR_CODE_INVALID_FONT_FILE,
+ "Failed to read PostScript name from font file", e);
+ }
+ if (postScriptName == null) {
+ throw new SystemFontException(
+ FontManager.ERROR_CODE_MISSING_POST_SCRIPT_NAME,
+ "Failed to read PostScript name from font file");
+ }
File newFontFile = new File(newDir, postScriptName + ALLOWED_EXTENSION);
if (!mFsverityUtil.rename(tempNewFontFile, newFontFile)) {
- // TODO: Define and return an error code for API
- throw new IOException("Failed to rename");
+ throw new SystemFontException(
+ FontManager.ERROR_CODE_FAILED_TO_WRITE_FONT_FILE,
+ "Failed to move verified font file.");
+ }
+ try {
+ // Make the font file readable by apps.
+ Os.chmod(newFontFile.getAbsolutePath(), 0644);
+ } catch (ErrnoException e) {
+ throw new SystemFontException(
+ FontManager.ERROR_CODE_FAILED_TO_WRITE_FONT_FILE,
+ "Failed to change mode to 711", e);
}
FontFileInfo fontFileInfo = validateFontFile(newFontFile);
- if (fontFileInfo == null) {
- // TODO: Define and return an error code for API
- throw new IllegalArgumentException("Invalid file");
+
+ // Write config file.
+ PersistentSystemFontConfig.Config copied = new PersistentSystemFontConfig.Config();
+ mConfig.copyTo(copied);
+
+ copied.lastModifiedDate = Instant.now().getEpochSecond();
+ try (FileOutputStream fos = new FileOutputStream(mTmpConfigFile)) {
+ PersistentSystemFontConfig.writeToXml(fos, copied);
+ } catch (Exception e) {
+ throw new SystemFontException(
+ FontManager.ERROR_CODE_FAILED_TO_CREATE_CONFIG_FILE,
+ "Failed to write config XML.", e);
}
- if (!addFileToMapLocked(fontFileInfo, false)) {
- // TODO: Define and return an error code for API
- throw new IllegalArgumentException("Version downgrade");
+
+ // Backup the mapping for rollback.
+ HashMap<String, FontFileInfo> backup = new HashMap<>(mFontFileInfoMap);
+ if (!addFileToMapIfNewerLocked(fontFileInfo, false)) {
+ throw new SystemFontException(
+ FontManager.ERROR_CODE_DOWNGRADING,
+ "Downgrading font file is forbidden.");
}
+
+ if (!mFsverityUtil.rename(mTmpConfigFile, mConfigFile)) {
+ // If we fail to stage the config file, need to rollback the config.
+ mFontFileInfoMap.clear();
+ mFontFileInfoMap.putAll(backup);
+ throw new SystemFontException(
+ FontManager.ERROR_CODE_FAILED_TO_CREATE_CONFIG_FILE,
+ "Failed to stage the config file.");
+ }
+
+
+ // Now font update is succeeded. Update config version.
+ copied.copyTo(mConfig);
+ mConfigVersion++;
+
success = true;
} finally {
if (!success) {
@@ -207,7 +349,7 @@
* higher than the currently used font file (either in {@link #mFontFileInfoMap} or {@link
* #mPreinstalledFontDirs}).
*/
- private boolean addFileToMapLocked(FontFileInfo fontFileInfo, boolean deleteOldFile) {
+ private boolean addFileToMapIfNewerLocked(FontFileInfo fontFileInfo, boolean deleteOldFile) {
String name = fontFileInfo.getFile().getName();
FontFileInfo existingInfo = mFontFileInfoMap.get(name);
final boolean shouldAddToMap;
@@ -224,13 +366,12 @@
FileUtils.deleteContentsAndDir(existingInfo.getRandomizedFontDir());
}
mFontFileInfoMap.put(name, fontFileInfo);
- return true;
} else {
if (deleteOldFile) {
FileUtils.deleteContentsAndDir(fontFileInfo.getRandomizedFontDir());
}
- return false;
}
+ return shouldAddToMap;
}
private long getPreinstalledFontRevision(String name) {
@@ -255,20 +396,23 @@
* returns a {@link FontFileInfo} on success. This method does not check if the font revision
* is higher than the currently used font.
*/
- @Nullable
- private FontFileInfo validateFontFile(File file) {
+ @NonNull
+ private FontFileInfo validateFontFile(File file) throws SystemFontException {
if (!mFsverityUtil.hasFsverity(file.getAbsolutePath())) {
- Slog.w(TAG, "Font validation failed. Fs-verity is not enabled: " + file);
- return null;
+ throw new SystemFontException(
+ FontManager.ERROR_CODE_VERIFICATION_FAILURE,
+ "Font validation failed. Fs-verity is not enabled: " + file);
}
if (!validateFontFileName(file)) {
- Slog.w(TAG, "Font validation failed. Could not validate font file name: " + file);
- return null;
+ throw new SystemFontException(
+ FontManager.ERROR_CODE_FONT_NAME_MISMATCH,
+ "Font validation failed. Could not validate font file name: " + file);
}
long revision = getFontRevision(file);
if (revision == -1) {
- Slog.w(TAG, "Font validation failed. Could not read font revision: " + file);
- return null;
+ throw new SystemFontException(
+ FontManager.ERROR_CODE_INVALID_FONT_FILE,
+ "Font validation failed. Could not read font revision: " + file);
}
return new FontFileInfo(file, revision);
}
@@ -318,4 +462,14 @@
}
return map;
}
+
+ /* package */ FontConfig getSystemFontConfig() {
+ synchronized (UpdatableFontDir.this) {
+ return SystemFonts.getSystemFontConfig(
+ getFontFileMap(),
+ mConfig.lastModifiedDate,
+ mConfigVersion
+ );
+ }
+ }
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 143ec15..6308ace 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -3947,58 +3947,61 @@
}
@Override
- public void setAdditionalInputMethodSubtypes(String imiId, InputMethodSubtype[] subtypes) {
- // By this IPC call, only a process which shares the same uid with the IME can add
- // additional input method subtypes to the IME.
- if (TextUtils.isEmpty(imiId) || subtypes == null) return;
- final ArrayList<InputMethodSubtype> toBeAdded = new ArrayList<>();
- for (InputMethodSubtype subtype : subtypes) {
- if (!toBeAdded.contains(subtype)) {
- toBeAdded.add(subtype);
- } else {
- Slog.w(TAG, "Duplicated subtype definition found: "
- + subtype.getLocale() + ", " + subtype.getMode());
+ public void setAdditionalInputMethodSubtypes(String imiId, InputMethodSubtype[] subtypes,
+ IVoidResultCallback resultCallback) {
+ CallbackUtils.onResult(resultCallback, () -> {
+ // By this IPC call, only a process which shares the same uid with the IME can add
+ // additional input method subtypes to the IME.
+ if (TextUtils.isEmpty(imiId) || subtypes == null) return;
+ final ArrayList<InputMethodSubtype> toBeAdded = new ArrayList<>();
+ for (InputMethodSubtype subtype : subtypes) {
+ if (!toBeAdded.contains(subtype)) {
+ toBeAdded.add(subtype);
+ } else {
+ Slog.w(TAG, "Duplicated subtype definition found: "
+ + subtype.getLocale() + ", " + subtype.getMode());
+ }
}
- }
- synchronized (mMethodMap) {
- if (!calledFromValidUserLocked()) {
- return;
- }
- if (!mSystemReady) {
- return;
- }
- final InputMethodInfo imi = mMethodMap.get(imiId);
- if (imi == null) return;
- final String[] packageInfos;
- try {
- packageInfos = mIPackageManager.getPackagesForUid(Binder.getCallingUid());
- } catch (RemoteException e) {
- Slog.e(TAG, "Failed to get package infos");
- return;
- }
- if (packageInfos != null) {
- final int packageNum = packageInfos.length;
- for (int i = 0; i < packageNum; ++i) {
- if (packageInfos[i].equals(imi.getPackageName())) {
- if (subtypes.length > 0) {
- mAdditionalSubtypeMap.put(imi.getId(), toBeAdded);
- } else {
- mAdditionalSubtypeMap.remove(imi.getId());
+ synchronized (mMethodMap) {
+ if (!calledFromValidUserLocked()) {
+ return;
+ }
+ if (!mSystemReady) {
+ return;
+ }
+ final InputMethodInfo imi = mMethodMap.get(imiId);
+ if (imi == null) return;
+ final String[] packageInfos;
+ try {
+ packageInfos = mIPackageManager.getPackagesForUid(Binder.getCallingUid());
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Failed to get package infos");
+ return;
+ }
+ if (packageInfos != null) {
+ final int packageNum = packageInfos.length;
+ for (int i = 0; i < packageNum; ++i) {
+ if (packageInfos[i].equals(imi.getPackageName())) {
+ if (subtypes.length > 0) {
+ mAdditionalSubtypeMap.put(imi.getId(), toBeAdded);
+ } else {
+ mAdditionalSubtypeMap.remove(imi.getId());
+ }
+ AdditionalSubtypeUtils.save(mAdditionalSubtypeMap, mMethodMap,
+ mSettings.getCurrentUserId());
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ buildInputMethodListLocked(false /* resetDefaultEnabledIme */);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ return;
}
- AdditionalSubtypeUtils.save(mAdditionalSubtypeMap, mMethodMap,
- mSettings.getCurrentUserId());
- final long ident = Binder.clearCallingIdentity();
- try {
- buildInputMethodListLocked(false /* resetDefaultEnabledIme */);
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- return;
}
}
}
- }
- return;
+ return;
+ });
}
/**
@@ -4103,16 +4106,21 @@
}
@Override
- public void removeImeSurface() {
- mContext.enforceCallingPermission(Manifest.permission.INTERNAL_SYSTEM_WINDOW, null);
- mHandler.sendMessage(mHandler.obtainMessage(MSG_REMOVE_IME_SURFACE));
+ public void removeImeSurface(IVoidResultCallback resultCallback) {
+ CallbackUtils.onResult(resultCallback, () -> {
+ mContext.enforceCallingPermission(Manifest.permission.INTERNAL_SYSTEM_WINDOW, null);
+ mHandler.sendMessage(mHandler.obtainMessage(MSG_REMOVE_IME_SURFACE));
+ });
}
@Override
- public void removeImeSurfaceFromWindow(IBinder windowToken) {
- // No permission check, because we'll only execute the request if the calling window is
- // also the current IME client.
- mHandler.obtainMessage(MSG_REMOVE_IME_SURFACE_FROM_WINDOW, windowToken).sendToTarget();
+ public void removeImeSurfaceFromWindow(IBinder windowToken,
+ IVoidResultCallback resultCallback) {
+ CallbackUtils.onResult(resultCallback, () -> {
+ // No permission check, because we'll only execute the request if the calling window is
+ // also the current IME client.
+ mHandler.obtainMessage(MSG_REMOVE_IME_SURFACE_FROM_WINDOW, windowToken).sendToTarget();
+ });
}
/**
diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
index 2dd7096..7f9c766 100644
--- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java
@@ -1502,14 +1502,17 @@
@BinderThread
@Override
- public void removeImeSurface() {
+ public void removeImeSurface(IVoidResultCallback resultCallback) {
reportNotSupported();
+ CallbackUtils.onResult(resultCallback, () -> { });
}
@BinderThread
@Override
- public void removeImeSurfaceFromWindow(IBinder windowToken) {
+ public void removeImeSurfaceFromWindow(IBinder windowToken,
+ IVoidResultCallback resultCallback) {
reportNotSupported();
+ CallbackUtils.onResult(resultCallback, () -> { });
}
@BinderThread
@@ -1815,8 +1818,10 @@
@BinderThread
@Override
- public void setAdditionalInputMethodSubtypes(String imiId, InputMethodSubtype[] subtypes) {
+ public void setAdditionalInputMethodSubtypes(String imiId, InputMethodSubtype[] subtypes,
+ IVoidResultCallback resultCallback) {
reportNotSupported();
+ CallbackUtils.onResult(resultCallback, () -> { });
}
@BinderThread
diff --git a/services/core/java/com/android/server/location/timezone/OWNERS b/services/core/java/com/android/server/location/timezone/OWNERS
deleted file mode 100644
index 28aff18..0000000
--- a/services/core/java/com/android/server/location/timezone/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-# Bug component: 847766
-nfuller@google.com
-include /core/java/android/app/timedetector/OWNERS
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
index 7dd961a..b92a83f 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
@@ -89,6 +89,7 @@
private static final String CHILD_PROFILE_LOCK_FILE = "gatekeeper.profile.key";
private static final String REBOOT_ESCROW_FILE = "reboot.escrow.key";
+ private static final String REBOOT_ESCROW_SERVER_BLOB = "reboot.escrow.server.blob.key";
private static final String SYNTHETIC_PASSWORD_DIRECTORY = "spblob/";
@@ -318,6 +319,22 @@
deleteFile(getRebootEscrowFile(userId));
}
+ public void writeRebootEscrowServerBlob(byte[] serverBlob) {
+ writeFile(getRebootEscrowServerBlob(), serverBlob);
+ }
+
+ public byte[] readRebootEscrowServerBlob() {
+ return readFile(getRebootEscrowServerBlob());
+ }
+
+ public boolean hasRebootEscrowServerBlob() {
+ return hasFile(getRebootEscrowServerBlob());
+ }
+
+ public void removeRebootEscrowServerBlob() {
+ deleteFile(getRebootEscrowServerBlob());
+ }
+
public boolean hasPassword(int userId) {
return hasFile(getLockPasswordFilename(userId));
}
@@ -446,6 +463,12 @@
return getLockCredentialFilePathForUser(userId, REBOOT_ESCROW_FILE);
}
+ @VisibleForTesting
+ String getRebootEscrowServerBlob() {
+ // There is a single copy of server blob for all users.
+ return getLockCredentialFilePathForUser(UserHandle.USER_SYSTEM, REBOOT_ESCROW_SERVER_BLOB);
+ }
+
private String getLockCredentialFilePathForUser(int userId, String basename) {
String dataSystemDirectory = Environment.getDataDirectory().getAbsolutePath() +
SYSTEM_DIRECTORY;
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
index fbec915..06962d4 100644
--- a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java
@@ -124,26 +124,28 @@
static class Injector {
protected Context mContext;
private final RebootEscrowKeyStoreManager mKeyStoreManager;
- private final RebootEscrowProviderInterface mRebootEscrowProvider;
+ private final LockSettingsStorage mStorage;
+ private RebootEscrowProviderInterface mRebootEscrowProvider;
- Injector(Context context) {
+ Injector(Context context, LockSettingsStorage storage) {
mContext = context;
+ mStorage = storage;
mKeyStoreManager = new RebootEscrowKeyStoreManager();
+ }
- RebootEscrowProviderInterface rebootEscrowProvider = null;
- // TODO(xunchang) add implementation for server based ror.
+ private RebootEscrowProviderInterface createRebootEscrowProvider() {
+ RebootEscrowProviderInterface rebootEscrowProvider;
if (DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_OTA,
"server_based_ror_enabled", false)) {
- Slog.e(TAG, "Server based ror isn't implemented yet.");
+ rebootEscrowProvider = new RebootEscrowProviderServerBasedImpl(mContext, mStorage);
} else {
rebootEscrowProvider = new RebootEscrowProviderHalImpl();
}
- if (rebootEscrowProvider != null && rebootEscrowProvider.hasRebootEscrowSupport()) {
- mRebootEscrowProvider = rebootEscrowProvider;
- } else {
- mRebootEscrowProvider = null;
+ if (rebootEscrowProvider.hasRebootEscrowSupport()) {
+ return rebootEscrowProvider;
}
+ return null;
}
public Context getContext() {
@@ -159,6 +161,12 @@
}
public RebootEscrowProviderInterface getRebootEscrowProvider() {
+ // Initialize for the provider lazily. Because the device_config and service
+ // implementation apps may change when system server is running.
+ if (mRebootEscrowProvider == null) {
+ mRebootEscrowProvider = createRebootEscrowProvider();
+ }
+
return mRebootEscrowProvider;
}
@@ -177,7 +185,7 @@
}
RebootEscrowManager(Context context, Callbacks callbacks, LockSettingsStorage storage) {
- this(new Injector(context), callbacks, storage);
+ this(new Injector(context, storage), callbacks, storage);
}
@VisibleForTesting
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java b/services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java
new file mode 100644
index 0000000..ba1a680
--- /dev/null
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.locksettings;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.os.RemoteException;
+import android.provider.DeviceConfig;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.locksettings.ResumeOnRebootServiceProvider.ResumeOnRebootServiceConnection;
+
+import java.io.IOException;
+import java.util.concurrent.TimeoutException;
+
+import javax.crypto.SecretKey;
+
+/**
+ * An implementation of the {@link RebootEscrowProviderInterface} by communicating with server to
+ * encrypt & decrypt the blob.
+ */
+class RebootEscrowProviderServerBasedImpl implements RebootEscrowProviderInterface {
+ private static final String TAG = "RebootEscrowProvider";
+
+ // Timeout for service binding
+ private static final long DEFAULT_SERVICE_TIMEOUT_IN_SECONDS = 10;
+
+ /**
+ * Use the default lifetime of 10 minutes. The lifetime covers the following activities:
+ * Server wrap secret -> device reboot -> server unwrap blob.
+ */
+ private static final long DEFAULT_SERVER_BLOB_LIFETIME_IN_MILLIS = 600_1000;
+
+ private final LockSettingsStorage mStorage;
+
+ private final Injector mInjector;
+
+ static class Injector {
+ private ResumeOnRebootServiceConnection mServiceConnection = null;
+
+ Injector(Context context) {
+ mServiceConnection = new ResumeOnRebootServiceProvider(context).getServiceConnection();
+ if (mServiceConnection == null) {
+ Slog.e(TAG, "Failed to resolve resume on reboot server service.");
+ }
+ }
+
+ Injector(ResumeOnRebootServiceConnection serviceConnection) {
+ mServiceConnection = serviceConnection;
+ }
+
+ @Nullable
+ private ResumeOnRebootServiceConnection getServiceConnection() {
+ return mServiceConnection;
+ }
+
+ long getServiceTimeoutInSeconds() {
+ return DeviceConfig.getLong(DeviceConfig.NAMESPACE_OTA,
+ "server_based_service_timeout_in_seconds",
+ DEFAULT_SERVICE_TIMEOUT_IN_SECONDS);
+ }
+
+ long getServerBlobLifetimeInMillis() {
+ return DeviceConfig.getLong(DeviceConfig.NAMESPACE_OTA,
+ "server_based_server_blob_lifetime_in_millis",
+ DEFAULT_SERVER_BLOB_LIFETIME_IN_MILLIS);
+ }
+ }
+
+ RebootEscrowProviderServerBasedImpl(Context context, LockSettingsStorage storage) {
+ this(storage, new Injector(context));
+ }
+
+ @VisibleForTesting
+ RebootEscrowProviderServerBasedImpl(LockSettingsStorage storage, Injector injector) {
+ mStorage = storage;
+ mInjector = injector;
+ }
+
+ @Override
+ public boolean hasRebootEscrowSupport() {
+ return mInjector.getServiceConnection() != null;
+ }
+
+ private byte[] unwrapServerBlob(byte[] serverBlob, SecretKey decryptionKey) throws
+ TimeoutException, RemoteException, IOException {
+ ResumeOnRebootServiceConnection serviceConnection = mInjector.getServiceConnection();
+ if (serviceConnection == null) {
+ Slog.w(TAG, "Had reboot escrow data for users, but resume on reboot server"
+ + " service is unavailable");
+ return null;
+ }
+
+ // Decrypt with k_k from the key store first.
+ byte[] decryptedBlob = AesEncryptionUtil.decrypt(decryptionKey, serverBlob);
+ if (decryptedBlob == null) {
+ Slog.w(TAG, "Decrypted server blob should not be null");
+ return null;
+ }
+
+ // Ask the server connection service to decrypt the inner layer, to get the reboot
+ // escrow key (k_s).
+ serviceConnection.bindToService(mInjector.getServiceTimeoutInSeconds());
+ byte[] escrowKeyBytes = serviceConnection.unwrap(decryptedBlob,
+ mInjector.getServiceTimeoutInSeconds());
+ serviceConnection.unbindService();
+
+ return escrowKeyBytes;
+ }
+
+ @Override
+ public RebootEscrowKey getAndClearRebootEscrowKey(SecretKey decryptionKey) {
+ byte[] serverBlob = mStorage.readRebootEscrowServerBlob();
+ // Delete the server blob in storage.
+ mStorage.removeRebootEscrowServerBlob();
+ if (serverBlob == null) {
+ Slog.w(TAG, "Failed to read reboot escrow server blob from storage");
+ return null;
+ }
+
+ try {
+ byte[] escrowKeyBytes = unwrapServerBlob(serverBlob, decryptionKey);
+ if (escrowKeyBytes == null) {
+ Slog.w(TAG, "Decrypted reboot escrow key bytes should not be null");
+ return null;
+ } else if (escrowKeyBytes.length != 32) {
+ Slog.e(TAG, "Decrypted reboot escrow key has incorrect size "
+ + escrowKeyBytes.length);
+ return null;
+ }
+
+ return RebootEscrowKey.fromKeyBytes(escrowKeyBytes);
+ } catch (TimeoutException | RemoteException | IOException e) {
+ Slog.w(TAG, "Failed to decrypt the server blob ", e);
+ return null;
+ }
+ }
+
+ @Override
+ public void clearRebootEscrowKey() {
+ mStorage.removeRebootEscrowServerBlob();
+ }
+
+ private byte[] wrapEscrowKey(byte[] escrowKeyBytes, SecretKey encryptionKey) throws
+ TimeoutException, RemoteException, IOException {
+ ResumeOnRebootServiceConnection serviceConnection = mInjector.getServiceConnection();
+ if (serviceConnection == null) {
+ Slog.w(TAG, "Failed to encrypt the reboot escrow key: resume on reboot server"
+ + " service is unavailable");
+ return null;
+ }
+
+ serviceConnection.bindToService(mInjector.getServiceTimeoutInSeconds());
+ // Ask the server connection service to encrypt the reboot escrow key.
+ byte[] serverEncryptedBlob = serviceConnection.wrapBlob(escrowKeyBytes,
+ mInjector.getServerBlobLifetimeInMillis(), mInjector.getServiceTimeoutInSeconds());
+ serviceConnection.unbindService();
+
+ if (serverEncryptedBlob == null) {
+ Slog.w(TAG, "Server encrypted reboot escrow key cannot be null");
+ return null;
+ }
+
+ // Additionally wrap the server blob with a local key.
+ return AesEncryptionUtil.encrypt(encryptionKey, serverEncryptedBlob);
+ }
+
+ @Override
+ public boolean storeRebootEscrowKey(RebootEscrowKey escrowKey, SecretKey encryptionKey) {
+ mStorage.removeRebootEscrowServerBlob();
+ try {
+ byte[] wrappedBlob = wrapEscrowKey(escrowKey.getKeyBytes(), encryptionKey);
+ if (wrappedBlob == null) {
+ Slog.w(TAG, "Failed to encrypt the reboot escrow key");
+ return false;
+ }
+ mStorage.writeRebootEscrowServerBlob(wrappedBlob);
+
+ Slog.i(TAG, "Reboot escrow key encrypted and stored.");
+ return true;
+ } catch (TimeoutException | RemoteException | IOException e) {
+ Slog.w(TAG, "Failed to encrypt the reboot escrow key ", e);
+ }
+
+ return false;
+ }
+}
diff --git a/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java b/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java
index 0a4d17f..e2e5046 100644
--- a/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java
+++ b/services/core/java/com/android/server/media/MediaButtonReceiverHolder.java
@@ -141,6 +141,11 @@
packageName != null ? packageName : "");
}
+ public static MediaButtonReceiverHolder create(int userId, ComponentName broadcastReceiver) {
+ return new MediaButtonReceiverHolder(userId, null, broadcastReceiver,
+ COMPONENT_TYPE_BROADCAST);
+ }
+
private MediaButtonReceiverHolder(int userId, PendingIntent pendingIntent,
ComponentName componentName, @ComponentType int componentType) {
mUserId = userId;
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index ea6e7d7..ae58d4c 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -18,6 +18,7 @@
import android.annotation.Nullable;
import android.app.PendingIntent;
+import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ParceledListSlice;
@@ -858,6 +859,21 @@
}
@Override
+ public void setMediaButtonBroadcastReceiver(ComponentName receiver) throws RemoteException {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ if ((mPolicies & SessionPolicyProvider.SESSION_POLICY_IGNORE_BUTTON_RECEIVER)
+ != 0) {
+ return;
+ }
+ mMediaButtonReceiverHolder = MediaButtonReceiverHolder.create(mUserId, receiver);
+ mService.onMediaButtonReceiverChanged(MediaSessionRecord.this);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override
public void setLaunchPendingIntent(PendingIntent pi) throws RemoteException {
mLaunchIntent = pi;
}
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index a12932a8..de85d9e 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -30,9 +30,9 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
-import android.content.pm.PackageParser;
import android.content.pm.PackageParser.PackageParserException;
import android.content.pm.parsing.PackageInfoWithoutStateUtils;
+import android.content.pm.parsing.ParsingPackageUtils;
import android.os.Binder;
import android.os.Environment;
import android.os.RemoteException;
@@ -508,7 +508,8 @@
for (ApexInfo ai : allPkgs) {
File apexFile = new File(ai.modulePath);
- parallelPackageParser.submit(apexFile, PackageParser.PARSE_COLLECT_CERTIFICATES);
+ parallelPackageParser.submit(apexFile,
+ ParsingPackageUtils.PARSE_COLLECT_CERTIFICATES);
parsingApexInfo.put(apexFile, ai);
}
diff --git a/services/core/java/com/android/server/pm/ApkChecksums.java b/services/core/java/com/android/server/pm/ApkChecksums.java
index ff3a12a..5373f99 100644
--- a/services/core/java/com/android/server/pm/ApkChecksums.java
+++ b/services/core/java/com/android/server/pm/ApkChecksums.java
@@ -24,7 +24,7 @@
import static android.content.pm.Checksum.TYPE_WHOLE_SHA256;
import static android.content.pm.Checksum.TYPE_WHOLE_SHA512;
import static android.content.pm.PackageManager.EXTRA_CHECKSUMS;
-import static android.content.pm.PackageParser.APK_FILE_EXTENSION;
+import static android.content.pm.parsing.ApkLiteParseUtils.APK_FILE_EXTENSION;
import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA256;
import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_CHUNKED_SHA512;
import static android.util.apk.ApkSigningBlockUtils.CONTENT_DIGEST_VERITY_CHUNKED_SHA256;
@@ -39,6 +39,7 @@
import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageParser;
import android.content.pm.Signature;
+import android.content.pm.parsing.ApkLiteParseUtils;
import android.os.Handler;
import android.os.SystemClock;
import android.os.incremental.IncrementalManager;
@@ -171,7 +172,7 @@
* @throws IllegalArgumentException if the code path is not an .apk.
*/
public static String buildDigestsPathForApk(String codePath) {
- if (!PackageParser.isApkPath(codePath)) {
+ if (!ApkLiteParseUtils.isApkPath(codePath)) {
throw new IllegalStateException("Code path is not an apk " + codePath);
}
return codePath.substring(0, codePath.length() - APK_FILE_EXTENSION.length())
diff --git a/services/core/java/com/android/server/pm/IncrementalStates.java b/services/core/java/com/android/server/pm/IncrementalStates.java
index 780c522..f5ec595 100644
--- a/services/core/java/com/android/server/pm/IncrementalStates.java
+++ b/services/core/java/com/android/server/pm/IncrementalStates.java
@@ -61,12 +61,12 @@
public IncrementalStates() {
// By default the package is not startable and not fully loaded (i.e., is loading)
- this(false, true);
+ this(false, true, 0);
}
- public IncrementalStates(boolean isStartable, boolean isLoading) {
+ public IncrementalStates(boolean isStartable, boolean isLoading, float loadingProgress) {
mStartableState = new StartableState(isStartable);
- mLoadingState = new LoadingState(isLoading);
+ mLoadingState = new LoadingState(isLoading, loadingProgress);
mStatusConsumer = new StatusConsumer();
}
@@ -405,9 +405,10 @@
private boolean mIsLoading;
private float mProgress;
- LoadingState(boolean isLoading) {
+ LoadingState(boolean isLoading, float loadingProgress) {
mIsLoading = isLoading;
- mProgress = isLoading ? 0 : 1;
+ // loading progress is reset to 1 if loading has finished
+ mProgress = isLoading ? loadingProgress : 1;
}
public boolean isLoading() {
diff --git a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
index 71b99bd..13fe8a0 100644
--- a/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
+++ b/services/core/java/com/android/server/pm/PackageAbiHelperImpl.java
@@ -17,7 +17,7 @@
package com.android.server.pm;
import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
-import static android.content.pm.PackageParser.isApkFile;
+import static android.content.pm.parsing.ApkLiteParseUtils.isApkFile;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
import static android.os.incremental.IncrementalManager.isIncrementalPath;
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index e143bd0..e218dc1 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -81,11 +81,12 @@
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageParser;
-import android.content.pm.PackageParser.ApkLite;
-import android.content.pm.PackageParser.PackageLite;
import android.content.pm.PackageParser.PackageParserException;
import android.content.pm.dex.DexMetadataHelper;
+import android.content.pm.parsing.ApkLite;
import android.content.pm.parsing.ApkLiteParseUtils;
+import android.content.pm.parsing.PackageLite;
+import android.content.pm.parsing.ParsingPackageUtils;
import android.content.pm.parsing.result.ParseResult;
import android.content.pm.parsing.result.ParseTypeImpl;
import android.graphics.Bitmap;
@@ -2671,16 +2672,17 @@
// Populate package name of the apex session
mPackageName = null;
- final ApkLite apk;
- try {
- apk = PackageParser.parseApkLite(
- mResolvedBaseFile, PackageParser.PARSE_COLLECT_CERTIFICATES);
- } catch (PackageParserException e) {
- throw PackageManagerException.from(e);
+ final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
+ final ParseResult<ApkLite> ret = ApkLiteParseUtils.parseApkLite(input.reset(),
+ mResolvedBaseFile, ParsingPackageUtils.PARSE_COLLECT_CERTIFICATES);
+ if (ret.isError()) {
+ throw new PackageManagerException(ret.getErrorCode(), ret.getErrorMessage(),
+ ret.getException());
}
+ final ApkLite apk = ret.getResult();
if (mPackageName == null) {
- mPackageName = apk.packageName;
+ mPackageName = apk.getPackageName();
mVersionCode = apk.getLongVersionCode();
}
}
@@ -2745,29 +2747,29 @@
// Verify that all staged packages are internally consistent
final ArraySet<String> stagedSplits = new ArraySet<>();
- final ArrayMap<String, PackageParser.ApkLite> splitApks = new ArrayMap<>();
- ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
+ final ArrayMap<String, ApkLite> splitApks = new ArrayMap<>();
+ final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
for (File addedFile : addedFiles) {
- ParseResult<ApkLite> result = ApkLiteParseUtils.parseApkLite(input.reset(),
- addedFile, PackageParser.PARSE_COLLECT_CERTIFICATES);
+ final ParseResult<ApkLite> result = ApkLiteParseUtils.parseApkLite(input.reset(),
+ addedFile, ParsingPackageUtils.PARSE_COLLECT_CERTIFICATES);
if (result.isError()) {
throw new PackageManagerException(result.getErrorCode(),
result.getErrorMessage(), result.getException());
}
final ApkLite apk = result.getResult();
- if (!stagedSplits.add(apk.splitName)) {
+ if (!stagedSplits.add(apk.getSplitName())) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
- "Split " + apk.splitName + " was defined multiple times");
+ "Split " + apk.getSplitName() + " was defined multiple times");
}
// Use first package to define unknown values
if (mPackageName == null) {
- mPackageName = apk.packageName;
+ mPackageName = apk.getPackageName();
mVersionCode = apk.getLongVersionCode();
}
if (mSigningDetails == PackageParser.SigningDetails.UNKNOWN) {
- mSigningDetails = apk.signingDetails;
+ mSigningDetails = apk.getSigningDetails();
}
assertApkConsistentLocked(String.valueOf(addedFile), apk);
@@ -2780,10 +2782,10 @@
}
// Yell loudly if installers drop attribute installLocation when apps explicitly set.
- if (apk.installLocation != PackageInfo.INSTALL_LOCATION_UNSPECIFIED) {
+ if (apk.getInstallLocation() != PackageInfo.INSTALL_LOCATION_UNSPECIFIED) {
final String installerPackageName = getInstallerPackageName();
if (installerPackageName != null
- && (params.installLocation != apk.installLocation)) {
+ && (params.installLocation != apk.getInstallLocation())) {
Slog.wtf(TAG, installerPackageName
+ " drops manifest attribute android:installLocation in " + targetName
+ " for " + mPackageName);
@@ -2791,14 +2793,14 @@
}
final File targetFile = new File(stageDir, targetName);
- resolveAndStageFileLocked(addedFile, targetFile, apk.splitName);
+ resolveAndStageFileLocked(addedFile, targetFile, apk.getSplitName());
// Base is coming from session
- if (apk.splitName == null) {
+ if (apk.getSplitName() == null) {
mResolvedBaseFile = targetFile;
baseApk = apk;
} else {
- splitApks.put(apk.splitName, apk);
+ splitApks.put(apk.getSplitName(), apk);
}
}
@@ -2854,7 +2856,7 @@
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Full install must include a base package");
}
- if (baseApk.isSplitRequired && stagedSplits.size() <= 1) {
+ if (baseApk.isSplitRequired() && stagedSplits.size() <= 1) {
throw new PackageManagerException(INSTALL_FAILED_MISSING_SPLIT,
"Missing split for " + mPackageName);
}
@@ -2879,10 +2881,10 @@
}
final PackageLite existing = pkgLiteResult.getResult();
packageLite = existing;
- assertPackageConsistentLocked("Existing", existing.packageName,
+ assertPackageConsistentLocked("Existing", existing.getPackageName(),
existing.getLongVersionCode());
final PackageParser.SigningDetails signingDetails =
- unsafeGetCertsWithoutVerification(existing.baseCodePath);
+ unsafeGetCertsWithoutVerification(existing.getBaseApkPath());
if (!mSigningDetails.signaturesMatchExactly(signingDetails)) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Existing signatures are inconsistent");
@@ -2895,10 +2897,10 @@
}
// Inherit splits if not overridden.
- if (!ArrayUtils.isEmpty(existing.splitNames)) {
- for (int i = 0; i < existing.splitNames.length; i++) {
- final String splitName = existing.splitNames[i];
- final File splitFile = new File(existing.splitCodePaths[i]);
+ if (!ArrayUtils.isEmpty(existing.getSplitNames())) {
+ for (int i = 0; i < existing.getSplitNames().length; i++) {
+ final String splitName = existing.getSplitNames()[i];
+ final File splitFile = new File(existing.getSplitApkPaths()[i]);
final boolean splitRemoved = removeSplitList.contains(splitName);
if (!stagedSplits.contains(splitName) && !splitRemoved) {
inheritFileLocked(splitFile);
@@ -2978,8 +2980,8 @@
}
}
// For the case of split required, failed if no splits existed
- if (packageLite.isSplitRequired) {
- final int existingSplits = ArrayUtils.size(existing.splitNames);
+ if (packageLite.isSplitRequired()) {
+ final int existingSplits = ArrayUtils.size(existing.getSplitNames());
final boolean allSplitsRemoved = (existingSplits == removeSplitList.size());
final boolean onlyBaseFileStaged = (stagedSplits.size() == 1
&& stagedSplits.contains(null));
@@ -2989,7 +2991,7 @@
}
}
}
- if (packageLite.useEmbeddedDex) {
+ if (packageLite.isUseEmbeddedDex()) {
for (File file : mResolvedStagedFiles) {
if (file.getName().endsWith(".apk")
&& !DexManager.auditUncompressedDexInApk(file.getPath())) {
@@ -3002,7 +3004,7 @@
final boolean isInstallerShell = (mInstallerUid == Process.SHELL_UID);
if (isInstallerShell && isIncrementalInstallation() && mIncrementalFileStorages != null) {
- if (!packageLite.debuggable && !packageLite.profilableByShell) {
+ if (!packageLite.isDebuggable() && !packageLite.isProfileableByShell()) {
mIncrementalFileStorages.disallowReadLogs();
}
}
@@ -3174,8 +3176,8 @@
@GuardedBy("mLock")
private void assertApkConsistentLocked(String tag, ApkLite apk)
throws PackageManagerException {
- assertPackageConsistentLocked(tag, apk.packageName, apk.getLongVersionCode());
- if (!mSigningDetails.signaturesMatchExactly(apk.signingDetails)) {
+ assertPackageConsistentLocked(tag, apk.getPackageName(), apk.getLongVersionCode());
+ if (!mSigningDetails.signaturesMatchExactly(apk.getSigningDetails())) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
tag + " signatures are inconsistent");
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 68b0698..c27e670 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -101,7 +101,7 @@
import static android.content.pm.PackageManager.UNINSTALL_REASON_UNKNOWN;
import static android.content.pm.PackageManagerInternal.LAST_KNOWN_PACKAGE;
import static android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion.SIGNING_BLOCK_V4;
-import static android.content.pm.PackageParser.isApkFile;
+import static android.content.pm.parsing.ApkLiteParseUtils.isApkFile;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
import static android.os.incremental.IncrementalManager.isIncrementalPath;
import static android.os.storage.StorageManager.FLAG_STORAGE_CE;
@@ -211,9 +211,7 @@
import android.content.pm.PackageManagerInternal.PackageListObserver;
import android.content.pm.PackageManagerInternal.PrivateResolveFlags;
import android.content.pm.PackageParser;
-import android.content.pm.PackageParser.PackageLite;
import android.content.pm.PackageParser.PackageParserException;
-import android.content.pm.PackageParser.ParseFlags;
import android.content.pm.PackageParser.SigningDetails;
import android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion;
import android.content.pm.PackagePartitions;
@@ -241,7 +239,9 @@
import android.content.pm.dex.DexMetadataHelper;
import android.content.pm.dex.IArtManager;
import android.content.pm.parsing.ApkLiteParseUtils;
+import android.content.pm.parsing.PackageLite;
import android.content.pm.parsing.ParsingPackageUtils;
+import android.content.pm.parsing.ParsingPackageUtils.ParseFlags;
import android.content.pm.parsing.component.ParsedActivity;
import android.content.pm.parsing.component.ParsedInstrumentation;
import android.content.pm.parsing.component.ParsedIntentInfo;
@@ -518,8 +518,7 @@
public static final boolean DEBUG_PERMISSIONS = false;
private static final boolean DEBUG_SHARED_LIBRARIES = false;
public static final boolean DEBUG_COMPRESSION = Build.IS_DEBUGGABLE;
- public static final boolean DEBUG_CACHES = false;
- public static final boolean TRACE_CACHES = false;
+ public static final boolean TRACE_SNAPSHOTS = false;
private static final boolean DEBUG_PER_UID_READ_TIMEOUTS = false;
// Debug output for dexopting. This is shared between PackageManagerService, OtaDexoptService
@@ -2015,7 +2014,7 @@
}
/**
- * A computer provides the functional interface to the cache
+ * A computer provides the functional interface to the snapshot.
*/
private interface Computer {
@@ -2181,13 +2180,11 @@
private final ResolveInfo mInstantAppInstallerInfo;
private final InstantAppRegistry mInstantAppRegistry;
private final ApplicationInfo mLocalAndroidApplication;
+ private final AppsFilter mAppsFilter;
// Immutable service attribute
private final String mAppPredictionServicePackage;
- // TODO: create cache copies of the following attributes
- private final AppsFilter mAppsFilter;
-
// The following are not cloned since changes to these have never
// been guarded by the PMS lock.
private final Context mContext;
@@ -4738,11 +4735,11 @@
private final Computer mLiveComputer;
// A lock-free cache for frequently called functions.
private volatile Computer mSnapshotComputer;
- // If true, the cached computer object is invalid (the cache is stale).
- // The attribute is static since it may be set from outside classes.
+ // If true, the snapshot is invalid (stale). The attribute is static since it may be
+ // set from outside classes.
private static volatile boolean sSnapshotInvalid = true;
- // If true, the cache is corked. Do not create a new cache but continue to use the
- // existing one. This throttles cache creation during periods of churn in Package
+ // If true, the snapshot is corked. Do not create a new snapshot but use the live
+ // computer. This throttles snapshot creation during periods of churn in Package
// Manager.
private static volatile boolean sSnapshotCorked = false;
@@ -4754,13 +4751,121 @@
*/
private final Object mSnapshotLock = new Object();
- // A counter of all queries that hit the cache.
- private AtomicInteger mSnapshotHits = new AtomicInteger(0);
+ // A counter of all queries that hit the current snapshot.
+ @GuardedBy("mSnapshotLock")
+ private int mSnapshotHits = 0;
- // The number of queries at the last miss. This is updated when the cache is rebuilt
- // (guarded by mLock) and is used to report the hit run-length.
+ // A class to record snapshot statistics.
+ private static class SnapshotStatistics {
+ // A build time is "big" if it takes longer than 5ms.
+ private static final long SNAPSHOT_BIG_BUILD_TIME_NS = TimeUnit.MILLISECONDS.toNanos(5);
+
+ // A snapshot is in quick succession to the previous snapshot if it less than
+ // 100ms since the previous snapshot.
+ private static final long SNAPSHOT_QUICK_REBUILD_INTERVAL_NS =
+ TimeUnit.MILLISECONDS.toNanos(100);
+
+ // The interval between snapshot statistics logging, in ns.
+ private static final long SNAPSHOT_LOG_INTERVAL_NS = TimeUnit.MINUTES.toNanos(10);
+
+ // The throttle parameters for big build reporting. Do not report more than this
+ // many events in a single log interval.
+ private static final int SNAPSHOT_BUILD_REPORT_LIMIT = 10;
+
+ // The time the snapshot statistics were last logged.
+ private long mStatisticsSent = 0;
+
+ // The number of build events logged since the last periodic log.
+ private int mLoggedBuilds = 0;
+
+ // The time of the last build.
+ private long mLastBuildTime = 0;
+
+ // The number of times the snapshot has been rebuilt since the statistics were
+ // last logged.
+ private int mRebuilds = 0;
+
+ // The number of times the snapshot has been used since it was rebuilt.
+ private int mReused = 0;
+
+ // The number of "big" build times since the last log. "Big" is defined by
+ // SNAPSHOT_BIG_BUILD_TIME.
+ private int mBigBuilds = 0;
+
+ // The number of quick rebuilds. "Quick" is defined by
+ // SNAPSHOT_QUICK_REBUILD_INTERVAL_NS.
+ private int mQuickRebuilds = 0;
+
+ // The time take to build a snapshot. This is cumulative over the rebuilds recorded
+ // in mRebuilds, so the average time to build a snapshot is given by
+ // mBuildTimeNs/mRebuilds.
+ private int mBuildTimeNs = 0;
+
+ // The maximum build time since the last log.
+ private long mMaxBuildTimeNs = 0;
+
+ // The constant that converts ns to ms. This is the divisor.
+ private final long NS_TO_MS = TimeUnit.MILLISECONDS.toNanos(1);
+
+ // Convert ns to an int ms. The maximum range of this method is about 24 days.
+ // There is no expectation that an event will take longer than that.
+ private int nsToMs(long ns) {
+ return (int) (ns / NS_TO_MS);
+ }
+
+ // The single method records a rebuild. The "now" parameter is passed in because
+ // the caller needed it to computer the duration, so pass it in to avoid
+ // recomputing it.
+ private void rebuild(long now, long done, int hits) {
+ if (mStatisticsSent == 0) {
+ mStatisticsSent = now;
+ }
+ final long elapsed = now - mLastBuildTime;
+ final long duration = done - now;
+ mLastBuildTime = now;
+
+ if (mMaxBuildTimeNs < duration) {
+ mMaxBuildTimeNs = duration;
+ }
+ mRebuilds++;
+ mReused += hits;
+ mBuildTimeNs += duration;
+
+ boolean log_build = false;
+ if (duration > SNAPSHOT_BIG_BUILD_TIME_NS) {
+ log_build = true;
+ mBigBuilds++;
+ }
+ if (elapsed < SNAPSHOT_QUICK_REBUILD_INTERVAL_NS) {
+ log_build = true;
+ mQuickRebuilds++;
+ }
+ if (log_build && mLoggedBuilds < SNAPSHOT_BUILD_REPORT_LIMIT) {
+ EventLogTags.writePmSnapshotRebuild(nsToMs(duration), nsToMs(elapsed));
+ mLoggedBuilds++;
+ }
+
+ final long log_interval = now - mStatisticsSent;
+ if (log_interval >= SNAPSHOT_LOG_INTERVAL_NS) {
+ EventLogTags.writePmSnapshotStats(mRebuilds, mReused,
+ mBigBuilds, mQuickRebuilds,
+ nsToMs(mMaxBuildTimeNs),
+ nsToMs(mBuildTimeNs));
+ mStatisticsSent = now;
+ mRebuilds = 0;
+ mReused = 0;
+ mBuildTimeNs = 0;
+ mMaxBuildTimeNs = 0;
+ mBigBuilds = 0;
+ mQuickRebuilds = 0;
+ mLoggedBuilds = 0;
+ }
+ }
+ }
+
+ // Snapshot statistics.
@GuardedBy("mLock")
- private int mSnapshotRebuilt = 0;
+ private final SnapshotStatistics mSnapshotStatistics = new SnapshotStatistics();
// The snapshot disable/enable switch. An image with the flag set true uses snapshots
// and an image with the flag set false does not use snapshots.
@@ -4786,23 +4891,21 @@
// yet invalidated the snapshot. Always give the thread the live computer.
return mLiveComputer;
}
- int hits = 0;
- if (TRACE_CACHES) {
- hits = mSnapshotHits.incrementAndGet();
- }
synchronized (mSnapshotLock) {
Computer c = mSnapshotComputer;
if (sSnapshotCorked && (c != null)) {
// Snapshots are corked, which means new ones should not be built right now.
return c;
}
+ // Deliberately capture the value pre-increment
+ final int hits = mSnapshotHits++;
if (sSnapshotInvalid || (c == null)) {
// The snapshot is invalid if it is marked as invalid or if it is null. If it
// is null, then it is currently being rebuilt by rebuildSnapshot().
synchronized (mLock) {
// Rebuild the snapshot if it is invalid. Note that the snapshot might be
// invalidated as it is rebuilt. However, the snapshot is still
- // self-consistent (the lock is being held)and is current as of the time
+ // self-consistent (the lock is being held) and is current as of the time
// this function is entered.
if (sSnapshotInvalid) {
rebuildSnapshot(hits);
@@ -4820,22 +4923,20 @@
}
/**
- * Rebuild the cached computer. mSnapshotComputer is temporarily set to null to block
- * other threads from using the invalid computer until it is rebuilt.
+ * Rebuild the cached computer. mSnapshotComputer is temporarily set to null to block other
+ * threads from using the invalid computer until it is rebuilt.
*/
@GuardedBy("mLock")
private void rebuildSnapshot(int hits) {
+ final long now = System.nanoTime();
mSnapshotComputer = null;
sSnapshotInvalid = false;
final Snapshot args = new Snapshot(Snapshot.SNAPPED);
mSnapshotComputer = new ComputerEngine(args);
+ final long done = System.nanoTime();
- // Still guarded by mLock
- final int run = hits - mSnapshotRebuilt;
- mSnapshotRebuilt = hits;
- if (TRACE_CACHES) {
- Log.w(TAG, "computer: rebuild after " + run + " hits");
- }
+ mSnapshotStatistics.rebuild(now, done, hits);
+ mSnapshotHits = 0;
}
/**
@@ -4852,8 +4953,8 @@
* @hide
*/
public static void onChange(@Nullable Watchable what) {
- if (TRACE_CACHES) {
- Log.e(TAG, "computer: onChange(" + what + ")");
+ if (TRACE_SNAPSHOTS) {
+ Log.e(TAG, "snapshot: onChange(" + what + ")");
}
sSnapshotInvalid = true;
}
@@ -6251,7 +6352,7 @@
if (separateProcesses != null && separateProcesses.length() > 0) {
if ("*".equals(separateProcesses)) {
- mDefParseFlags = PackageParser.PARSE_IGNORE_PROCESSES;
+ mDefParseFlags = ParsingPackageUtils.PARSE_IGNORE_PROCESSES;
mSeparateProcesses = null;
Slog.w(TAG, "Running with debug.separate_processes: * (ALL)");
} else {
@@ -6451,7 +6552,7 @@
scanFlags = scanFlags | SCAN_FIRST_BOOT_OR_UPGRADE;
}
- final int systemParseFlags = mDefParseFlags | PackageParser.PARSE_IS_SYSTEM_DIR;
+ final int systemParseFlags = mDefParseFlags | ParsingPackageUtils.PARSE_IS_SYSTEM_DIR;
final int systemScanFlags = scanFlags | SCAN_AS_SYSTEM;
PackageParser2 packageParser = injector.getScanningCachingPackageParser();
@@ -7091,7 +7192,7 @@
*/
private boolean enableCompressedPackage(AndroidPackage stubPkg,
@NonNull PackageSetting stubPkgSetting) {
- final int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY
+ final int parseFlags = mDefParseFlags | ParsingPackageUtils.PARSE_CHATTY
| PackageParser.PARSE_ENFORCE_CODE;
synchronized (mInstallLock) {
final AndroidPackage pkg;
@@ -11317,7 +11418,8 @@
@ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,
@Nullable UserHandle user)
throws PackageManagerException {
- final boolean scanSystemPartition = (parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0;
+ final boolean scanSystemPartition =
+ (parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) != 0;
final String renamedPkgName;
final PackageSetting disabledPkgSetting;
final boolean isSystemPkgUpdated;
@@ -11360,7 +11462,7 @@
0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true)
: null;
if (DEBUG_PACKAGE_SCANNING
- && (parseFlags & PackageParser.PARSE_CHATTY) != 0
+ && (parseFlags & ParsingPackageUtils.PARSE_CHATTY) != 0
&& sharedUserSetting != null) {
Log.d(TAG, "Shared UserID " + parsedPackage.getSharedUserId()
+ " (uid=" + sharedUserSetting.userId + "):"
@@ -13179,10 +13281,11 @@
sharedUserSetting = mSettings.getSharedUserLPw(parsedPackage.getSharedUserId(),
0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true /*create*/);
if (DEBUG_PACKAGE_SCANNING) {
- if ((parseFlags & PackageParser.PARSE_CHATTY) != 0)
+ if ((parseFlags & ParsingPackageUtils.PARSE_CHATTY) != 0) {
Log.d(TAG, "Shared UserID " + parsedPackage.getSharedUserId()
+ " (uid=" + sharedUserSetting.userId + "):"
+ " packages=" + sharedUserSetting.packages);
+ }
}
}
String platformPackageName = mPlatformPackage == null
@@ -13339,7 +13442,7 @@
final int userId = user == null ? 0 : user.getIdentifier();
// Modify state for the given package setting
commitPackageSettings(pkg, oldPkg, pkgSetting, scanFlags,
- (parseFlags & PackageParser.PARSE_CHATTY) != 0 /*chatty*/, reconciledPkg);
+ (parseFlags & ParsingPackageUtils.PARSE_CHATTY) != 0 /*chatty*/, reconciledPkg);
if (pkgSetting.getInstantApp(userId)) {
mInstantAppRegistry.addInstantAppLPw(userId, pkgSetting.appId);
}
@@ -13548,8 +13651,9 @@
List<String> changedAbiCodePath = null;
if (DEBUG_PACKAGE_SCANNING) {
- if ((parseFlags & PackageParser.PARSE_CHATTY) != 0)
+ if ((parseFlags & ParsingPackageUtils.PARSE_CHATTY) != 0) {
Log.d(TAG, "Scanning package " + parsedPackage.getPackageName());
+ }
}
// Initialize package source and resource directories
@@ -13816,7 +13920,7 @@
} else if (pkgSetting.firstInstallTime == 0) {
// We need *something*. Take time time stamp of the file.
pkgSetting.firstInstallTime = pkgSetting.lastUpdateTime = scanFileTime;
- } else if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0) {
+ } else if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) != 0) {
if (scanFileTime != pkgSetting.timeStamp) {
// A package on the system image has changed; consider this
// to be an update.
@@ -14013,7 +14117,7 @@
private void assertPackageIsValid(AndroidPackage pkg, final @ParseFlags int parseFlags,
final @ScanFlags int scanFlags)
throws PackageManagerException {
- if ((parseFlags & PackageParser.PARSE_ENFORCE_CODE) != 0) {
+ if ((parseFlags & ParsingPackageUtils.PARSE_ENFORCE_CODE) != 0) {
assertCodePolicy(pkg);
}
@@ -14270,7 +14374,7 @@
if ((scanFlags & SCAN_AS_SYSTEM) != 0) {
// We are scanning a system overlay. This can be the first scan of the
// system/vendor/oem partition, or an update to the system overlay.
- if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
+ if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) == 0) {
// This must be an update to a system overlay. Immutable overlays cannot be
// upgraded.
Objects.requireNonNull(mOverlayConfig,
@@ -14350,7 +14454,7 @@
// If the package is not on a system partition ensure it is signed with at least the
// minimum signature scheme version required for its target SDK.
- if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
+ if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) == 0) {
int minSignatureSchemeVersion =
ApkSignatureVerifier.getMinimumSignatureSchemeVersionForTargetSdk(
pkg.getTargetSdkVersion());
@@ -18015,11 +18119,12 @@
// Try enumerating all code paths before deleting
List<String> allCodePaths = Collections.EMPTY_LIST;
if (codeFile != null && codeFile.exists()) {
- try {
- final PackageLite pkg = PackageParser.parsePackageLite(codeFile, 0);
- allCodePaths = pkg.getAllCodePaths();
- } catch (PackageParserException e) {
- // Ignored; we tried our best
+ final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
+ final ParseResult<PackageLite> result = ApkLiteParseUtils.parsePackageLite(
+ input.reset(), codeFile, /* flags */ 0);
+ if (result.isSuccess()) {
+ // Ignore error; we tried our best
+ allCodePaths = result.getResult().getAllApkPaths();
}
}
@@ -18637,7 +18742,7 @@
// We just determined the app is signed correctly, so bring
// over the latest parsed certs.
} else {
- if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
+ if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) == 0) {
throw new ReconcileFailure(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
"Package " + parsedPackage.getPackageName()
+ " upgrade keys do not match the previously installed"
@@ -18687,7 +18792,7 @@
}
}
} catch (PackageManagerException e) {
- if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
+ if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) == 0) {
throw new ReconcileFailure(e);
}
signingDetails = parsedPackage.getSigningDetails();
@@ -18766,7 +18871,8 @@
// apps are scanned to avoid dependency based scanning.
final ScanResult scanResult = scannedPackages.get(installPackageName);
if ((scanResult.request.scanFlags & SCAN_BOOTING) != 0
- || (scanResult.request.parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0) {
+ || (scanResult.request.parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR)
+ != 0) {
continue;
}
try {
@@ -19652,9 +19758,9 @@
}
// Retrieve PackageSettings and parse package
- @ParseFlags final int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY
- | PackageParser.PARSE_ENFORCE_CODE
- | (onExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0);
+ @ParseFlags final int parseFlags = mDefParseFlags | ParsingPackageUtils.PARSE_CHATTY
+ | ParsingPackageUtils.PARSE_ENFORCE_CODE
+ | (onExternal ? ParsingPackageUtils.PARSE_EXTERNAL_STORAGE : 0);
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
final ParsedPackage parsedPackage;
@@ -21384,8 +21490,8 @@
final File codePath = new File(codePathString);
@ParseFlags int parseFlags =
mDefParseFlags
- | PackageParser.PARSE_MUST_BE_APK
- | PackageParser.PARSE_IS_SYSTEM_DIR;
+ | ParsingPackageUtils.PARSE_MUST_BE_APK
+ | ParsingPackageUtils.PARSE_IS_SYSTEM_DIR;
@ScanFlags int scanFlags = SCAN_AS_SYSTEM;
for (int i = mDirsToScanAsSystem.size() - 1; i >= 0; i--) {
ScanPartition partition = mDirsToScanAsSystem.get(i);
@@ -24837,7 +24943,7 @@
final ArrayList<PackageFreezer> freezers = new ArrayList<>();
final ArrayList<AndroidPackage> loaded = new ArrayList<>();
- final int parseFlags = mDefParseFlags | PackageParser.PARSE_EXTERNAL_STORAGE;
+ final int parseFlags = mDefParseFlags | ParsingPackageUtils.PARSE_EXTERNAL_STORAGE;
final VersionInfo ver;
final List<PackageSetting> packages;
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index d3d7c60..ee94b85 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -35,9 +35,12 @@
import android.content.pm.PackageInfoLite;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
-import android.content.pm.PackageParser.PackageParserException;
import android.content.pm.ResolveInfo;
import android.content.pm.Signature;
+import android.content.pm.parsing.ApkLiteParseUtils;
+import android.content.pm.parsing.PackageLite;
+import android.content.pm.parsing.result.ParseResult;
+import android.content.pm.parsing.result.ParseTypeImpl;
import android.os.Build;
import android.os.Debug;
import android.os.Environment;
@@ -819,8 +822,8 @@
/**
* Parse given package and return minimal details.
*/
- public static PackageInfoLite getMinimalPackageInfo(Context context,
- PackageParser.PackageLite pkg, String packagePath, int flags, String abiOverride) {
+ public static PackageInfoLite getMinimalPackageInfo(Context context, PackageLite pkg,
+ String packagePath, int flags, String abiOverride) {
final PackageInfoLite ret = new PackageInfoLite();
if (packagePath == null || pkg == null) {
Slog.i(TAG, "Invalid package file " + packagePath);
@@ -843,19 +846,19 @@
}
final int recommendedInstallLocation = PackageHelper.resolveInstallLocation(context,
- pkg.packageName, pkg.installLocation, sizeBytes, flags);
+ pkg.getPackageName(), pkg.getInstallLocation(), sizeBytes, flags);
- ret.packageName = pkg.packageName;
- ret.splitNames = pkg.splitNames;
- ret.versionCode = pkg.versionCode;
- ret.versionCodeMajor = pkg.versionCodeMajor;
- ret.baseRevisionCode = pkg.baseRevisionCode;
- ret.splitRevisionCodes = pkg.splitRevisionCodes;
- ret.installLocation = pkg.installLocation;
- ret.verifiers = pkg.verifiers;
+ ret.packageName = pkg.getPackageName();
+ ret.splitNames = pkg.getSplitNames();
+ ret.versionCode = pkg.getVersionCode();
+ ret.versionCodeMajor = pkg.getVersionCodeMajor();
+ ret.baseRevisionCode = pkg.getBaseRevisionCode();
+ ret.splitRevisionCodes = pkg.getSplitRevisionCodes();
+ ret.installLocation = pkg.getInstallLocation();
+ ret.verifiers = pkg.getVerifiers();
ret.recommendedInstallLocation = recommendedInstallLocation;
- ret.multiArch = pkg.multiArch;
- ret.debuggable = pkg.debuggable;
+ ret.multiArch = pkg.isMultiArch();
+ ret.debuggable = pkg.isDebuggable();
return ret;
}
@@ -868,11 +871,16 @@
*/
public static long calculateInstalledSize(String packagePath, String abiOverride) {
final File packageFile = new File(packagePath);
- final PackageParser.PackageLite pkg;
try {
- pkg = PackageParser.parsePackageLite(packageFile, 0);
- return PackageHelper.calculateInstalledSize(pkg, abiOverride);
- } catch (PackageParserException | IOException e) {
+ final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
+ final ParseResult<PackageLite> result = ApkLiteParseUtils.parsePackageLite(
+ input.reset(), packageFile, /* flags */ 0);
+ if (result.isError()) {
+ throw new PackageManagerException(result.getErrorCode(),
+ result.getErrorMessage(), result.getException());
+ }
+ return PackageHelper.calculateInstalledSize(result.getResult(), abiOverride);
+ } catch (PackageManagerException | IOException e) {
Slog.w(TAG, "Failed to calculate installed size: " + e);
return -1;
}
@@ -931,16 +939,23 @@
try {
final File packageFile = new File(packagePath);
- final PackageParser.PackageLite pkg = PackageParser.parsePackageLite(packageFile, 0);
- copyFile(pkg.baseCodePath, targetDir, "base.apk");
- if (!ArrayUtils.isEmpty(pkg.splitNames)) {
- for (int i = 0; i < pkg.splitNames.length; i++) {
- copyFile(pkg.splitCodePaths[i], targetDir,
- "split_" + pkg.splitNames[i] + ".apk");
+ final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
+ final ParseResult<PackageLite> result = ApkLiteParseUtils.parsePackageLite(
+ input.reset(), packageFile, /* flags */ 0);
+ if (result.isError()) {
+ Slog.w(TAG, "Failed to parse package at " + packagePath);
+ return result.getErrorCode();
+ }
+ final PackageLite pkg = result.getResult();
+ copyFile(pkg.getBaseApkPath(), targetDir, "base.apk");
+ if (!ArrayUtils.isEmpty(pkg.getSplitNames())) {
+ for (int i = 0; i < pkg.getSplitNames().length; i++) {
+ copyFile(pkg.getSplitApkPaths()[i], targetDir,
+ "split_" + pkg.getSplitNames()[i] + ".apk");
}
}
return PackageManager.INSTALL_SUCCEEDED;
- } catch (PackageParserException | IOException | ErrnoException e) {
+ } catch (IOException | ErrnoException e) {
Slog.w(TAG, "Failed to copy package at " + packagePath + ": " + e);
return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 446342a..3207d56a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -49,8 +49,6 @@
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageParser.ApkLite;
-import android.content.pm.PackageParser.PackageLite;
import android.content.pm.ParceledListSlice;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
@@ -61,7 +59,9 @@
import android.content.pm.dex.ArtManager;
import android.content.pm.dex.DexMetadataHelper;
import android.content.pm.dex.ISnapshotRuntimeProfileCallback;
+import android.content.pm.parsing.ApkLite;
import android.content.pm.parsing.ApkLiteParseUtils;
+import android.content.pm.parsing.PackageLite;
import android.content.pm.parsing.result.ParseResult;
import android.content.pm.parsing.result.ParseTypeImpl;
import android.content.res.AssetManager;
@@ -555,8 +555,8 @@
apkLiteResult.getException());
}
final ApkLite apkLite = apkLiteResult.getResult();
- PackageLite pkgLite = new PackageLite(null, apkLite.codePath, apkLite, null, null,
- null, null, null, null);
+ final PackageLite pkgLite = new PackageLite(null, apkLite.getPath(), apkLite, null,
+ null, null, null, null, null);
sessionSize += PackageHelper.calculateInstalledSize(pkgLite,
params.sessionParams.abiOverride, fd.getFileDescriptor());
} catch (IOException e) {
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 3369a4f..2929568 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -73,6 +73,7 @@
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
+import android.os.incremental.IncrementalManager;
import android.os.storage.StorageManager;
import android.os.storage.VolumeInfo;
import android.service.pm.PackageServiceDumpProto;
@@ -2957,6 +2958,8 @@
if (pkg.isPackageLoading()) {
serializer.attributeBoolean(null, "isLoading", true);
}
+ serializer.attributeFloat(null, "loadingProgress",
+ pkg.getIncrementalStates().getProgress());
writeUsesStaticLibLPw(serializer, pkg.usesStaticLibraries, pkg.usesStaticLibrariesVersions);
@@ -3699,6 +3702,7 @@
boolean installedForceQueryable = false;
boolean isStartable = false;
boolean isLoading = false;
+ float loadingProgress = 0;
try {
name = parser.getAttributeValue(null, ATTR_NAME);
realName = parser.getAttributeValue(null, "realName");
@@ -3717,6 +3721,7 @@
installedForceQueryable = parser.getAttributeBoolean(null, "forceQueryable", false);
isStartable = parser.getAttributeBoolean(null, "isStartable", false);
isLoading = parser.getAttributeBoolean(null, "isLoading", false);
+ loadingProgress = parser.getAttributeFloat(null, "loadingProgress", 0);
if (primaryCpuAbiString == null && legacyCpuAbiString != null) {
primaryCpuAbiString = legacyCpuAbiString;
@@ -3864,7 +3869,8 @@
packageSetting.secondaryCpuAbiString = secondaryCpuAbiString;
packageSetting.updateAvailable = updateAvailable;
packageSetting.forceQueryableOverride = installedForceQueryable;
- packageSetting.incrementalStates = new IncrementalStates(isStartable, isLoading);
+ packageSetting.incrementalStates = new IncrementalStates(isStartable, isLoading,
+ loadingProgress);
// Handle legacy string here for single-user mode
final String enabledStr = parser.getAttributeValue(null, ATTR_ENABLED);
if (enabledStr != null) {
@@ -4814,6 +4820,10 @@
pw.print(prefix); pw.print(" installerAttributionTag=");
pw.println(ps.installSource.installerAttributionTag);
}
+ if (IncrementalManager.isIncrementalPath(ps.getPathString())) {
+ pw.print(prefix); pw.println(" loadingProgress="
+ + (int) (ps.getIncrementalStates().getProgress() * 100) + "%");
+ }
if (ps.volumeUuid != null) {
pw.print(prefix); pw.print(" volumeUuid=");
pw.println(ps.volumeUuid);
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 314510b..f43240b 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -47,6 +47,7 @@
import android.content.pm.ShortcutServiceInternal;
import android.content.pm.UserInfo;
import android.content.pm.UserInfo.UserInfoFlag;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.os.Binder;
@@ -92,6 +93,7 @@
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
import android.util.TimeUtils;
+import android.util.TypedValue;
import android.util.TypedXmlPullParser;
import android.util.TypedXmlSerializer;
import android.util.Xml;
@@ -139,6 +141,7 @@
import java.util.Set;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
/**
* Service for {@link UserManager}.
@@ -461,6 +464,26 @@
}
};
+ /**
+ * Cache the owner name string, since it could be read repeatedly on a critical code path
+ * but hit by slow IO. This could be eliminated once we have the cached UserInfo in place.
+ */
+ private final AtomicReference<String> mOwnerName = new AtomicReference<>();
+
+ private final TypedValue mOwnerNameTypedValue = new TypedValue();
+
+ private final Configuration mLastConfiguration = new Configuration();
+
+ private final BroadcastReceiver mConfigurationChangeReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (!Intent.ACTION_CONFIGURATION_CHANGED.equals(intent.getAction())) {
+ return;
+ }
+ invalidateOwnerNameIfNecessary(context.getResources(), false /* forceUpdate */);
+ }
+ };
+
// TODO(b/161915546): remove once userWithName() is fixed / removed
// Use to debug / dump when user 0 is allocated at userWithName()
public static final boolean DBG_ALLOCATION = false; // DO NOT SUBMIT WITH TRUE
@@ -636,6 +659,7 @@
mHandler = new MainHandler();
mUserDataPreparer = userDataPreparer;
mUserTypes = UserTypeFactory.getUserTypes();
+ invalidateOwnerNameIfNecessary(context.getResources(), true /* forceUpdate */);
synchronized (mPackagesLock) {
mUsersDir = new File(dataDir, USER_INFO_DIR);
mUsersDir.mkdirs();
@@ -669,6 +693,10 @@
mContext.registerReceiver(mDisableQuietModeCallback,
new IntentFilter(ACTION_DISABLE_QUIET_MODE_AFTER_UNLOCK),
null, mHandler);
+
+ mContext.registerReceiver(mConfigurationChangeReceiver,
+ new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED),
+ null, mHandler);
}
/**
@@ -2851,7 +2879,16 @@
}
private String getOwnerName() {
- return mContext.getResources().getString(com.android.internal.R.string.owner_name);
+ return mOwnerName.get();
+ }
+
+ private void invalidateOwnerNameIfNecessary(@NonNull Resources res, boolean forceUpdate) {
+ final int configChanges = mLastConfiguration.updateFrom(res.getConfiguration());
+ if (forceUpdate || (configChanges & mOwnerNameTypedValue.changingConfigurations) != 0) {
+ res.getValue(com.android.internal.R.string.owner_name, mOwnerNameTypedValue, true);
+ final CharSequence ownerName = mOwnerNameTypedValue.coerceToString();
+ mOwnerName.set(ownerName != null ? ownerName.toString() : null);
+ }
}
private void scheduleWriteUser(UserData userData) {
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java
index a13680a..471a4d3 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackage.java
@@ -24,6 +24,7 @@
import android.content.pm.PackageParser;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.parsing.ParsingPackageRead;
+import android.content.pm.parsing.ParsingPackageUtils;
import android.content.pm.parsing.component.ParsedAttribution;
import android.content.pm.parsing.component.ParsedIntentInfo;
import android.content.pm.parsing.component.ParsedPermissionGroup;
@@ -56,7 +57,7 @@
/**
* The names of packages to adopt ownership of permissions from, parsed under
- * {@link PackageParser#TAG_ADOPT_PERMISSIONS}.
+ * {@link ParsingPackageUtils#TAG_ADOPT_PERMISSIONS}.
* @see R.styleable#AndroidManifestOriginalPackage_name
*/
@NonNull
@@ -84,7 +85,7 @@
/**
* For use with {@link com.android.server.pm.KeySetManagerService}. Parsed in
- * {@link PackageParser#TAG_KEY_SETS}.
+ * {@link ParsingPackageUtils#TAG_KEY_SETS}.
* @see R.styleable#AndroidManifestKeySet
* @see R.styleable#AndroidManifestPublicKey
*/
@@ -230,7 +231,7 @@
/**
* For use with {@link com.android.server.pm.KeySetManagerService}. Parsed in
- * {@link PackageParser#TAG_KEY_SETS}.
+ * {@link ParsingPackageUtils#TAG_KEY_SETS}.
* @see R.styleable#AndroidManifestUpgradeKeySet
*/
@NonNull
diff --git a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java
index ab25a7c..37dfea4 100644
--- a/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/pkg/AndroidPackageUtils.java
@@ -21,12 +21,12 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageParser;
import android.content.pm.PackageParser.PackageParserException;
import android.content.pm.SharedLibraryInfo;
import android.content.pm.VersionedPackage;
import android.content.pm.dex.DexMetadataHelper;
import android.content.pm.parsing.ParsingPackageRead;
+import android.content.pm.parsing.ParsingPackageUtils;
import android.content.pm.parsing.component.ParsedActivity;
import android.content.pm.parsing.component.ParsedInstrumentation;
import android.content.pm.parsing.component.ParsedProvider;
@@ -233,7 +233,7 @@
}
public static int getIcon(ParsingPackageRead pkg) {
- return (PackageParser.sUseRoundIcon && pkg.getRoundIconRes() != 0)
+ return (ParsingPackageUtils.sUseRoundIcon && pkg.getRoundIconRes() != 0)
? pkg.getRoundIconRes() : pkg.getIconRes();
}
diff --git a/services/core/java/com/android/server/power/DisplayPowerRequestMapper.java b/services/core/java/com/android/server/power/DisplayPowerRequestMapper.java
index 6477552..2fc3e40 100644
--- a/services/core/java/com/android/server/power/DisplayPowerRequestMapper.java
+++ b/services/core/java/com/android/server/power/DisplayPowerRequestMapper.java
@@ -25,7 +25,6 @@
import android.view.Display;
import com.android.internal.annotations.GuardedBy;
-import com.android.server.display.DisplayGroup;
/**
* Responsible for creating {@link DisplayPowerRequest}s and associating them with
@@ -110,8 +109,8 @@
DisplayManagerInternal displayManagerInternal, Handler handler) {
mDisplayManagerInternal = displayManagerInternal;
displayManager.registerDisplayListener(mDisplayListener, handler);
- mDisplayPowerRequests.append(DisplayGroup.DEFAULT, new DisplayPowerRequest());
- mDisplayGroupIds.append(Display.DEFAULT_DISPLAY, DisplayGroup.DEFAULT);
+ mDisplayPowerRequests.append(Display.DEFAULT_DISPLAY_GROUP, new DisplayPowerRequest());
+ mDisplayGroupIds.append(Display.DEFAULT_DISPLAY, Display.DEFAULT_DISPLAY_GROUP);
}
DisplayPowerRequest get(int displayId) {
diff --git a/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java b/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java
index e71b962..766cf9c 100644
--- a/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java
+++ b/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java
@@ -266,6 +266,7 @@
long token = pos.start(PowerStatsServiceMeterProto.CHANNEL);
pos.write(ChannelProto.ID, channel[i].id);
pos.write(ChannelProto.NAME, channel[i].name);
+ pos.write(ChannelProto.SUBSYSTEM, channel[i].subsystem);
pos.end(token);
}
}
@@ -275,7 +276,8 @@
for (int i = 0; i < channel.length; i++) {
Slog.d(TAG, "ChannelId: " + channel[i].id
- + ", ChannelName: " + channel[i].name);
+ + ", ChannelName: " + channel[i].name
+ + ", ChannelSubsystem: " + channel[i].subsystem);
}
}
@@ -284,7 +286,8 @@
for (int i = 0; i < channel.length; i++) {
pw.println("ChannelId: " + channel[i].id
- + ", ChannelName: " + channel[i].name);
+ + ", ChannelName: " + channel[i].name
+ + ", ChannelSubsystem: " + channel[i].subsystem);
}
}
}
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index bd66aa3..a4459d0 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -33,9 +33,9 @@
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
-import android.content.pm.PackageParser;
import android.content.pm.ParceledListSlice;
import android.content.pm.VersionedPackage;
+import android.content.pm.parsing.ApkLite;
import android.content.pm.parsing.ApkLiteParseUtils;
import android.content.pm.parsing.result.ParseResult;
import android.content.pm.parsing.result.ParseTypeImpl;
@@ -831,24 +831,24 @@
}
// Get information about the package to be installed.
- ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
- ParseResult<PackageParser.ApkLite> parseResult = ApkLiteParseUtils.parseApkLite(
+ final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
+ final ParseResult<ApkLite> parseResult = ApkLiteParseUtils.parseApkLite(
input.reset(), new File(session.resolvedBaseCodePath), 0);
if (parseResult.isError()) {
Slog.e(TAG, "Unable to parse new package: " + parseResult.getErrorMessage(),
parseResult.getException());
return false;
}
- PackageParser.ApkLite newPackage = parseResult.getResult();
+ final ApkLite newPackage = parseResult.getResult();
- String packageName = newPackage.packageName;
- int rollbackDataPolicy = computeRollbackDataPolicy(
- session.rollbackDataPolicy, newPackage.rollbackDataPolicy);
+ final String packageName = newPackage.getPackageName();
+ final int rollbackDataPolicy = computeRollbackDataPolicy(
+ session.rollbackDataPolicy, newPackage.getRollbackDataPolicy());
Slog.i(TAG, "Enabling rollback for install of " + packageName
+ ", session:" + session.sessionId
+ ", rollbackDataPolicy=" + rollbackDataPolicy);
- String installerPackageName = session.getInstallerPackageName();
+ final String installerPackageName = session.getInstallerPackageName();
if (!enableRollbackAllowed(installerPackageName, packageName)) {
Slog.e(TAG, "Installer " + installerPackageName
+ " is not allowed to enable rollback on " + packageName);
@@ -900,7 +900,7 @@
* a rollback object is inconsistent because it doesn't count apk-in-apex.
*/
ApplicationInfo appInfo = pkgInfo.applicationInfo;
- return rollback.enableForPackage(packageName, newPackage.versionCode,
+ return rollback.enableForPackage(packageName, newPackage.getVersionCode(),
pkgInfo.getLongVersionCode(), isApex, appInfo.sourceDir,
appInfo.splitSourceDirs, rollbackDataPolicy);
}
diff --git a/services/core/java/com/android/server/rotationresolver/OWNERS b/services/core/java/com/android/server/rotationresolver/OWNERS
new file mode 100644
index 0000000..81b6f05
--- /dev/null
+++ b/services/core/java/com/android/server/rotationresolver/OWNERS
@@ -0,0 +1 @@
+include /core/java/android/rotationresolver/OWNERS
diff --git a/services/core/java/com/android/server/speech/Android.bp b/services/core/java/com/android/server/speech/Android.bp
new file mode 100644
index 0000000..379b075
--- /dev/null
+++ b/services/core/java/com/android/server/speech/Android.bp
@@ -0,0 +1,13 @@
+filegroup {
+ name: "services.speech-sources",
+ srcs: ["java/**/*.java"],
+ path: "java",
+ visibility: ["//frameworks/base/services"],
+}
+
+java_library_static {
+ name: "services.speech",
+ defaults: ["platform_service_defaults"],
+ srcs: [":services.speech-sources"],
+ libs: ["services.core"],
+}
diff --git a/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java b/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java
new file mode 100644
index 0000000..96248c3
--- /dev/null
+++ b/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.speech;
+
+import static com.android.internal.infra.AbstractRemoteService.PERMANENT_BOUND_TIMEOUT_MS;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.RemoteException;
+import android.speech.IRecognitionListener;
+import android.speech.IRecognitionService;
+import android.speech.RecognitionService;
+import android.util.Slog;
+
+import com.android.internal.infra.ServiceConnector;
+
+final class RemoteSpeechRecognitionService extends ServiceConnector.Impl<IRecognitionService> {
+ private static final String TAG = RemoteSpeechRecognitionService.class.getSimpleName();
+ private static final boolean DEBUG = true;
+
+ RemoteSpeechRecognitionService(Context context, ComponentName serviceName, int userId) {
+ super(context,
+ new Intent(RecognitionService.SERVICE_INTERFACE).setComponent(serviceName),
+ Context.BIND_AUTO_CREATE
+ | Context.BIND_FOREGROUND_SERVICE
+ | Context.BIND_INCLUDE_CAPABILITIES
+ | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS,
+ userId,
+ IRecognitionService.Stub::asInterface);
+
+ if (DEBUG) {
+ Slog.i(TAG, "Bound to recognition service at: " + serviceName.flattenToString());
+ }
+ }
+
+ void startListening(Intent recognizerIntent, IRecognitionListener listener, String packageName,
+ String featureId) throws RemoteException {
+ if (DEBUG) {
+ Slog.i(TAG, "#startListening for package: " + packageName + ", feature=" + featureId);
+ }
+ run(service -> service.startListening(recognizerIntent, listener, packageName, featureId));
+ }
+
+ void stopListening(IRecognitionListener listener, String packageName, String featureId)
+ throws RemoteException {
+ if (DEBUG) {
+ Slog.i(TAG, "#stopListening for package: " + packageName + ", feature=" + featureId);
+ }
+ run(service -> service.stopListening(listener, packageName, featureId));
+ }
+
+ void cancel(IRecognitionListener listener, String packageName, String featureId)
+ throws RemoteException {
+ if (DEBUG) {
+ Slog.i(TAG, "#cancel for package: " + packageName + ", feature=" + featureId);
+ }
+ run(service -> service.cancel(listener, packageName, featureId));
+ }
+
+ @Override // from ServiceConnector.Impl
+ protected void onServiceConnectionStatusChanged(
+ IRecognitionService service, boolean connected) {
+ if (!DEBUG) {
+ return;
+ }
+
+ if (connected) {
+ Slog.i(TAG, "Connected to ASR service");
+ } else {
+ Slog.w(TAG, "Disconnected from ASR service");
+ }
+ }
+
+ @Override // from AbstractRemoteService
+ protected long getAutoDisconnectTimeoutMs() {
+ return PERMANENT_BOUND_TIMEOUT_MS;
+ }
+}
diff --git a/services/core/java/com/android/server/speech/SpeechRecognitionManagerService.java b/services/core/java/com/android/server/speech/SpeechRecognitionManagerService.java
new file mode 100644
index 0000000..592ba9e
--- /dev/null
+++ b/services/core/java/com/android/server/speech/SpeechRecognitionManagerService.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.speech;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.content.Context;
+import android.os.UserHandle;
+import android.speech.IRecognitionServiceManager;
+import android.speech.IRecognitionServiceManagerCallback;
+
+import com.android.internal.R;
+import com.android.server.infra.AbstractMasterSystemService;
+import com.android.server.infra.FrameworkResourcesServiceNameResolver;
+
+/**
+ * System service implementation for Speech Recognizer.
+ *
+ * <p>This service uses RemoteSpeechRecognitionService to bind to a default implementation of
+ * ISpeechRecognition. It relays all the requests from the client to the default system impl of
+ * ISpeechRecognition service (denoted by {@code
+ * R.string.config_defaultOnDeviceSpeechRecognitionService}).
+ */
+public final class SpeechRecognitionManagerService extends
+ AbstractMasterSystemService<SpeechRecognitionManagerService,
+ SpeechRecognitionManagerServiceImpl> {
+ private static final String TAG = SpeechRecognitionManagerService.class.getSimpleName();
+
+ public SpeechRecognitionManagerService(@NonNull Context context) {
+ super(context,
+ new FrameworkResourcesServiceNameResolver(
+ context,
+ R.string.config_defaultOnDeviceSpeechRecognitionService),
+ /*disallowProperty=*/ null);
+ }
+
+ @Override // from SystemService
+ public void onStart() {
+ SpeechRecognitionManagerServiceStub serviceStub = new SpeechRecognitionManagerServiceStub();
+ publishBinderService(Context.SPEECH_RECOGNITION_SERVICE, serviceStub);
+ }
+
+ @Override
+ protected SpeechRecognitionManagerServiceImpl newServiceLocked(
+ @UserIdInt int resolvedUserId, boolean disabled) {
+ return new SpeechRecognitionManagerServiceImpl(this, mLock, resolvedUserId, disabled);
+ }
+
+ final class SpeechRecognitionManagerServiceStub extends IRecognitionServiceManager.Stub {
+
+ @Override
+ public void createSession(IRecognitionServiceManagerCallback callback) {
+ int userId = UserHandle.getCallingUserId();
+ synchronized (mLock) {
+ SpeechRecognitionManagerServiceImpl service = getServiceForUserLocked(userId);
+ service.createSessionLocked(callback);
+ }
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java b/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java
new file mode 100644
index 0000000..bcaf174
--- /dev/null
+++ b/services/core/java/com/android/server/speech/SpeechRecognitionManagerServiceImpl.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.speech;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.AppGlobals;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
+import android.os.RemoteException;
+import android.speech.IRecognitionListener;
+import android.speech.IRecognitionService;
+import android.speech.IRecognitionServiceManagerCallback;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.infra.AbstractPerUserSystemService;
+
+final class SpeechRecognitionManagerServiceImpl extends
+ AbstractPerUserSystemService<SpeechRecognitionManagerServiceImpl,
+ SpeechRecognitionManagerService> {
+
+ private static final String TAG = SpeechRecognitionManagerServiceImpl.class.getSimpleName();
+
+ @GuardedBy("mLock")
+ @Nullable
+ private RemoteSpeechRecognitionService mRemoteService;
+
+ SpeechRecognitionManagerServiceImpl(
+ @NonNull SpeechRecognitionManagerService master,
+ @NonNull Object lock, @UserIdInt int userId, boolean disabled) {
+ super(master, lock, userId);
+ updateRemoteServiceLocked();
+ }
+
+ @GuardedBy("mLock")
+ @Override // from PerUserSystemService
+ protected ServiceInfo newServiceInfoLocked(@NonNull ComponentName serviceComponent)
+ throws PackageManager.NameNotFoundException {
+ try {
+ return AppGlobals.getPackageManager().getServiceInfo(serviceComponent,
+ PackageManager.GET_META_DATA, mUserId);
+ } catch (RemoteException e) {
+ throw new PackageManager.NameNotFoundException(
+ "Could not get service for " + serviceComponent);
+ }
+ }
+
+ @GuardedBy("mLock")
+ @Override // from PerUserSystemService
+ protected boolean updateLocked(boolean disabled) {
+ final boolean enabledChanged = super.updateLocked(disabled);
+ updateRemoteServiceLocked();
+ return enabledChanged;
+ }
+
+ /**
+ * Updates the reference to the remote service.
+ */
+ @GuardedBy("mLock")
+ private void updateRemoteServiceLocked() {
+ if (mRemoteService != null) {
+ if (mMaster.debug) {
+ Slog.d(TAG, "updateRemoteService(): destroying old remote service");
+ }
+ mRemoteService.unbind();
+ mRemoteService = null;
+ }
+ }
+
+ void createSessionLocked(IRecognitionServiceManagerCallback callback) {
+ // TODO(b/176578753): check clients have record audio permission.
+ // TODO(b/176578753): verify caller package is the one supplied
+
+ RemoteSpeechRecognitionService service = ensureRemoteServiceLocked();
+
+ if (service == null) {
+ tryRespondWithError(callback);
+ return;
+ }
+
+ service.connect().thenAccept(binderService -> {
+ if (binderService != null) {
+ try {
+ callback.onSuccess(new IRecognitionService.Stub() {
+ @Override
+ public void startListening(Intent recognizerIntent,
+ IRecognitionListener listener,
+ String packageName, String featureId) throws RemoteException {
+ service.startListening(
+ recognizerIntent, listener, packageName, featureId);
+ }
+
+ @Override
+ public void stopListening(IRecognitionListener listener,
+ String packageName,
+ String featureId) throws RemoteException {
+ service.stopListening(listener, packageName, featureId);
+ }
+
+ @Override
+ public void cancel(IRecognitionListener listener,
+ String packageName,
+ String featureId) throws RemoteException {
+ service.cancel(listener, packageName, featureId);
+ }
+ });
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error creating a speech recognition session", e);
+ tryRespondWithError(callback);
+ }
+ } else {
+ tryRespondWithError(callback);
+ }
+ });
+ }
+
+ @GuardedBy("mLock")
+ @Nullable
+ private RemoteSpeechRecognitionService ensureRemoteServiceLocked() {
+ if (mRemoteService == null) {
+ final String serviceName = getComponentNameLocked();
+ if (serviceName == null) {
+ if (mMaster.verbose) {
+ Slog.v(TAG, "ensureRemoteServiceLocked(): no service component name.");
+ }
+ return null;
+ }
+ final ComponentName serviceComponent = ComponentName.unflattenFromString(serviceName);
+ mRemoteService =
+ new RemoteSpeechRecognitionService(getContext(), serviceComponent, mUserId);
+ }
+ return mRemoteService;
+ }
+
+ private static void tryRespondWithError(IRecognitionServiceManagerCallback callback) {
+ try {
+ callback.onError();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to respond with error");
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/location/timezone/BinderLocationTimeZoneProvider.java b/services/core/java/com/android/server/timezonedetector/location/BinderLocationTimeZoneProvider.java
similarity index 89%
rename from services/core/java/com/android/server/location/timezone/BinderLocationTimeZoneProvider.java
rename to services/core/java/com/android/server/timezonedetector/location/BinderLocationTimeZoneProvider.java
index 210fb5c..c0c9e6d 100644
--- a/services/core/java/com/android/server/location/timezone/BinderLocationTimeZoneProvider.java
+++ b/services/core/java/com/android/server/timezonedetector/location/BinderLocationTimeZoneProvider.java
@@ -14,15 +14,15 @@
* limitations under the License.
*/
-package com.android.server.location.timezone;
+package com.android.server.timezonedetector.location;
-import static com.android.server.location.timezone.LocationTimeZoneManagerService.debugLog;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_UNCERTAIN;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STOPPED;
+import static com.android.server.timezonedetector.location.LocationTimeZoneManagerService.debugLog;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_UNCERTAIN;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STOPPED;
import android.annotation.NonNull;
import android.annotation.Nullable;
diff --git a/services/core/java/com/android/server/location/timezone/ControllerCallbackImpl.java b/services/core/java/com/android/server/timezonedetector/location/ControllerCallbackImpl.java
similarity index 96%
rename from services/core/java/com/android/server/location/timezone/ControllerCallbackImpl.java
rename to services/core/java/com/android/server/timezonedetector/location/ControllerCallbackImpl.java
index cd9aa2f..46eaad0 100644
--- a/services/core/java/com/android/server/location/timezone/ControllerCallbackImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/location/ControllerCallbackImpl.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.location.timezone;
+package com.android.server.timezonedetector.location;
import android.annotation.NonNull;
diff --git a/services/core/java/com/android/server/location/timezone/ControllerEnvironmentImpl.java b/services/core/java/com/android/server/timezonedetector/location/ControllerEnvironmentImpl.java
similarity index 97%
rename from services/core/java/com/android/server/location/timezone/ControllerEnvironmentImpl.java
rename to services/core/java/com/android/server/timezonedetector/location/ControllerEnvironmentImpl.java
index d896f6e..83b33ee 100644
--- a/services/core/java/com/android/server/location/timezone/ControllerEnvironmentImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/location/ControllerEnvironmentImpl.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.location.timezone;
+package com.android.server.timezonedetector.location;
import android.annotation.NonNull;
diff --git a/services/core/java/com/android/server/location/timezone/ControllerImpl.java b/services/core/java/com/android/server/timezonedetector/location/ControllerImpl.java
similarity index 94%
rename from services/core/java/com/android/server/location/timezone/ControllerImpl.java
rename to services/core/java/com/android/server/timezonedetector/location/ControllerImpl.java
index 0d284fc..fb2a184 100644
--- a/services/core/java/com/android/server/location/timezone/ControllerImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/location/ControllerImpl.java
@@ -14,20 +14,20 @@
* limitations under the License.
*/
-package com.android.server.location.timezone;
+package com.android.server.timezonedetector.location;
-import static com.android.server.location.timezone.LocationTimeZoneManagerService.debugLog;
-import static com.android.server.location.timezone.LocationTimeZoneManagerService.warnLog;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_UNCERTAIN;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STOPPED;
-import static com.android.server.location.timezone.TimeZoneProviderEvent.EVENT_TYPE_PERMANENT_FAILURE;
-import static com.android.server.location.timezone.TimeZoneProviderEvent.EVENT_TYPE_SUGGESTION;
-import static com.android.server.location.timezone.TimeZoneProviderEvent.EVENT_TYPE_UNCERTAIN;
+import static com.android.server.timezonedetector.location.LocationTimeZoneManagerService.debugLog;
+import static com.android.server.timezonedetector.location.LocationTimeZoneManagerService.warnLog;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_UNCERTAIN;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STOPPED;
+import static com.android.server.timezonedetector.location.TimeZoneProviderEvent.EVENT_TYPE_PERMANENT_FAILURE;
+import static com.android.server.timezonedetector.location.TimeZoneProviderEvent.EVENT_TYPE_SUGGESTION;
+import static com.android.server.timezonedetector.location.TimeZoneProviderEvent.EVENT_TYPE_UNCERTAIN;
import android.annotation.DurationMillisLong;
import android.annotation.NonNull;
@@ -36,9 +36,9 @@
import android.util.IndentingPrintWriter;
import com.android.internal.annotations.GuardedBy;
-import com.android.server.location.timezone.ThreadingDomain.SingleRunnableQueue;
import com.android.server.timezonedetector.ConfigurationInternal;
import com.android.server.timezonedetector.GeolocationTimeZoneSuggestion;
+import com.android.server.timezonedetector.location.ThreadingDomain.SingleRunnableQueue;
import java.time.Duration;
import java.util.List;
diff --git a/services/core/java/com/android/server/location/timezone/HandlerThreadingDomain.java b/services/core/java/com/android/server/timezonedetector/location/HandlerThreadingDomain.java
similarity index 98%
rename from services/core/java/com/android/server/location/timezone/HandlerThreadingDomain.java
rename to services/core/java/com/android/server/timezonedetector/location/HandlerThreadingDomain.java
index 3055ff8..0dd2922 100644
--- a/services/core/java/com/android/server/location/timezone/HandlerThreadingDomain.java
+++ b/services/core/java/com/android/server/timezonedetector/location/HandlerThreadingDomain.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.location.timezone;
+package com.android.server.timezonedetector.location;
import android.annotation.DurationMillisLong;
import android.annotation.NonNull;
diff --git a/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
similarity index 99%
rename from services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java
rename to services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
index 54535eb..5bee7ee 100644
--- a/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java
+++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerService.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.location.timezone;
+package com.android.server.timezonedetector.location;
import static android.app.time.LocationTimeZoneManager.PRIMARY_PROVIDER_NAME;
import static android.app.time.LocationTimeZoneManager.PROVIDER_MODE_OVERRIDE_DISABLED;
diff --git a/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerServiceState.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerServiceState.java
similarity index 95%
rename from services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerServiceState.java
rename to services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerServiceState.java
index b1dd55f..113926a 100644
--- a/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerServiceState.java
+++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerServiceState.java
@@ -14,13 +14,13 @@
* limitations under the License.
*/
-package com.android.server.location.timezone;
+package com.android.server.timezonedetector.location;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState;
import com.android.server.timezonedetector.GeolocationTimeZoneSuggestion;
+import com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState;
import java.util.ArrayList;
import java.util.Collections;
diff --git a/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerShellCommand.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerShellCommand.java
similarity index 92%
rename from services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerShellCommand.java
rename to services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerShellCommand.java
index 6f9863c..b53150c 100644
--- a/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerShellCommand.java
+++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneManagerShellCommand.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.server.location.timezone;
+package com.android.server.timezonedetector.location;
import static android.app.time.LocationTimeZoneManager.DUMP_STATE_OPTION_PROTO;
import static android.app.time.LocationTimeZoneManager.PRIMARY_PROVIDER_NAME;
@@ -28,13 +28,13 @@
import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_START;
import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_STOP;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_UNCERTAIN;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STOPPED;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_UNKNOWN;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_UNCERTAIN;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STOPPED;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_UNKNOWN;
import android.annotation.NonNull;
import android.app.time.GeolocationTimeZoneSuggestionProto;
@@ -47,8 +47,8 @@
import android.util.proto.ProtoOutputStream;
import com.android.internal.util.dump.DualDumpOutputStream;
-import com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.ProviderStateEnum;
import com.android.server.timezonedetector.GeolocationTimeZoneSuggestion;
+import com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.ProviderStateEnum;
import java.io.FileDescriptor;
import java.io.PrintWriter;
diff --git a/services/core/java/com/android/server/location/timezone/LocationTimeZoneProvider.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProvider.java
similarity index 94%
rename from services/core/java/com/android/server/location/timezone/LocationTimeZoneProvider.java
rename to services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProvider.java
index 9a7b775..ef2f357 100644
--- a/services/core/java/com/android/server/location/timezone/LocationTimeZoneProvider.java
+++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProvider.java
@@ -14,22 +14,22 @@
* limitations under the License.
*/
-package com.android.server.location.timezone;
+package com.android.server.timezonedetector.location;
import static android.service.timezone.TimeZoneProviderService.TEST_COMMAND_RESULT_ERROR_KEY;
import static android.service.timezone.TimeZoneProviderService.TEST_COMMAND_RESULT_SUCCESS_KEY;
-import static com.android.server.location.timezone.LocationTimeZoneManagerService.debugLog;
-import static com.android.server.location.timezone.LocationTimeZoneManagerService.warnLog;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_UNCERTAIN;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STOPPED;
-import static com.android.server.location.timezone.TimeZoneProviderEvent.EVENT_TYPE_PERMANENT_FAILURE;
-import static com.android.server.location.timezone.TimeZoneProviderEvent.EVENT_TYPE_SUGGESTION;
-import static com.android.server.location.timezone.TimeZoneProviderEvent.EVENT_TYPE_UNCERTAIN;
+import static com.android.server.timezonedetector.location.LocationTimeZoneManagerService.debugLog;
+import static com.android.server.timezonedetector.location.LocationTimeZoneManagerService.warnLog;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_UNCERTAIN;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STOPPED;
+import static com.android.server.timezonedetector.location.TimeZoneProviderEvent.EVENT_TYPE_PERMANENT_FAILURE;
+import static com.android.server.timezonedetector.location.TimeZoneProviderEvent.EVENT_TYPE_SUGGESTION;
+import static com.android.server.timezonedetector.location.TimeZoneProviderEvent.EVENT_TYPE_UNCERTAIN;
import android.annotation.ElapsedRealtimeLong;
import android.annotation.IntDef;
@@ -42,11 +42,11 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.ProviderStateEnum;
-import com.android.server.location.timezone.ThreadingDomain.SingleRunnableQueue;
import com.android.server.timezonedetector.ConfigurationInternal;
import com.android.server.timezonedetector.Dumpable;
import com.android.server.timezonedetector.ReferenceWithHistory;
+import com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.ProviderStateEnum;
+import com.android.server.timezonedetector.location.ThreadingDomain.SingleRunnableQueue;
import java.time.Duration;
import java.util.ArrayList;
diff --git a/services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderController.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProviderController.java
similarity index 97%
rename from services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderController.java
rename to services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProviderController.java
index ec2bc13..b4aff3e 100644
--- a/services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderController.java
+++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProviderController.java
@@ -14,17 +14,17 @@
* limitations under the License.
*/
-package com.android.server.location.timezone;
+package com.android.server.timezonedetector.location;
import android.annotation.DurationMillisLong;
import android.annotation.NonNull;
import android.os.Handler;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState;
import com.android.server.timezonedetector.ConfigurationInternal;
import com.android.server.timezonedetector.Dumpable;
import com.android.server.timezonedetector.GeolocationTimeZoneSuggestion;
+import com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState;
import java.time.Duration;
import java.util.Objects;
diff --git a/services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderProxy.java b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProviderProxy.java
similarity index 98%
rename from services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderProxy.java
rename to services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProviderProxy.java
index 8368b5e..43b1b5f0 100644
--- a/services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderProxy.java
+++ b/services/core/java/com/android/server/timezonedetector/location/LocationTimeZoneProviderProxy.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.location.timezone;
+package com.android.server.timezonedetector.location;
import android.annotation.NonNull;
import android.annotation.Nullable;
diff --git a/services/core/java/com/android/server/location/timezone/NullLocationTimeZoneProviderProxy.java b/services/core/java/com/android/server/timezonedetector/location/NullLocationTimeZoneProviderProxy.java
similarity index 98%
rename from services/core/java/com/android/server/location/timezone/NullLocationTimeZoneProviderProxy.java
rename to services/core/java/com/android/server/timezonedetector/location/NullLocationTimeZoneProviderProxy.java
index c2abbf9..1f45e82 100644
--- a/services/core/java/com/android/server/location/timezone/NullLocationTimeZoneProviderProxy.java
+++ b/services/core/java/com/android/server/timezonedetector/location/NullLocationTimeZoneProviderProxy.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.location.timezone;
+package com.android.server.timezonedetector.location;
import android.annotation.NonNull;
import android.annotation.Nullable;
diff --git a/services/core/java/com/android/server/location/timezone/RealLocationTimeZoneProviderProxy.java b/services/core/java/com/android/server/timezonedetector/location/RealLocationTimeZoneProviderProxy.java
similarity index 98%
rename from services/core/java/com/android/server/location/timezone/RealLocationTimeZoneProviderProxy.java
rename to services/core/java/com/android/server/timezonedetector/location/RealLocationTimeZoneProviderProxy.java
index 0904ba4..38211ef 100644
--- a/services/core/java/com/android/server/location/timezone/RealLocationTimeZoneProviderProxy.java
+++ b/services/core/java/com/android/server/timezonedetector/location/RealLocationTimeZoneProviderProxy.java
@@ -14,13 +14,13 @@
* limitations under the License.
*/
-package com.android.server.location.timezone;
+package com.android.server.timezonedetector.location;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.service.timezone.TimeZoneProviderService.TEST_COMMAND_RESULT_ERROR_KEY;
import static android.service.timezone.TimeZoneProviderService.TEST_COMMAND_RESULT_SUCCESS_KEY;
-import static com.android.server.location.timezone.LocationTimeZoneManagerService.warnLog;
+import static com.android.server.timezonedetector.location.LocationTimeZoneManagerService.warnLog;
import android.Manifest;
import android.annotation.NonNull;
diff --git a/services/core/java/com/android/server/location/timezone/SimulatedLocationTimeZoneProviderProxy.java b/services/core/java/com/android/server/timezonedetector/location/SimulatedLocationTimeZoneProviderProxy.java
similarity index 99%
rename from services/core/java/com/android/server/location/timezone/SimulatedLocationTimeZoneProviderProxy.java
rename to services/core/java/com/android/server/timezonedetector/location/SimulatedLocationTimeZoneProviderProxy.java
index 66ccaed..02b0a84 100644
--- a/services/core/java/com/android/server/location/timezone/SimulatedLocationTimeZoneProviderProxy.java
+++ b/services/core/java/com/android/server/timezonedetector/location/SimulatedLocationTimeZoneProviderProxy.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.location.timezone;
+package com.android.server.timezonedetector.location;
import static android.app.time.LocationTimeZoneManager.SIMULATED_PROVIDER_TEST_COMMAND_ON_BIND;
import static android.app.time.LocationTimeZoneManager.SIMULATED_PROVIDER_TEST_COMMAND_ON_UNBIND;
diff --git a/services/core/java/com/android/server/location/timezone/TestCommand.java b/services/core/java/com/android/server/timezonedetector/location/TestCommand.java
similarity index 98%
rename from services/core/java/com/android/server/location/timezone/TestCommand.java
rename to services/core/java/com/android/server/timezonedetector/location/TestCommand.java
index 0df3ca0..21482ea 100644
--- a/services/core/java/com/android/server/location/timezone/TestCommand.java
+++ b/services/core/java/com/android/server/timezonedetector/location/TestCommand.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.location.timezone;
+package com.android.server.timezonedetector.location;
import android.annotation.NonNull;
import android.net.Uri;
diff --git a/services/core/java/com/android/server/location/timezone/ThreadingDomain.java b/services/core/java/com/android/server/timezonedetector/location/ThreadingDomain.java
similarity index 98%
rename from services/core/java/com/android/server/location/timezone/ThreadingDomain.java
rename to services/core/java/com/android/server/timezonedetector/location/ThreadingDomain.java
index 4ada6f5..9e3497f 100644
--- a/services/core/java/com/android/server/location/timezone/ThreadingDomain.java
+++ b/services/core/java/com/android/server/timezonedetector/location/ThreadingDomain.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.location.timezone;
+package com.android.server.timezonedetector.location;
import android.annotation.DurationMillisLong;
import android.annotation.NonNull;
diff --git a/services/core/java/com/android/server/location/timezone/TimeZoneProviderEvent.java b/services/core/java/com/android/server/timezonedetector/location/TimeZoneProviderEvent.java
similarity index 98%
rename from services/core/java/com/android/server/location/timezone/TimeZoneProviderEvent.java
rename to services/core/java/com/android/server/timezonedetector/location/TimeZoneProviderEvent.java
index 2d6f8ad..3e224e0 100644
--- a/services/core/java/com/android/server/location/timezone/TimeZoneProviderEvent.java
+++ b/services/core/java/com/android/server/timezonedetector/location/TimeZoneProviderEvent.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.location.timezone;
+package com.android.server.timezonedetector.location;
import android.annotation.IntDef;
import android.annotation.NonNull;
diff --git a/services/core/java/com/android/server/location/timezone/TimeZoneProviderRequest.java b/services/core/java/com/android/server/timezonedetector/location/TimeZoneProviderRequest.java
similarity index 98%
rename from services/core/java/com/android/server/location/timezone/TimeZoneProviderRequest.java
rename to services/core/java/com/android/server/timezonedetector/location/TimeZoneProviderRequest.java
index 649a74b..1482031 100644
--- a/services/core/java/com/android/server/location/timezone/TimeZoneProviderRequest.java
+++ b/services/core/java/com/android/server/timezonedetector/location/TimeZoneProviderRequest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.location.timezone;
+package com.android.server.timezonedetector.location;
import android.annotation.NonNull;
import android.annotation.Nullable;
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 0fa97a2..703bfab 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -122,7 +122,9 @@
private static final int TOKEN_ALL = Integer.MIN_VALUE;
private static final int NETWORK_LOSS_DISCONNECT_TIMEOUT_SECONDS = 30;
- private static final int TEARDOWN_TIMEOUT_SECONDS = 5;
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ static final int TEARDOWN_TIMEOUT_SECONDS = 5;
private interface EventInfo {}
@@ -413,13 +415,6 @@
private int mCurrentToken = -1;
/**
- * The next usable token.
- *
- * <p>A new token MUST be used for all new IKE sessions.
- */
- private int mNextToken = 0;
-
- /**
* The number of unsuccessful attempts since the last successful connection.
*
* <p>This number MUST be incremented each time the RetryTimeout state is entered, and cleared
@@ -440,7 +435,7 @@
* <p>Set in Connecting or Migrating States, always @NonNull in Connecting, Connected, and
* Migrating states, null otherwise.
*/
- private IkeSession mIkeSession;
+ private VcnIkeSession mIkeSession;
/**
* The last known child configuration.
@@ -643,6 +638,22 @@
protected abstract void processStateMsg(Message msg) throws Exception;
+ @Override
+ public void exit() {
+ try {
+ exitState();
+ } catch (Exception e) {
+ Slog.wtf(TAG, "Uncaught exception", e);
+ sendMessage(
+ EVENT_DISCONNECT_REQUESTED,
+ TOKEN_ALL,
+ new EventDisconnectRequestedInfo(
+ DISCONNECT_REASON_INTERNAL_ERROR + e.toString()));
+ }
+ }
+
+ protected void exitState() throws Exception {}
+
protected void logUnhandledMessage(Message msg) {
// Log as unexpected all known messages, and log all else as unknown.
switch (msg.what) {
@@ -669,18 +680,11 @@
}
}
- protected void teardownIke() {
- if (mIkeSession != null) {
- mIkeSession.close();
- }
- }
-
protected void handleDisconnectRequested(String msg) {
Slog.v(TAG, "Tearing down. Cause: " + msg);
mIsRunning = false;
teardownNetwork();
- teardownIke();
if (mIkeSession == null) {
// Already disconnected, go straight to DisconnectedState
@@ -773,8 +777,91 @@
* does not complete teardown in a timely fashion, it will be killed (forcibly closed).
*/
private class DisconnectingState extends ActiveBaseState {
+ /**
+ * Whether to skip the RetryTimeoutState and go straight to the ConnectingState.
+ *
+ * <p>This is used when an underlying network change triggered a restart on a new network.
+ *
+ * <p>Reset (to false) upon exit of the DisconnectingState.
+ */
+ private boolean mSkipRetryTimeout = false;
+
+ // TODO(b/178441390): Remove this in favor of resetting retry timers on UND_NET change.
+ public void setSkipRetryTimeout(boolean shouldSkip) {
+ mSkipRetryTimeout = shouldSkip;
+ }
+
@Override
- protected void processStateMsg(Message msg) {}
+ protected void enterState() throws Exception {
+ if (mIkeSession == null) {
+ Slog.wtf(TAG, "IKE session was already closed when entering Disconnecting state.");
+ sendMessage(EVENT_SESSION_CLOSED, mCurrentToken);
+ return;
+ }
+
+ // If underlying network has already been lost, save some time and just kill the session
+ if (mUnderlying == null) {
+ // Will trigger a EVENT_SESSION_CLOSED as IkeSession shuts down.
+ mIkeSession.kill();
+ return;
+ }
+
+ mIkeSession.close();
+ sendMessageDelayed(
+ EVENT_TEARDOWN_TIMEOUT_EXPIRED,
+ mCurrentToken,
+ TimeUnit.SECONDS.toMillis(TEARDOWN_TIMEOUT_SECONDS));
+ }
+
+ @Override
+ protected void processStateMsg(Message msg) {
+ switch (msg.what) {
+ case EVENT_UNDERLYING_NETWORK_CHANGED: // Fallthrough
+ mUnderlying = ((EventUnderlyingNetworkChangedInfo) msg.obj).newUnderlying;
+
+ // If we received a new underlying network, continue.
+ if (mUnderlying != null) {
+ break;
+ }
+
+ // Fallthrough; no network exists to send IKE close session requests.
+ case EVENT_TEARDOWN_TIMEOUT_EXPIRED:
+ // Grace period ended. Kill session, triggering EVENT_SESSION_CLOSED
+ mIkeSession.kill();
+
+ break;
+ case EVENT_DISCONNECT_REQUESTED:
+ teardownNetwork();
+
+ String reason = ((EventDisconnectRequestedInfo) msg.obj).reason;
+ if (reason.equals(DISCONNECT_REASON_UNDERLYING_NETWORK_LOST)) {
+ // Will trigger EVENT_SESSION_CLOSED immediately.
+ mIkeSession.kill();
+ break;
+ }
+
+ // Otherwise we are already in the process of shutting down.
+ break;
+ case EVENT_SESSION_CLOSED:
+ mIkeSession = null;
+
+ if (mIsRunning && mUnderlying != null) {
+ transitionTo(mSkipRetryTimeout ? mConnectingState : mRetryTimeoutState);
+ } else {
+ teardownNetwork();
+ transitionTo(mDisconnectedState);
+ }
+ break;
+ default:
+ logUnhandledMessage(msg);
+ break;
+ }
+ }
+
+ @Override
+ protected void exitState() throws Exception {
+ mSkipRetryTimeout = false;
+ }
}
/**
@@ -785,7 +872,69 @@
*/
private class ConnectingState extends ActiveBaseState {
@Override
- protected void processStateMsg(Message msg) {}
+ protected void enterState() {
+ if (mIkeSession != null) {
+ Slog.wtf(TAG, "ConnectingState entered with active session");
+
+ // Attempt to recover.
+ mIkeSession.kill();
+ mIkeSession = null;
+ }
+
+ mIkeSession = buildIkeSession();
+ }
+
+ @Override
+ protected void processStateMsg(Message msg) {
+ switch (msg.what) {
+ case EVENT_UNDERLYING_NETWORK_CHANGED:
+ final UnderlyingNetworkRecord oldUnderlying = mUnderlying;
+ mUnderlying = ((EventUnderlyingNetworkChangedInfo) msg.obj).newUnderlying;
+
+ if (oldUnderlying == null) {
+ // This should never happen, but if it does, there's likely a nasty bug.
+ Slog.wtf(TAG, "Old underlying network was null in connected state. Bug?");
+ }
+
+ // If new underlying is null, all underlying networks have been lost; disconnect
+ if (mUnderlying == null) {
+ transitionTo(mDisconnectingState);
+ break;
+ }
+
+ if (oldUnderlying != null
+ && mUnderlying.network.equals(oldUnderlying.network)) {
+ break; // Only network properties have changed; continue connecting.
+ }
+ // Else, retry on the new network.
+
+ // Immediately come back to the ConnectingState (skip RetryTimeout, since this
+ // isn't a failure)
+ mDisconnectingState.setSkipRetryTimeout(true);
+
+ // fallthrough - disconnect, and retry on new network.
+ case EVENT_SESSION_LOST:
+ transitionTo(mDisconnectingState);
+ break;
+ case EVENT_SESSION_CLOSED:
+ deferMessage(msg);
+
+ transitionTo(mDisconnectingState);
+ break;
+ case EVENT_SETUP_COMPLETED: // fallthrough
+ case EVENT_TRANSFORM_CREATED:
+ // Child setup complete; move to ConnectedState for NetworkAgent registration
+ deferMessage(msg);
+ transitionTo(mConnectedState);
+ break;
+ case EVENT_DISCONNECT_REQUESTED:
+ handleDisconnectRequested(((EventDisconnectRequestedInfo) msg.obj).reason);
+ break;
+ default:
+ logUnhandledMessage(msg);
+ break;
+ }
+ }
}
private abstract class ConnectedStateBase extends ActiveBaseState {}
@@ -946,6 +1095,38 @@
mIsRunning = isRunning;
}
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ VcnIkeSession getIkeSession() {
+ return mIkeSession;
+ }
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ void setIkeSession(@Nullable VcnIkeSession session) {
+ mIkeSession = session;
+ }
+
+ private IkeSessionParams buildIkeParams() {
+ // TODO: Implement this once IkeSessionParams is persisted
+ return null;
+ }
+
+ private ChildSessionParams buildChildParams() {
+ // TODO: Implement this once IkeSessionParams is persisted
+ return null;
+ }
+
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ VcnIkeSession buildIkeSession() {
+ final int token = ++mCurrentToken;
+
+ return mDeps.newIkeSession(
+ mVcnContext,
+ buildIkeParams(),
+ buildChildParams(),
+ new IkeSessionCallbackImpl(token),
+ new ChildSessionCallbackImpl(token));
+ }
+
/** External dependencies used by VcnGatewayConnection, for injection in tests */
@VisibleForTesting(visibility = Visibility.PRIVATE)
public static class Dependencies {
@@ -958,19 +1139,67 @@
}
/** Builds a new IkeSession. */
- public IkeSession newIkeSession(
+ public VcnIkeSession newIkeSession(
VcnContext vcnContext,
IkeSessionParams ikeSessionParams,
ChildSessionParams childSessionParams,
IkeSessionCallback ikeSessionCallback,
ChildSessionCallback childSessionCallback) {
- return new IkeSession(
- vcnContext.getContext(),
+ return new VcnIkeSession(
+ vcnContext,
ikeSessionParams,
childSessionParams,
- new HandlerExecutor(new Handler(vcnContext.getLooper())),
ikeSessionCallback,
childSessionCallback);
}
}
+
+ /** Proxy implementation of IKE session, used for testing. */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ public static class VcnIkeSession {
+ private final IkeSession mImpl;
+
+ public VcnIkeSession(
+ VcnContext vcnContext,
+ IkeSessionParams ikeSessionParams,
+ ChildSessionParams childSessionParams,
+ IkeSessionCallback ikeSessionCallback,
+ ChildSessionCallback childSessionCallback) {
+ mImpl =
+ new IkeSession(
+ vcnContext.getContext(),
+ ikeSessionParams,
+ childSessionParams,
+ new HandlerExecutor(new Handler(vcnContext.getLooper())),
+ ikeSessionCallback,
+ childSessionCallback);
+ }
+
+ /** Creates a new IKE Child session. */
+ public void openChildSession(
+ @NonNull ChildSessionParams childSessionParams,
+ @NonNull ChildSessionCallback childSessionCallback) {
+ mImpl.openChildSession(childSessionParams, childSessionCallback);
+ }
+
+ /** Closes an IKE session as identified by the ChildSessionCallback. */
+ public void closeChildSession(@NonNull ChildSessionCallback childSessionCallback) {
+ mImpl.closeChildSession(childSessionCallback);
+ }
+
+ /** Gracefully closes this IKE Session, waiting for remote acknowledgement. */
+ public void close() {
+ mImpl.close();
+ }
+
+ /** Forcibly kills this IKE Session, without waiting for a closure confirmation. */
+ public void kill() {
+ mImpl.kill();
+ }
+
+ /** Sets the underlying network used by the IkeSession. */
+ public void setNetwork(@NonNull Network network) {
+ mImpl.setNetwork(network);
+ }
+ }
}
diff --git a/services/core/java/com/android/server/vibrator/VibrationThread.java b/services/core/java/com/android/server/vibrator/VibrationThread.java
index c36375e..5355252 100644
--- a/services/core/java/com/android/server/vibrator/VibrationThread.java
+++ b/services/core/java/com/android/server/vibrator/VibrationThread.java
@@ -74,6 +74,7 @@
void onVibrationEnded(long vibrationId, Vibration.Status status);
}
+ private final Object mLock = new Object();
private final WorkSource mWorkSource = new WorkSource();
private final PowerManager.WakeLock mWakeLock;
private final IBatteryStats mBatteryStatsService;
@@ -81,10 +82,10 @@
private final VibrationCallbacks mCallbacks;
private final SparseArray<VibratorController> mVibrators;
- @GuardedBy("this")
+ @GuardedBy("mLock")
@Nullable
private VibrateStep mCurrentVibrateStep;
- @GuardedBy("this")
+ @GuardedBy("mLock")
private boolean mForceStop;
// TODO(b/159207608): Remove this constructor once VibratorService is removed
@@ -113,6 +114,10 @@
}
}
+ public Vibration getVibration() {
+ return mVibration;
+ }
+
@Override
public void binderDied() {
cancel();
@@ -136,15 +141,15 @@
/** Cancel current vibration and shuts down the thread gracefully. */
public void cancel() {
- synchronized (this) {
+ synchronized (mLock) {
mForceStop = true;
- notify();
+ mLock.notify();
}
}
/** Notify current vibration that a step has completed on given vibrator. */
public void vibratorComplete(int vibratorId) {
- synchronized (this) {
+ synchronized (mLock) {
if (mCurrentVibrateStep != null) {
mCurrentVibrateStep.vibratorComplete(vibratorId);
}
@@ -168,7 +173,7 @@
final int stepCount = steps.size();
for (int i = 0; i < stepCount; i++) {
Step step = steps.get(i);
- synchronized (this) {
+ synchronized (mLock) {
if (step instanceof VibrateStep) {
mCurrentVibrateStep = (VibrateStep) step;
} else {
@@ -295,21 +300,48 @@
* Sleeps until given {@code wakeUpTime}.
*
* <p>This stops immediately when {@link #cancel()} is called.
+ *
+ * @return true if waited until wake-up time, false if it was cancelled.
*/
- private void waitUntil(long wakeUpTime) {
- synchronized (this) {
+ private boolean waitUntil(long wakeUpTime) {
+ synchronized (mLock) {
long durationRemaining = wakeUpTime - SystemClock.uptimeMillis();
while (durationRemaining > 0) {
try {
- VibrationThread.this.wait(durationRemaining);
+ mLock.wait(durationRemaining);
} catch (InterruptedException e) {
}
if (mForceStop) {
- break;
+ return false;
}
durationRemaining = wakeUpTime - SystemClock.uptimeMillis();
}
}
+ return true;
+ }
+
+ /**
+ * Sleeps until given {@link VibrateStep#isVibrationComplete()}, or until {@code wakeUpTime}.
+ *
+ * <p>This stops immediately when {@link #cancel()} is called.
+ *
+ * @return true if finished on vibration complete, false if it was cancelled or timed out.
+ */
+ private boolean waitForVibrationComplete(VibrateStep step, long wakeUpTime) {
+ synchronized (mLock) {
+ long durationRemaining = wakeUpTime - SystemClock.uptimeMillis();
+ while (!step.isVibrationComplete() && durationRemaining > 0) {
+ try {
+ mLock.wait(durationRemaining);
+ } catch (InterruptedException e) {
+ }
+ if (mForceStop) {
+ return false;
+ }
+ durationRemaining = wakeUpTime - SystemClock.uptimeMillis();
+ }
+ }
+ return step.isVibrationComplete();
}
private void noteVibratorOn(long duration) {
@@ -341,6 +373,9 @@
private interface VibrateStep extends Step {
/** Callback to notify a vibrator has finished playing a effect. */
void vibratorComplete(int vibratorId);
+
+ /** Returns true if the vibration played by this step is complete. */
+ boolean isVibrationComplete();
}
/** Represent a vibration on a single vibrator. */
@@ -348,11 +383,20 @@
private final VibratorController mVibrator;
private final VibrationEffect mEffect;
+ @GuardedBy("mLock")
+ private boolean mVibrationComplete;
+
SingleVibrateStep(VibratorController vibrator, VibrationEffect effect) {
mVibrator = vibrator;
mEffect = effect;
}
+ @GuardedBy("mLock")
+ @Override
+ public boolean isVibrationComplete() {
+ return mVibrationComplete;
+ }
+
@Override
public void vibratorComplete(int vibratorId) {
if (mVibrator.getVibratorInfo().getId() != vibratorId) {
@@ -364,8 +408,9 @@
return;
}
mVibrator.off();
- synchronized (VibrationThread.this) {
- VibrationThread.this.notify();
+ synchronized (mLock) {
+ mVibrationComplete = true;
+ mLock.notify();
}
}
@@ -384,12 +429,13 @@
noteVibratorOn(duration);
// Vibration is playing with no need to control amplitudes, just wait for native
// callback or timeout.
- waitUntil(startTime + duration + CALLBACKS_EXTRA_TIMEOUT);
- if (mForceStop) {
- mVibrator.off();
- return Vibration.Status.CANCELLED;
+ if (waitForVibrationComplete(this,
+ startTime + duration + CALLBACKS_EXTRA_TIMEOUT)) {
+ return Vibration.Status.FINISHED;
}
- return Vibration.Status.FINISHED;
+ // Timed out or vibration cancelled. Stop vibrator anyway.
+ mVibrator.off();
+ return mForceStop ? Vibration.Status.CANCELLED : Vibration.Status.FINISHED;
}
startTime = SystemClock.uptimeMillis();
@@ -407,8 +453,7 @@
noteVibratorOn(duration);
}
while (amplitudeStep != null) {
- waitUntil(amplitudeStep.startTime);
- if (mForceStop) {
+ if (!waitUntil(amplitudeStep.startTime)) {
mVibrator.off();
return Vibration.Status.CANCELLED;
}
@@ -482,7 +527,7 @@
private final int mRequiredCapabilities;
private final int[] mVibratorIds;
- @GuardedBy("VibrationThread.this")
+ @GuardedBy("mLock")
private int mActiveVibratorCounter;
SyncedVibrateStep(SparseArray<VibrationEffect> effects) {
@@ -496,6 +541,12 @@
}
}
+ @GuardedBy("mLock")
+ @Override
+ public boolean isVibrationComplete() {
+ return mActiveVibratorCounter <= 0;
+ }
+
@Override
public void vibratorComplete(int vibratorId) {
VibrationEffect effect = mEffects.get(vibratorId);
@@ -508,10 +559,9 @@
return;
}
mVibrators.get(vibratorId).off();
- synchronized (VibrationThread.this) {
- if (--mActiveVibratorCounter <= 0) {
- VibrationThread.this.notify();
- }
+ synchronized (mLock) {
+ --mActiveVibratorCounter;
+ mLock.notify();
}
}
@@ -532,8 +582,7 @@
while (!nextSteps.isEmpty()) {
AmplitudeStep step = nextSteps.poll();
- waitUntil(step.startTime);
- if (mForceStop) {
+ if (!waitUntil(step.startTime)) {
stopAllVibrators();
return Vibration.Status.CANCELLED;
}
@@ -541,7 +590,7 @@
AmplitudeStep nextStep = step.nextStep();
if (nextStep == null) {
// This vibrator has finished playing the effect for this step.
- synchronized (VibrationThread.this) {
+ synchronized (mLock) {
mActiveVibratorCounter--;
}
} else {
@@ -549,19 +598,18 @@
}
}
- // All OneShot and Waveform effects have finished. Just wait for the other effects
- // to end via native callbacks before finishing this synced step.
- synchronized (VibrationThread.this) {
- if (mActiveVibratorCounter > 0) {
- waitUntil(startTime + timeout + CALLBACKS_EXTRA_TIMEOUT);
+ synchronized (mLock) {
+ // All OneShot and Waveform effects have finished. Just wait for the other
+ // effects to end via native callbacks before finishing this synced step.
+ final long wakeUpTime = startTime + timeout + CALLBACKS_EXTRA_TIMEOUT;
+ if (mActiveVibratorCounter <= 0 || waitForVibrationComplete(this, wakeUpTime)) {
+ return Vibration.Status.FINISHED;
}
- }
- if (mForceStop) {
- stopAllVibrators();
- return Vibration.Status.CANCELLED;
- }
- return Vibration.Status.FINISHED;
+ // Timed out or vibration cancelled. Stop all vibrators anyway.
+ stopAllVibrators();
+ return mForceStop ? Vibration.Status.CANCELLED : Vibration.Status.FINISHED;
+ }
} finally {
if (timeout > 0) {
noteVibratorOff();
@@ -774,8 +822,10 @@
if (DEBUG) {
Slog.d(TAG, "DelayStep of " + mDelay + "ms starting...");
}
- waitUntil(SystemClock.uptimeMillis() + mDelay);
- return mForceStop ? Vibration.Status.CANCELLED : Vibration.Status.FINISHED;
+ if (waitUntil(SystemClock.uptimeMillis() + mDelay)) {
+ return Vibration.Status.FINISHED;
+ }
+ return Vibration.Status.CANCELLED;
} finally {
if (DEBUG) {
Slog.d(TAG, "DelayStep done.");
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index c25f1b4..5fe853a 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -63,6 +63,7 @@
import android.view.RemoteAnimationDefinition;
import com.android.internal.app.AssistUtils;
+import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.internal.protolog.common.ProtoLog;
import com.android.server.LocalServices;
import com.android.server.Watchdog;
@@ -1019,6 +1020,50 @@
}
@Override
+ public void restartActivityProcessIfVisible(IBinder token) {
+ ActivityTaskManagerService.enforceTaskPermission("restartActivityProcess");
+ final long callingId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token);
+ if (r != null) {
+ r.restartProcessIfVisible();
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(callingId);
+ }
+ }
+
+ @Override
+ public void invalidateHomeTaskSnapshot(IBinder token) {
+ synchronized (mGlobalLock) {
+ final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token);
+ if (r != null && r.isActivityTypeHome()) {
+ mService.mWindowManager.mTaskSnapshotController.removeSnapshotCache(
+ r.getTask().mTaskId);
+ }
+ }
+ }
+
+ @Override
+ public void dismissKeyguard(IBinder token, IKeyguardDismissCallback callback,
+ CharSequence message) {
+ if (message != null) {
+ mService.mAmInternal.enforceCallingPermission(
+ android.Manifest.permission.SHOW_KEYGUARD_MESSAGE, "dismissKeyguard");
+ }
+ final long callingId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ mService.mKeyguardController.dismissKeyguard(token, callback, message);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(callingId);
+ }
+ }
+
+ @Override
public void registerRemoteAnimations(IBinder token, RemoteAnimationDefinition definition) {
mService.mAmInternal.enforceCallingPermission(CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS,
"registerRemoteAnimations");
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 5610573..f0db3f9 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -231,7 +231,6 @@
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.os.TransferPipe;
-import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.internal.policy.KeyguardDismissCallback;
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.ArrayUtils;
@@ -1796,23 +1795,6 @@
}
@Override
- public void restartActivityProcessIfVisible(IBinder activityToken) {
- enforceTaskPermission("restartActivityProcess()");
- final long callingId = Binder.clearCallingIdentity();
- try {
- synchronized (mGlobalLock) {
- final ActivityRecord r = ActivityRecord.isInRootTaskLocked(activityToken);
- if (r == null) {
- return;
- }
- r.restartProcessIfVisible();
- }
- } finally {
- Binder.restoreCallingIdentity(callingId);
- }
- }
-
- @Override
public boolean removeTask(int taskId) {
enforceCallerIsRecentsOrHasPermission(REMOVE_TASKS, "removeTask()");
synchronized (mGlobalLock) {
@@ -3263,7 +3245,7 @@
// If the keyguard is showing or occluded, then try and dismiss it before
// entering picture-in-picture (this will prompt the user to authenticate if the
// device is currently locked).
- dismissKeyguard(r.appToken, new KeyguardDismissCallback() {
+ mActivityClientController.dismissKeyguard(r.appToken, new KeyguardDismissCallback() {
@Override
public void onDismissSucceeded() {
mH.post(enterPipRunnable);
@@ -3388,23 +3370,6 @@
}
@Override
- public void dismissKeyguard(IBinder token, IKeyguardDismissCallback callback,
- CharSequence message) {
- if (message != null) {
- mAmInternal.enforceCallingPermission(
- Manifest.permission.SHOW_KEYGUARD_MESSAGE, "dismissKeyguard()");
- }
- final long callingId = Binder.clearCallingIdentity();
- try {
- synchronized (mGlobalLock) {
- mKeyguardController.dismissKeyguard(token, callback, message);
- }
- } finally {
- Binder.restoreCallingIdentity(callingId);
- }
- }
-
- @Override
public void cancelTaskWindowTransition(int taskId) {
enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_TASKS,
"cancelTaskWindowTransition()");
@@ -3450,17 +3415,6 @@
return task.getSnapshot(isLowResolution, restoreFromDisk);
}
- @Override
- public void invalidateHomeTaskSnapshot(IBinder token) {
- synchronized (mGlobalLock) {
- final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token);
- if (r == null || !r.isActivityTypeHome()) {
- return;
- }
- mWindowManager.mTaskSnapshotController.removeSnapshotCache(r.getTask().mTaskId);
- }
- }
-
/** Return the user id of the last resumed activity. */
@Override
public @UserIdInt
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 8841a9b..2a40500 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -669,9 +669,8 @@
// Used in updating override configurations
private final Configuration mTempConfig = new Configuration();
- // Used in performing layout, to record the insets provided by other windows above the current
- // window.
- private InsetsState mTmpAboveInsetsState = new InsetsState();
+ // Used in performing layout
+ private boolean mTmpWindowsBehindIme;
/**
* Used to prevent recursions when calling
@@ -770,11 +769,17 @@
+ " parentHidden=" + w.isParentWindowHidden());
}
- // Sets mAboveInsets for each window. Windows behind the window providing the insets can
- // receive the insets.
- if (!w.mAboveInsetsState.equals(mTmpAboveInsetsState)) {
- w.mAboveInsetsState.set(mTmpAboveInsetsState);
- mWinInsetsChanged.add(w);
+ // Sets mBehindIme for each window. Windows behind IME can get IME insets.
+ if (w.mBehindIme != mTmpWindowsBehindIme) {
+ w.mBehindIme = mTmpWindowsBehindIme;
+ if (getInsetsStateController().getRawInsetsState().getSourceOrDefaultVisibility(
+ ITYPE_IME)) {
+ // If IME is invisible, behind IME or not doesn't make the insets different.
+ mWinInsetsChanged.add(w);
+ }
+ }
+ if (w == mInputMethodWindow) {
+ mTmpWindowsBehindIme = true;
}
// If this view is GONE, then skip it -- keep the current frame, and let the caller know
@@ -810,16 +815,8 @@
+ " mContainingFrame=" + w.getContainingFrame()
+ " mDisplayFrame=" + w.getDisplayFrame());
}
- provideInsetsByWindow(w);
};
- private void provideInsetsByWindow(WindowState w) {
- for (int i = 0; i < w.mProvidedInsetsSources.size(); i++) {
- final InsetsSource providedSource = w.mProvidedInsetsSources.valueAt(i);
- mTmpAboveInsetsState.addSource(providedSource);
- }
- }
-
private final Consumer<WindowState> mPerformLayoutAttached = w -> {
if (w.mLayoutAttached) {
if (DEBUG_LAYOUT) Slog.v(TAG, "2ND PASS " + w + " mHaveFrame=" + w.mHaveFrame
@@ -4272,20 +4269,14 @@
+ " dh=" + mDisplayInfo.logicalHeight);
}
- // Used to indicate that we have processed the insets windows. This needs to be after
- // beginLayoutLw to ensure the raw insets state display related info is initialized.
- final InsetsState rawInsetsState = getInsetsStateController().getRawInsetsState();
- mTmpAboveInsetsState = new InsetsState();
- mTmpAboveInsetsState.setDisplayFrame(rawInsetsState.getDisplayFrame());
- mTmpAboveInsetsState.setDisplayCutout(rawInsetsState.getDisplayCutout());
- mTmpAboveInsetsState.mirrorAlwaysVisibleInsetsSources(rawInsetsState);
-
int seq = mLayoutSeq + 1;
if (seq < 0) seq = 0;
mLayoutSeq = seq;
mTmpInitial = initial;
+ // Used to indicate that we have processed the IME window.
+ mTmpWindowsBehindIme = false;
// First perform layout of any root windows (not attached to another window).
forAllWindows(mPerformLayout, true /* traverseTopToBottom */);
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index 7d0854d..1692df6 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -16,7 +16,6 @@
package com.android.server.wm;
-import static android.os.Build.IS_DEBUGGABLE;
import static android.view.InsetsState.ITYPE_CLIMATE_BAR;
import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
import static android.view.InsetsState.ITYPE_IME;
@@ -152,7 +151,6 @@
// animate-out as new one animates-in.
mWin.cancelAnimation();
mWin.mPendingPositionChanged = null;
- mWin.mProvidedInsetsSources.remove(mSource.getType());
}
ProtoLog.d(WM_DEBUG_IME, "InsetsSource setWin %s", win);
mWin = win;
@@ -162,14 +160,11 @@
setServerVisible(false);
mSource.setFrame(new Rect());
mSource.setVisibleFrame(null);
- } else {
- mWin.mProvidedInsetsSources.put(mSource.getType(), mSource);
- if (mControllable) {
- mWin.setControllableInsetProvider(this);
- if (mPendingControlTarget != null) {
- updateControlForTarget(mPendingControlTarget, true /* force */);
- mPendingControlTarget = null;
- }
+ } else if (mControllable) {
+ mWin.setControllableInsetProvider(this);
+ if (mPendingControlTarget != null) {
+ updateControlForTarget(mPendingControlTarget, true /* force */);
+ mPendingControlTarget = null;
}
}
}
@@ -557,11 +552,6 @@
// TODO: use 0 alpha and remove t.hide() once b/138459974 is fixed.
t.setAlpha(animationLeash, 1 /* alpha */);
t.hide(animationLeash);
-
- // TODO(b/175954493): Remove this after finding root cause.
- if (IS_DEBUGGABLE) {
- animationLeash.setDebugRelease(true);
- }
}
ProtoLog.i(WM_DEBUG_IME,
"ControlAdapter startAnimation mSource: %s controlTarget: %s", mSource,
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index 7b709ea..398049f 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -104,8 +104,6 @@
* visible to the target. e.g., the source which represents the target window itself, and the
* IME source when the target is above IME. We also need to exclude certain types of insets
* source for client within specific windowing modes.
- * This is to get the insets for a window layout on the screen. If the window is not there, use
- * the {@link #getInsetsForWindowMetrics} to get insets instead.
*
* @param target The window associate with the perspective.
* @return The state stripped of the necessary information.
@@ -119,7 +117,7 @@
final @InternalInsetsType int type = provider != null
? provider.getSource().getType() : ITYPE_INVALID;
return getInsetsForTarget(type, target.getWindowingMode(), target.isAlwaysOnTop(),
- target.mAboveInsetsState);
+ isAboveIme(target));
}
InsetsState getInsetsForWindowMetrics(@NonNull WindowManager.LayoutParams attrs) {
@@ -134,7 +132,19 @@
final @WindowingMode int windowingMode = token != null
? token.getWindowingMode() : WINDOWING_MODE_UNDEFINED;
final boolean alwaysOnTop = token != null && token.isAlwaysOnTop();
- return getInsetsForTarget(type, windowingMode, alwaysOnTop, mState);
+ return getInsetsForTarget(type, windowingMode, alwaysOnTop, isAboveIme(token));
+ }
+
+ private boolean isAboveIme(WindowContainer target) {
+ final WindowState imeWindow = mDisplayContent.mInputMethodWindow;
+ if (target == null || imeWindow == null) {
+ return false;
+ }
+ if (target instanceof WindowState) {
+ final WindowState win = (WindowState) target;
+ return win.needsRelativeLayeringToIme() || !win.mBehindIme;
+ }
+ return false;
}
private static @InternalInsetsType
@@ -170,12 +180,11 @@
* @see #getInsetsForWindowMetrics
*/
private InsetsState getInsetsForTarget(@InternalInsetsType int type,
- @WindowingMode int windowingMode, boolean isAlwaysOnTop, InsetsState state) {
- boolean stateCopied = false;
+ @WindowingMode int windowingMode, boolean isAlwaysOnTop, boolean aboveIme) {
+ InsetsState state = mState;
if (type != ITYPE_INVALID) {
state = new InsetsState(state);
- stateCopied = true;
state.removeSource(type);
// Navigation bar doesn't get influenced by anything else
@@ -210,15 +219,23 @@
if (WindowConfiguration.isFloating(windowingMode)
|| (windowingMode == WINDOWING_MODE_MULTI_WINDOW && isAlwaysOnTop)) {
- if (!stateCopied) {
- state = new InsetsState(state);
- stateCopied = true;
- }
+ state = new InsetsState(state);
state.removeSource(ITYPE_STATUS_BAR);
state.removeSource(ITYPE_NAVIGATION_BAR);
state.removeSource(ITYPE_EXTRA_NAVIGATION_BAR);
}
+ if (aboveIme) {
+ InsetsSource imeSource = state.peekSource(ITYPE_IME);
+ if (imeSource != null && imeSource.isVisible()) {
+ imeSource = new InsetsSource(imeSource);
+ imeSource.setVisible(false);
+ imeSource.setFrame(0, 0, 0, 0);
+ state = new InsetsState(state);
+ state.addSource(imeSource);
+ }
+ }
+
return state;
}
diff --git a/services/core/java/com/android/server/wm/RefreshRatePolicy.java b/services/core/java/com/android/server/wm/RefreshRatePolicy.java
index 91014aa..26871d1 100644
--- a/services/core/java/com/android/server/wm/RefreshRatePolicy.java
+++ b/services/core/java/com/android/server/wm/RefreshRatePolicy.java
@@ -28,7 +28,7 @@
*/
class RefreshRatePolicy {
- private final int mLowRefreshRateId;
+ private final Mode mLowRefreshRateMode;
private final ArraySet<String> mNonHighRefreshRatePackages = new ArraySet<>();
private final HighRefreshRateDenylist mHighRefreshRateDenylist;
private final WindowManagerService mWmService;
@@ -56,7 +56,7 @@
RefreshRatePolicy(WindowManagerService wmService, DisplayInfo displayInfo,
HighRefreshRateDenylist denylist) {
- mLowRefreshRateId = findLowRefreshRateModeId(displayInfo);
+ mLowRefreshRateMode = findLowRefreshRateMode(displayInfo);
mHighRefreshRateDenylist = denylist;
mWmService = wmService;
}
@@ -65,7 +65,7 @@
* Finds the mode id with the lowest refresh rate which is >= 60hz and same resolution as the
* default mode.
*/
- private int findLowRefreshRateModeId(DisplayInfo displayInfo) {
+ private Mode findLowRefreshRateMode(DisplayInfo displayInfo) {
Mode mode = displayInfo.getDefaultMode();
float[] refreshRates = displayInfo.getDefaultRefreshRates();
float bestRefreshRate = mode.getRefreshRate();
@@ -104,13 +104,9 @@
// If app is using Camera, force it to default (lower) refresh rate.
if (mNonHighRefreshRatePackages.contains(packageName)) {
- return mLowRefreshRateId;
+ return mLowRefreshRateMode.getModeId();
}
- // If app is denylisted using higher refresh rate, return default (lower) refresh rate
- if (mHighRefreshRateDenylist.isDenylisted(packageName)) {
- return mLowRefreshRateId;
- }
return 0;
}
@@ -137,4 +133,18 @@
}
return LAYER_PRIORITY_UNSET;
}
+
+ float getPreferredRefreshRate(WindowState w) {
+ // If app is animating, it's not able to control refresh rate because we want the animation
+ // to run in default refresh rate.
+ if (w.isAnimating(TRANSITION | PARENTS)) {
+ return 0;
+ }
+
+ final String packageName = w.getOwningPackage();
+ if (mHighRefreshRateDenylist.isDenylisted(packageName)) {
+ return mLowRefreshRateMode.getRefreshRate();
+ }
+ return 0;
+ }
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 79d1123..ec1588d 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -124,6 +124,7 @@
import static com.android.server.wm.Task.ActivityState.STARTED;
import static com.android.server.wm.Task.ActivityState.STOPPING;
import static com.android.server.wm.TaskProto.ACTIVITY_TYPE;
+import static com.android.server.wm.TaskProto.AFFINITY;
import static com.android.server.wm.TaskProto.BOUNDS;
import static com.android.server.wm.TaskProto.CREATED_BY_ORGANIZER;
import static com.android.server.wm.TaskProto.DISPLAY_ID;
@@ -1244,27 +1245,20 @@
mCallingFeatureId = r.launchedFromFeatureId;
setIntent(intent != null ? intent : r.intent, info != null ? info : r.info);
setLockTaskAuth(r);
-
- final WindowContainer parent = getParent();
- if (parent != null) {
- final Task t = parent.asTask();
- if (t != null) {
- t.setIntent(r);
- }
- }
}
/** Sets the original intent, _without_ updating the calling uid or package. */
private void setIntent(Intent _intent, ActivityInfo info) {
- final boolean isLeaf = isLeafTask();
+ if (!isLeafTask()) return;
+
if (intent == null) {
mNeverRelinquishIdentity =
(info.flags & FLAG_RELINQUISH_TASK_IDENTITY) == 0;
- } else if (mNeverRelinquishIdentity && isLeaf) {
+ } else if (mNeverRelinquishIdentity) {
return;
}
- affinity = isLeaf ? info.taskAffinity : null;
+ affinity = info.taskAffinity;
if (intent == null) {
// If this task already has an intent associated with it, don't set the root
// affinity -- we don't want it changing after initially set, but the initially
@@ -7809,6 +7803,7 @@
}
proto.write(CREATED_BY_ORGANIZER, mCreatedByOrganizer);
+ proto.write(AFFINITY, affinity);
proto.end(token);
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 3be4e78..093106f 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -212,7 +212,6 @@
import android.os.WorkSource;
import android.provider.Settings;
import android.text.TextUtils;
-import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.util.MergedConfiguration;
@@ -233,6 +232,7 @@
import android.view.InsetsSource;
import android.view.InsetsState;
import android.view.InsetsState.InternalInsetsType;
+import android.view.Surface;
import android.view.Surface.Rotation;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
@@ -647,14 +647,9 @@
boolean mSeamlesslyRotated = false;
/**
- * The insets state of sources provided by windows above the current window.
+ * Indicates if this window is behind IME. Only windows behind IME can get insets from IME.
*/
- InsetsState mAboveInsetsState = new InsetsState();
-
- /**
- * The insets sources provided by this window.
- */
- ArrayMap<Integer, InsetsSource> mProvidedInsetsSources = new ArrayMap<>();
+ boolean mBehindIme = false;
/**
* Surface insets from the previous call to relayout(), used to track
@@ -730,6 +725,13 @@
*/
int mFrameRateSelectionPriority = RefreshRatePolicy.LAYER_PRIORITY_UNSET;
+ /**
+ * This is the frame rate which is passed to SurfaceFlinger if the window is part of the
+ * high refresh rate deny list. The variable is cached, so we do not send too many updates to
+ * SF.
+ */
+ float mDenyListFrameRate = 0f;
+
static final int BLAST_TIMEOUT_DURATION = 5000; /* milliseconds */
private final WindowProcessController mWpcForDisplayAreaConfigChanges;
@@ -5233,7 +5235,6 @@
return (mAttrs.flags & FLAG_BLUR_BEHIND) != 0 && mOwnerCanUseBackgroundBlur;
}
-
/**
* Notifies SF about the priority of the window, if it changed. SF then uses this information
* to decide which window's desired rendering rate should have a priority when deciding about
@@ -5242,13 +5243,21 @@
*/
@VisibleForTesting
void updateFrameRateSelectionPriorityIfNeeded() {
- final int priority = getDisplayContent().getDisplayPolicy().getRefreshRatePolicy()
- .calculatePriority(this);
+ RefreshRatePolicy refreshRatePolicy =
+ getDisplayContent().getDisplayPolicy().getRefreshRatePolicy();
+ final int priority = refreshRatePolicy.calculatePriority(this);
if (mFrameRateSelectionPriority != priority) {
mFrameRateSelectionPriority = priority;
getPendingTransaction().setFrameRateSelectionPriority(mSurfaceControl,
mFrameRateSelectionPriority);
}
+
+ final float refreshRate = refreshRatePolicy.getPreferredRefreshRate(this);
+ if (mDenyListFrameRate != refreshRate) {
+ mDenyListFrameRate = refreshRate;
+ getPendingTransaction().setFrameRate(
+ mSurfaceControl, mDenyListFrameRate, Surface.FRAME_RATE_COMPATIBILITY_EXACT);
+ }
}
private void updateGlobalScaleIfNeeded() {
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 9d013c1..345b246 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -70,6 +70,7 @@
"frameworks/base/libs",
"frameworks/native/services",
"system/gatekeeper/include",
+ "system/memory/libmeminfo/include",
],
header_libs: [
@@ -97,6 +98,7 @@
"libhardware_legacy",
"libhidlbase",
"libkeystore_binder",
+ "libmeminfo",
"libmtp",
"libnativehelper",
"libnativewindow",
diff --git a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
index 678308a..156ef79 100644
--- a/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
+++ b/services/core/jni/com_android_server_am_CachedAppOptimizer.cpp
@@ -17,15 +17,25 @@
#define LOG_TAG "CachedAppOptimizer"
//#define LOG_NDEBUG 0
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+#include <android_runtime/AndroidRuntime.h>
+#include <cutils/compiler.h>
#include <dirent.h>
+#include <jni.h>
+#include <linux/errno.h>
+#include <log/log.h>
+#include <meminfo/procmeminfo.h>
+#include <nativehelper/JNIHelp.h>
#include <stddef.h>
#include <stdio.h>
+#include <sys/mman.h>
#include <sys/stat.h>
+#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
-#include <android-base/stringprintf.h>
-#include <android-base/file.h>
+#include <algorithm>
#include <nativehelper/JNIHelp.h>
#include <android_runtime/AndroidRuntime.h>
@@ -35,12 +45,149 @@
using android::base::StringPrintf;
using android::base::WriteStringToFile;
+using android::meminfo::ProcMemInfo;
+using namespace android::meminfo;
+
+// This is temporarily hard-coded and should be removed once
+// bionic/libc/kernel/uapi/asm-generic/unistd.h are updated with process_madvise syscall header
+#ifndef __NR_process_madvise
+#define __NR_process_madvise 440
+#define MADV_COLD 20 /* deactivate these pages */
+#define MADV_PAGEOUT 21
+#endif
+
+#define COMPACT_ACTION_FILE_FLAG 1
+#define COMPACT_ACTION_ANON_FLAG 2
+
+using VmaToAdviseFunc = std::function<int(const Vma&)>;
#define SYNC_RECEIVED_WHILE_FROZEN (1)
#define ASYNC_RECEIVED_WHILE_FROZEN (2)
namespace android {
+// Legacy method for compacting processes, any new code should
+// use compactProcess instead.
+static inline void compactProcessProcfs(int pid, const std::string& compactionType) {
+ std::string reclaim_path = StringPrintf("/proc/%d/reclaim", pid);
+ WriteStringToFile(compactionType, reclaim_path);
+}
+
+static int compactMemory(const std::vector<Vma>& vmas, int pid, int madviseType) {
+ // UIO_MAXIOV is currently a small value and we might have more addresses
+ // we do multiple syscalls if we exceed its maximum
+ static struct iovec vmasToKernel[UIO_MAXIOV];
+
+ int err = 0;
+
+ if (vmas.empty()) {
+ return err;
+ }
+
+ int pidfd = syscall(__NR_pidfd_open, pid, 0);
+ err = -errno;
+ if (err < 0) {
+ // Skip compaction if failed to open pidfd with any error
+ return err;
+ }
+
+ for (int iBase = 0; iBase < vmas.size(); iBase += UIO_MAXIOV) {
+ int totalVmasToKernel = std::min(UIO_MAXIOV, (int)(vmas.size() - iBase));
+ for (int iVec = 0, iVma = iBase; iVec < totalVmasToKernel; ++iVec, ++iVma) {
+ vmasToKernel[iVec].iov_base = (void*)vmas[iVma].start;
+ vmasToKernel[iVec].iov_len = vmas[iVma].end - vmas[iVma].start;
+ }
+
+ process_madvise(pidfd, vmasToKernel, totalVmasToKernel, madviseType, 0);
+ err = -errno;
+ if (CC_UNLIKELY(err == -ENOSYS)) {
+ // Syscall does not exist, skip trying more calls process_madvise
+ break;
+ }
+ }
+
+ close(pidfd);
+
+ return err;
+}
+
+static int getFilePageAdvice(const Vma& vma) {
+ if (vma.inode > 0 && !vma.is_shared) {
+ return MADV_COLD;
+ }
+ return -1;
+}
+static int getAnonPageAdvice(const Vma& vma) {
+ if (vma.inode == 0 && !vma.is_shared) {
+ return MADV_PAGEOUT;
+ }
+ return -1;
+}
+static bool getAnyPageAdvice(const Vma& vma) {
+ if (vma.inode == 0 && !vma.is_shared) {
+ return MADV_PAGEOUT;
+ }
+ return MADV_COLD;
+}
+
+// Perform a full process compaction using process_madvise syscall
+// reading all filtering VMAs and filtering pages as specified by pageFilter
+static int compactProcess(int pid, VmaToAdviseFunc vmaToAdviseFunc) {
+ ProcMemInfo meminfo(pid);
+ std::vector<Vma> pageoutVmas, coldVmas;
+ auto vmaCollectorCb = [&](Vma vma) {
+ int advice = vmaToAdviseFunc(vma);
+ switch (advice) {
+ case MADV_COLD:
+ coldVmas.push_back(vma);
+ break;
+ case MADV_PAGEOUT:
+ pageoutVmas.push_back(vma);
+ break;
+ }
+ };
+ meminfo.ForEachVma(vmaCollectorCb);
+
+ int err = compactMemory(pageoutVmas, pid, MADV_PAGEOUT);
+ if (!err) {
+ err = compactMemory(coldVmas, pid, MADV_COLD);
+ }
+ return err;
+}
+
+// Compact process using process_madvise syscall or fallback to procfs in
+// case syscall does not exist.
+static void compactProcessOrFallback(int pid, int compactionFlags) {
+ if ((compactionFlags & (COMPACT_ACTION_ANON_FLAG | COMPACT_ACTION_FILE_FLAG)) == 0) return;
+
+ bool compactAnon = compactionFlags & COMPACT_ACTION_ANON_FLAG;
+ bool compactFile = compactionFlags & COMPACT_ACTION_FILE_FLAG;
+
+ // Set when the system does not support process_madvise syscall to avoid
+ // gathering VMAs in subsequent calls prior to falling back to procfs
+ static bool shouldForceProcFs = false;
+ std::string compactionType;
+ VmaToAdviseFunc vmaToAdviseFunc;
+
+ if (compactAnon) {
+ if (compactFile) {
+ compactionType = "all";
+ vmaToAdviseFunc = getAnyPageAdvice;
+ } else {
+ compactionType = "anon";
+ vmaToAdviseFunc = getAnonPageAdvice;
+ }
+ } else {
+ compactionType = "file";
+ vmaToAdviseFunc = getFilePageAdvice;
+ }
+
+ if (shouldForceProcFs || compactProcess(pid, vmaToAdviseFunc) == -ENOSYS) {
+ shouldForceProcFs = true;
+ compactProcessProcfs(pid, compactionType);
+ }
+}
+
// This performs per-process reclaim on all processes belonging to non-app UIDs.
// For the most part, these are non-zygote processes like Treble HALs, but it
// also includes zygote-derived processes that run in system UIDs, like bluetooth
@@ -74,11 +221,17 @@
continue;
}
- std::string reclaim_path = StringPrintf("/proc/%s/reclaim", current->d_name);
- WriteStringToFile(std::string("all"), reclaim_path);
+ int pid = atoi(current->d_name);
+
+ compactProcessOrFallback(pid, COMPACT_ACTION_ANON_FLAG | COMPACT_ACTION_FILE_FLAG);
}
}
+static void com_android_server_am_CachedAppOptimizer_compactProcess(JNIEnv*, jobject, jint pid,
+ jint compactionFlags) {
+ compactProcessOrFallback(pid, compactionFlags);
+}
+
static void com_android_server_am_CachedAppOptimizer_enableFreezerInternal(
JNIEnv *env, jobject clazz, jboolean enable) {
bool success = true;
@@ -126,14 +279,14 @@
}
static const JNINativeMethod sMethods[] = {
- /* name, signature, funcPtr */
- {"compactSystem", "()V", (void*)com_android_server_am_CachedAppOptimizer_compactSystem},
- {"enableFreezerInternal", "(Z)V",
- (void*)com_android_server_am_CachedAppOptimizer_enableFreezerInternal},
- {"freezeBinder", "(IZ)V", (void*)com_android_server_am_CachedAppOptimizer_freezeBinder},
- {"getBinderFreezeInfo", "(I)I",
- (void*)com_android_server_am_CachedAppOptimizer_getBinderFreezeInfo}
-};
+ /* name, signature, funcPtr */
+ {"compactSystem", "()V", (void*)com_android_server_am_CachedAppOptimizer_compactSystem},
+ {"compactProcess", "(II)V", (void*)com_android_server_am_CachedAppOptimizer_compactProcess},
+ {"enableFreezerInternal", "(Z)V",
+ (void*)com_android_server_am_CachedAppOptimizer_enableFreezerInternal},
+ {"freezeBinder", "(IZ)V", (void*)com_android_server_am_CachedAppOptimizer_freezeBinder},
+ {"getBinderFreezeInfo", "(I)I",
+ (void*)com_android_server_am_CachedAppOptimizer_getBinderFreezeInfo}};
int register_android_server_am_CachedAppOptimizer(JNIEnv* env)
{
diff --git a/services/core/jni/com_android_server_powerstats_PowerStatsService.cpp b/services/core/jni/com_android_server_powerstats_PowerStatsService.cpp
index ec2549c..1208354 100644
--- a/services/core/jni/com_android_server_powerstats_PowerStatsService.cpp
+++ b/services/core/jni/com_android_server_powerstats_PowerStatsService.cpp
@@ -33,6 +33,7 @@
static jmethodID method_C_init;
static jfieldID field_C_id;
static jfieldID field_C_name;
+static jfieldID field_C_subsystem;
// EnergyMeasurement
static jclass class_EM;
@@ -277,11 +278,14 @@
channelArray = env->NewObjectArray(railInfo.size(), class_C, nullptr);
for (int i = 0; i < railInfo.size(); i++) {
jstring name = env->NewStringUTF(railInfo[i].railName.c_str());
+ jstring subsystem = env->NewStringUTF(railInfo[i].subsysName.c_str());
jobject channel = env->NewObject(class_C, method_C_init);
env->SetIntField(channel, field_C_id, railInfo[i].index);
env->SetObjectField(channel, field_C_name, name);
+ env->SetObjectField(channel, field_C_subsystem, subsystem);
env->SetObjectArrayElement(channelArray, i, channel);
env->DeleteLocalRef(name);
+ env->DeleteLocalRef(subsystem);
env->DeleteLocalRef(channel);
}
}
@@ -359,6 +363,7 @@
method_C_init = env->GetMethodID(class_C, "<init>", "()V");
field_C_id = env->GetFieldID(class_C, "id", "I");
field_C_name = env->GetFieldID(class_C, "name", "Ljava/lang/String;");
+ field_C_subsystem = env->GetFieldID(class_C, "subsystem", "Ljava/lang/String;");
// EnergyMeasurement
temp = env->FindClass("android/hardware/power/stats/EnergyMeasurement");
diff --git a/services/core/xsd/Android.bp b/services/core/xsd/Android.bp
index d1918d8..bdccf45 100644
--- a/services/core/xsd/Android.bp
+++ b/services/core/xsd/Android.bp
@@ -8,11 +8,19 @@
xsd_config {
name: "platform-compat-config",
- srcs: ["platform-compat-config.xsd"],
- api_dir: "platform-compat-schema",
+ srcs: ["platform-compat/config/platform-compat-config.xsd"],
+ api_dir: "platform-compat/config/schema",
package_name: "com.android.server.compat.config",
}
+xsd_config {
+ name: "platform-compat-overrides",
+ srcs: ["platform-compat/overrides/platform-compat-overrides.xsd"],
+ api_dir: "platform-compat/overrides/schema",
+ package_name: "com.android.server.compat.overrides",
+ gen_writer: true,
+}
+
xsd_config {
name: "display-device-config",
diff --git a/services/core/xsd/platform-compat-schema/OWNERS b/services/core/xsd/platform-compat/OWNERS
similarity index 100%
rename from services/core/xsd/platform-compat-schema/OWNERS
rename to services/core/xsd/platform-compat/OWNERS
diff --git a/services/core/xsd/platform-compat-config.xsd b/services/core/xsd/platform-compat/config/platform-compat-config.xsd
similarity index 100%
rename from services/core/xsd/platform-compat-config.xsd
rename to services/core/xsd/platform-compat/config/platform-compat-config.xsd
diff --git a/services/core/xsd/platform-compat-schema/current.txt b/services/core/xsd/platform-compat/config/schema/current.txt
similarity index 100%
rename from services/core/xsd/platform-compat-schema/current.txt
rename to services/core/xsd/platform-compat/config/schema/current.txt
diff --git a/services/core/xsd/platform-compat-schema/last_current.txt b/services/core/xsd/platform-compat/config/schema/last_current.txt
similarity index 100%
rename from services/core/xsd/platform-compat-schema/last_current.txt
rename to services/core/xsd/platform-compat/config/schema/last_current.txt
diff --git a/services/core/xsd/platform-compat-schema/last_removed.txt b/services/core/xsd/platform-compat/config/schema/last_removed.txt
similarity index 100%
rename from services/core/xsd/platform-compat-schema/last_removed.txt
rename to services/core/xsd/platform-compat/config/schema/last_removed.txt
diff --git a/services/core/xsd/platform-compat-schema/removed.txt b/services/core/xsd/platform-compat/config/schema/removed.txt
similarity index 100%
rename from services/core/xsd/platform-compat-schema/removed.txt
rename to services/core/xsd/platform-compat/config/schema/removed.txt
diff --git a/services/core/xsd/platform-compat/overrides/platform-compat-overrides.xsd b/services/core/xsd/platform-compat/overrides/platform-compat-overrides.xsd
new file mode 100644
index 0000000..e27e1b8
--- /dev/null
+++ b/services/core/xsd/platform-compat/overrides/platform-compat-overrides.xsd
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright (C) 2019 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<!-- This defines the format of the XML file used to store compat config overrides in
+ ~ /data/misc/appcompat/compat_framework_overrides.xml
+-->
+<xs:schema version="2.0" elementFormDefault="qualified"
+ xmlns:xs="http://www.w3.org/2001/XMLSchema">
+
+
+ <xs:complexType name="override-value">
+ <xs:attribute type="xs:string" name="packageName" use="required" />
+ <xs:attribute type="xs:boolean" name="enabled" use="required" />
+ </xs:complexType>
+
+ <xs:complexType name="change-overrides">
+ <xs:attribute type="xs:long" name="changeId" use="required"/>
+ <xs:element name="validated">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="override-value" type="override-value" maxOccurs="unbounded" minOccurs="0" />
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="deferred">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="override-value" type="override-value" maxOccurs="unbounded" minOccurs="0" />
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ </xs:complexType>
+
+ <xs:element name="overrides">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="change-overrides" type="change-overrides" maxOccurs="unbounded" minOccurs="0" />
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+</xs:schema>
diff --git a/services/core/xsd/platform-compat/overrides/schema/current.txt b/services/core/xsd/platform-compat/overrides/schema/current.txt
new file mode 100644
index 0000000..08b8207
--- /dev/null
+++ b/services/core/xsd/platform-compat/overrides/schema/current.txt
@@ -0,0 +1,51 @@
+// Signature format: 2.0
+package com.android.server.compat.overrides {
+
+ public class ChangeOverrides {
+ ctor public ChangeOverrides();
+ method public long getChangeId();
+ method public com.android.server.compat.overrides.ChangeOverrides.Deferred getDeferred();
+ method public com.android.server.compat.overrides.ChangeOverrides.Validated getValidated();
+ method public void setChangeId(long);
+ method public void setDeferred(com.android.server.compat.overrides.ChangeOverrides.Deferred);
+ method public void setValidated(com.android.server.compat.overrides.ChangeOverrides.Validated);
+ }
+
+ public static class ChangeOverrides.Deferred {
+ ctor public ChangeOverrides.Deferred();
+ method public java.util.List<com.android.server.compat.overrides.OverrideValue> getOverrideValue();
+ }
+
+ public static class ChangeOverrides.Validated {
+ ctor public ChangeOverrides.Validated();
+ method public java.util.List<com.android.server.compat.overrides.OverrideValue> getOverrideValue();
+ }
+
+ public class OverrideValue {
+ ctor public OverrideValue();
+ method public boolean getEnabled();
+ method public String getPackageName();
+ method public void setEnabled(boolean);
+ method public void setPackageName(String);
+ }
+
+ public class Overrides {
+ ctor public Overrides();
+ method public java.util.List<com.android.server.compat.overrides.ChangeOverrides> getChangeOverrides();
+ }
+
+ public class XmlParser {
+ ctor public XmlParser();
+ method public static com.android.server.compat.overrides.Overrides read(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+ method public static String readText(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+ method public static void skip(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+ }
+
+ public class XmlWriter implements java.io.Closeable {
+ ctor public XmlWriter(java.io.PrintWriter);
+ method public void close();
+ method public static void write(com.android.server.compat.overrides.XmlWriter, com.android.server.compat.overrides.Overrides) throws java.io.IOException;
+ }
+
+}
+
diff --git a/services/core/xsd/platform-compat-schema/last_current.txt b/services/core/xsd/platform-compat/overrides/schema/last_current.txt
similarity index 100%
copy from services/core/xsd/platform-compat-schema/last_current.txt
copy to services/core/xsd/platform-compat/overrides/schema/last_current.txt
diff --git a/services/core/xsd/platform-compat-schema/last_removed.txt b/services/core/xsd/platform-compat/overrides/schema/last_removed.txt
similarity index 100%
copy from services/core/xsd/platform-compat-schema/last_removed.txt
copy to services/core/xsd/platform-compat/overrides/schema/last_removed.txt
diff --git a/services/core/xsd/platform-compat-schema/removed.txt b/services/core/xsd/platform-compat/overrides/schema/removed.txt
similarity index 100%
copy from services/core/xsd/platform-compat-schema/removed.txt
copy to services/core/xsd/platform-compat/overrides/schema/removed.txt
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 12595af..d28c3cc 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -326,13 +326,15 @@
private static final String TIME_ZONE_DETECTOR_SERVICE_CLASS =
"com.android.server.timezonedetector.TimeZoneDetectorService$Lifecycle";
private static final String LOCATION_TIME_ZONE_MANAGER_SERVICE_CLASS =
- "com.android.server.location.timezone.LocationTimeZoneManagerService$Lifecycle";
+ "com.android.server.timezonedetector.location.LocationTimeZoneManagerService$Lifecycle";
private static final String GNSS_TIME_UPDATE_SERVICE_CLASS =
"com.android.server.timedetector.GnssTimeUpdateService$Lifecycle";
private static final String ACCESSIBILITY_MANAGER_SERVICE_CLASS =
"com.android.server.accessibility.AccessibilityManagerService$Lifecycle";
private static final String ADB_SERVICE_CLASS =
"com.android.server.adb.AdbService$Lifecycle";
+ private static final String SPEECH_RECOGNITION_MANAGER_SERVICE_CLASS =
+ "com.android.server.speech.SpeechRecognitionManagerService";
private static final String APP_PREDICTION_MANAGER_SERVICE_CLASS =
"com.android.server.appprediction.AppPredictionManagerService";
private static final String CONTENT_SUGGESTIONS_SERVICE_CLASS =
@@ -1629,12 +1631,21 @@
"MusicRecognitionManagerService not defined by OEM or disabled by flag");
}
-
startContentCaptureService(context, t);
startAttentionService(context, t);
startRotationResolverService(context, t);
startSystemCaptionsManagerService(context, t);
+ // System Speech Recognition Service
+ if (deviceHasConfigString(context,
+ R.string.config_defaultOnDeviceSpeechRecognitionService)) {
+ t.traceBegin("StartSpeechRecognitionManagerService");
+ mSystemServiceManager.startService(SPEECH_RECOGNITION_MANAGER_SERVICE_CLASS);
+ t.traceEnd();
+ } else {
+ Slog.d(TAG, "System speech recognition is not defined by OEM");
+ }
+
// App prediction manager service
if (deviceHasConfigString(context, R.string.config_defaultAppPredictionService)) {
t.traceBegin("StartAppPredictionService");
@@ -1694,15 +1705,6 @@
}
t.traceEnd();
- t.traceBegin("StartVcnManagementService");
- try {
- vcnManagement = VcnManagementService.create(context);
- ServiceManager.addService(Context.VCN_MANAGEMENT_SERVICE, vcnManagement);
- } catch (Throwable e) {
- reportWtf("starting VCN Management Service", e);
- }
- t.traceEnd();
-
t.traceBegin("StartFontManagerService");
mSystemServiceManager.startService(FontManagerService.Lifecycle.class);
t.traceEnd();
@@ -1804,6 +1806,15 @@
networkPolicy.bindConnectivityManager(connectivity);
t.traceEnd();
+ t.traceBegin("StartVcnManagementService");
+ try {
+ vcnManagement = VcnManagementService.create(context);
+ ServiceManager.addService(Context.VCN_MANAGEMENT_SERVICE, vcnManagement);
+ } catch (Throwable e) {
+ reportWtf("starting VCN Management Service", e);
+ }
+ t.traceEnd();
+
t.traceBegin("StartNsdService");
try {
serviceDiscovery = NsdService.create(context);
@@ -2621,15 +2632,6 @@
reportWtf("making IpSec Service ready", e);
}
t.traceEnd();
- t.traceBegin("MakeVcnManagementServiceReady");
- try {
- if (vcnManagementF != null) {
- vcnManagementF.systemReady();
- }
- } catch (Throwable e) {
- reportWtf("making VcnManagementService ready", e);
- }
- t.traceEnd();
t.traceBegin("MakeNetworkStatsServiceReady");
try {
if (networkStatsF != null) {
@@ -2648,6 +2650,15 @@
reportWtf("making Connectivity Service ready", e);
}
t.traceEnd();
+ t.traceBegin("MakeVcnManagementServiceReady");
+ try {
+ if (vcnManagementF != null) {
+ vcnManagementF.systemReady();
+ }
+ } catch (Throwable e) {
+ reportWtf("making VcnManagementService ready", e);
+ }
+ t.traceEnd();
t.traceBegin("MakeNetworkPolicyServiceReady");
try {
if (networkPolicyF != null) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/AppChildProcessTest.java b/services/tests/mockingservicestests/src/com/android/server/am/AppChildProcessTest.java
index b85da94..17f326f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/AppChildProcessTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/AppChildProcessTest.java
@@ -298,7 +298,7 @@
}
void addToProcess(int uid, int pid, int newPid) {
- final String path = PhantomProcessList.getCgroupFilePath(uid, pid);
+ final String path = mPhantomProcessList.getCgroupFilePath(uid, pid);
StringBuffer sb = mPathToData.get(path);
if (sb == null) {
sb = new StringBuffer();
diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
index ac8dc34..a53ff9b 100644
--- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
@@ -44,6 +44,8 @@
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Paths;
import java.util.UUID;
@RunWith(AndroidJUnit4.class)
@@ -69,6 +71,10 @@
os.close();
}
+ private String readFile(File file) throws IOException {
+ return new String(Files.readAllBytes(Paths.get(file.toURI())));
+ }
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
@@ -499,4 +505,86 @@
assertThat(compatConfig.isChangeEnabled(1236L,
ApplicationInfoBuilder.create().withTargetSdk(1).build())).isTrue();
}
+
+ @Test
+ public void testSaveOverrides() throws Exception {
+ File overridesFile = new File(createTempDir(), "overrides.xml");
+ CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+ .addDisabledChangeWithId(1L)
+ .addEnableSinceSdkChangeWithId(2, 2L)
+ .build();
+ compatConfig.forceNonDebuggableFinalForTest(true);
+ compatConfig.initOverrides(overridesFile);
+ when(mPackageManager.getApplicationInfo(eq("foo.bar"), anyInt()))
+ .thenReturn(ApplicationInfoBuilder.create()
+ .withPackageName("foo.bar")
+ .debuggable()
+ .build());
+ when(mPackageManager.getApplicationInfo(eq("bar.baz"), anyInt()))
+ .thenThrow(new NameNotFoundException());
+
+ compatConfig.addOverride(1L, "foo.bar", true);
+ compatConfig.addOverride(2L, "bar.baz", false);
+
+ assertThat(readFile(overridesFile)).isEqualTo("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+ + "<overrides>\n"
+ + " <change-overrides changeId=\"1\">\n"
+ + " <validated>\n"
+ + " <override-value packageName=\"foo.bar\" enabled=\"true\">\n"
+ + " </override-value>\n"
+ + " </validated>\n"
+ + " <deferred>\n"
+ + " </deferred>\n"
+ + " </change-overrides>\n"
+ + " <change-overrides changeId=\"2\">\n"
+ + " <validated>\n"
+ + " </validated>\n"
+ + " <deferred>\n"
+ + " <override-value packageName=\"bar.baz\" enabled=\"false\">\n"
+ + " </override-value>\n"
+ + " </deferred>\n"
+ + " </change-overrides>\n"
+ + "</overrides>\n");
+ }
+
+ @Test
+ public void testLoadOverrides() throws Exception {
+ File tempDir = createTempDir();
+ File overridesFile = new File(tempDir, "overrides.xml");
+ // Change 1 is enabled for foo.bar (validated)
+ // Change 2 is disabled for bar.baz (deferred)
+ String xmlData = "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
+ + "<overrides>"
+ + "<change-overrides changeId=\"1\">"
+ + "<deferred/>"
+ + "<validated>"
+ + "<override-value packageName=\"foo.bar\" enabled=\"true\"/>"
+ + "</validated>"
+ + "</change-overrides>"
+ + "<change-overrides changeId=\"2\">"
+ + "<deferred>"
+ + "<override-value packageName=\"bar.baz\" enabled=\"false\"/>"
+ + "</deferred>"
+ + "<validated/>"
+ + "</change-overrides>"
+ + "</overrides>";
+ writeToFile(tempDir, "overrides.xml", xmlData);
+ CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+ .addDisabledChangeWithId(1L)
+ .addEnableSinceSdkChangeWithId(2, 2L)
+ .build();
+ compatConfig.forceNonDebuggableFinalForTest(true);
+ compatConfig.initOverrides(overridesFile);
+ ApplicationInfo applicationInfo = ApplicationInfoBuilder.create()
+ .withPackageName("foo.bar")
+ .debuggable()
+ .build();
+ when(mPackageManager.getApplicationInfo(eq("foo.bar"), anyInt()))
+ .thenReturn(applicationInfo);
+ when(mPackageManager.getApplicationInfo(eq("bar.baz"), anyInt()))
+ .thenThrow(new NameNotFoundException());
+
+ assertThat(compatConfig.isChangeEnabled(1L, applicationInfo)).isTrue();
+ assertThat(compatConfig.willChangeBeEnabled(2L, "bar.baz")).isFalse();
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
index 26c304f..2e3178b 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
@@ -818,10 +818,5 @@
PEAK_REFRESH_RATE_URI);
}
}
-
- @Override
- public boolean isDeviceInteractive(@NonNull Context context) {
- return true;
- }
}
}
diff --git a/services/tests/servicestests/src/com/android/server/graphics/fonts/PersistentSystemFontConfigTest.java b/services/tests/servicestests/src/com/android/server/graphics/fonts/PersistentSystemFontConfigTest.java
new file mode 100644
index 0000000..c10cee9
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/graphics/fonts/PersistentSystemFontConfigTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.graphics.fonts;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public final class PersistentSystemFontConfigTest {
+
+ @Test
+ public void testWriteRead() throws IOException, XmlPullParserException {
+ long expectedModifiedDate = 1234567890;
+ PersistentSystemFontConfig.Config config = new PersistentSystemFontConfig.Config();
+ config.lastModifiedDate = expectedModifiedDate;
+
+ try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+ PersistentSystemFontConfig.writeToXml(baos, config);
+
+ byte[] written = baos.toByteArray();
+ assertThat(written).isNotEmpty();
+
+ try (ByteArrayInputStream bais = new ByteArrayInputStream(written)) {
+ PersistentSystemFontConfig.Config another = new PersistentSystemFontConfig.Config();
+ PersistentSystemFontConfig.loadFromXml(bais, another);
+
+ assertThat(another.lastModifiedDate).isEqualTo(expectedModifiedDate);
+ }
+ }
+ }
+
+ @Test
+ public void testWrongType() throws IOException, XmlPullParserException {
+ String xml = "<fontConfig>"
+ + " <lastModifiedDate value=\"string\" />"
+ + "</fontConfig>";
+
+ try (ByteArrayInputStream bais =
+ new ByteArrayInputStream(xml.getBytes(StandardCharsets.UTF_8))) {
+ PersistentSystemFontConfig.Config config = new PersistentSystemFontConfig.Config();
+ PersistentSystemFontConfig.loadFromXml(bais, config);
+ assertThat(config.lastModifiedDate).isEqualTo(0);
+ }
+ }
+
+}
diff --git a/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java b/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java
index 75bf1e6..f437d1f 100644
--- a/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java
+++ b/services/tests/servicestests/src/com/android/server/graphics/fonts/UpdatableFontDirTest.java
@@ -22,8 +22,10 @@
import static org.junit.Assert.fail;
import android.content.Context;
+import android.graphics.fonts.FontManager;
import android.os.FileUtils;
import android.platform.test.annotations.Presubmit;
+import android.system.Os;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
@@ -36,6 +38,7 @@
import java.io.File;
import java.io.FileInputStream;
+import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
@@ -107,6 +110,7 @@
private File mCacheDir;
private File mUpdatableFontFilesDir;
+ private File mConfigFile;
private List<File> mPreinstalledFontDirs;
@SuppressWarnings("ResultOfMethodCallIgnored")
@@ -124,6 +128,7 @@
for (File dir : mPreinstalledFontDirs) {
dir.mkdir();
}
+ mConfigFile = new File(mCacheDir, "config.xml");
}
@After
@@ -133,19 +138,30 @@
@Test
public void construct() throws Exception {
+ long expectedModifiedDate = 1234567890;
FakeFontFileParser parser = new FakeFontFileParser();
FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
+ PersistentSystemFontConfig.Config config = new PersistentSystemFontConfig.Config();
+ config.lastModifiedDate = expectedModifiedDate;
+ writeConfig(config, mConfigFile);
UpdatableFontDir dirForPreparation = new UpdatableFontDir(
- mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil);
+ mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
+ mConfigFile);
+ assertThat(dirForPreparation.getSystemFontConfig().getLastModifiedDate())
+ .isEqualTo(expectedModifiedDate);
installFontFile(dirForPreparation, "foo,1", GOOD_SIGNATURE);
installFontFile(dirForPreparation, "bar,2", GOOD_SIGNATURE);
installFontFile(dirForPreparation, "foo,3", GOOD_SIGNATURE);
installFontFile(dirForPreparation, "bar,4", GOOD_SIGNATURE);
// Four font dirs are created.
assertThat(mUpdatableFontFilesDir.list()).hasLength(4);
+ //
+ assertThat(dirForPreparation.getSystemFontConfig().getLastModifiedDate())
+ .isNotEqualTo(expectedModifiedDate);
UpdatableFontDir dir = new UpdatableFontDir(
- mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil);
+ mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
+ mConfigFile);
assertThat(dir.getFontFileMap()).containsKey("foo.ttf");
assertThat(parser.getRevision(dir.getFontFileMap().get("foo.ttf"))).isEqualTo(3);
assertThat(dir.getFontFileMap()).containsKey("bar.ttf");
@@ -159,7 +175,8 @@
FakeFontFileParser parser = new FakeFontFileParser();
FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
UpdatableFontDir dir = new UpdatableFontDir(
- mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil);
+ mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
+ mConfigFile);
assertThat(dir.getFontFileMap()).isEmpty();
}
@@ -168,7 +185,8 @@
FakeFontFileParser parser = new FakeFontFileParser();
FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
UpdatableFontDir dirForPreparation = new UpdatableFontDir(
- mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil);
+ mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
+ mConfigFile);
installFontFile(dirForPreparation, "foo,1", GOOD_SIGNATURE);
installFontFile(dirForPreparation, "bar,2", GOOD_SIGNATURE);
installFontFile(dirForPreparation, "foo,3", GOOD_SIGNATURE);
@@ -179,7 +197,8 @@
fakeFsverityUtil.remove(
dirForPreparation.getFontFileMap().get("foo.ttf").getAbsolutePath());
UpdatableFontDir dir = new UpdatableFontDir(
- mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil);
+ mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
+ mConfigFile);
assertThat(dir.getFontFileMap()).isEmpty();
// All font dirs (including dir for "bar.ttf") should be deleted.
assertThat(mUpdatableFontFilesDir.list()).hasLength(0);
@@ -190,7 +209,8 @@
FakeFontFileParser parser = new FakeFontFileParser();
FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
UpdatableFontDir dirForPreparation = new UpdatableFontDir(
- mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil);
+ mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
+ mConfigFile);
installFontFile(dirForPreparation, "foo,1", GOOD_SIGNATURE);
installFontFile(dirForPreparation, "bar,2", GOOD_SIGNATURE);
installFontFile(dirForPreparation, "foo,3", GOOD_SIGNATURE);
@@ -202,7 +222,8 @@
FileUtils.stringToFile(dirForPreparation.getFontFileMap().get("foo.ttf"), "bar,4");
UpdatableFontDir dir = new UpdatableFontDir(
- mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil);
+ mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
+ mConfigFile);
assertThat(dir.getFontFileMap()).isEmpty();
// All font dirs (including dir for "bar.ttf") should be deleted.
assertThat(mUpdatableFontFilesDir.list()).hasLength(0);
@@ -213,7 +234,8 @@
FakeFontFileParser parser = new FakeFontFileParser();
FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
UpdatableFontDir dirForPreparation = new UpdatableFontDir(
- mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil);
+ mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
+ mConfigFile);
installFontFile(dirForPreparation, "foo,1", GOOD_SIGNATURE);
installFontFile(dirForPreparation, "bar,2", GOOD_SIGNATURE);
installFontFile(dirForPreparation, "foo,3", GOOD_SIGNATURE);
@@ -226,7 +248,8 @@
FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(0), "bar.ttf"), "bar,1");
FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(1), "bar.ttf"), "bar,2");
UpdatableFontDir dir = new UpdatableFontDir(
- mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil);
+ mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
+ mConfigFile);
// For foo.ttf, preinstalled font (revision 5) should be used.
assertThat(dir.getFontFileMap()).doesNotContainKey("foo.ttf");
// For bar.ttf, updated font (revision 4) should be used.
@@ -239,15 +262,30 @@
}
@Test
+ public void construct_failedToLoadConfig() throws Exception {
+ FakeFontFileParser parser = new FakeFontFileParser();
+ FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
+ UpdatableFontDir dir = new UpdatableFontDir(
+ mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
+ new File("/dev/null"));
+ assertThat(dir.getFontFileMap()).isEmpty();
+ }
+
+ @Test
public void installFontFile() throws Exception {
FakeFontFileParser parser = new FakeFontFileParser();
FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
UpdatableFontDir dir = new UpdatableFontDir(
- mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil);
+ mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
+ mConfigFile);
installFontFile(dir, "test,1", GOOD_SIGNATURE);
assertThat(dir.getFontFileMap()).containsKey("test.ttf");
assertThat(parser.getRevision(dir.getFontFileMap().get("test.ttf"))).isEqualTo(1);
+ File fontFile = dir.getFontFileMap().get("test.ttf");
+ assertThat(Os.stat(fontFile.getAbsolutePath()).st_mode & 0777).isEqualTo(0644);
+ File fontDir = fontFile.getParentFile();
+ assertThat(Os.stat(fontDir.getAbsolutePath()).st_mode & 0777).isEqualTo(0711);
}
@Test
@@ -255,7 +293,8 @@
FakeFontFileParser parser = new FakeFontFileParser();
FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
UpdatableFontDir dir = new UpdatableFontDir(
- mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil);
+ mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
+ mConfigFile);
installFontFile(dir, "test,1", GOOD_SIGNATURE);
Map<String, File> mapBeforeUpgrade = dir.getFontFileMap();
@@ -272,14 +311,15 @@
FakeFontFileParser parser = new FakeFontFileParser();
FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
UpdatableFontDir dir = new UpdatableFontDir(
- mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil);
+ mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
+ mConfigFile);
installFontFile(dir, "test,2", GOOD_SIGNATURE);
try {
installFontFile(dir, "test,1", GOOD_SIGNATURE);
fail("Expect IllegalArgumentException");
- } catch (IllegalArgumentException e) {
- // Expect
+ } catch (FontManagerService.SystemFontException e) {
+ assertThat(e.getErrorCode()).isEqualTo(FontManager.ERROR_CODE_DOWNGRADING);
}
assertThat(dir.getFontFileMap()).containsKey("test.ttf");
assertWithMessage("Font should not be downgraded to an older revision")
@@ -291,7 +331,8 @@
FakeFontFileParser parser = new FakeFontFileParser();
FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
UpdatableFontDir dir = new UpdatableFontDir(
- mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil);
+ mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
+ mConfigFile);
installFontFile(dir, "foo,1", GOOD_SIGNATURE);
installFontFile(dir, "bar,2", GOOD_SIGNATURE);
@@ -302,17 +343,19 @@
}
@Test
- public void installFontFile_invalidSignature() {
+ public void installFontFile_invalidSignature() throws Exception {
FakeFontFileParser parser = new FakeFontFileParser();
FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
UpdatableFontDir dir = new UpdatableFontDir(
- mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil);
+ mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
+ mConfigFile);
try {
installFontFile(dir, "test,1", "Invalid signature");
- fail("Expect IOException");
- } catch (IOException e) {
- // Expect
+ fail("Expect SystemFontException");
+ } catch (FontManagerService.SystemFontException e) {
+ assertThat(e.getErrorCode())
+ .isEqualTo(FontManager.ERROR_CODE_VERIFICATION_FAILURE);
}
assertThat(dir.getFontFileMap()).isEmpty();
}
@@ -323,23 +366,155 @@
FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(0), "test.ttf"), "test,1");
UpdatableFontDir dir = new UpdatableFontDir(
- mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil);
+ mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
+ mConfigFile);
try {
installFontFile(dir, "test,1", GOOD_SIGNATURE);
fail("Expect IllegalArgumentException");
- } catch (IllegalArgumentException e) {
- // Expect
+ } catch (FontManagerService.SystemFontException e) {
+ assertThat(e.getErrorCode()).isEqualTo(FontManager.ERROR_CODE_DOWNGRADING);
+ }
+ assertThat(dir.getFontFileMap()).isEmpty();
+ }
+
+ @Test
+ public void installFontFile_failedToWriteConfigXml() throws Exception {
+ long expectedModifiedDate = 1234567890;
+ FakeFontFileParser parser = new FakeFontFileParser();
+ FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
+ FileUtils.stringToFile(new File(mPreinstalledFontDirs.get(0), "test.ttf"), "test,1");
+
+ File readonlyDir = new File(mCacheDir, "readonly");
+ assertThat(readonlyDir.mkdir()).isTrue();
+ File readonlyFile = new File(readonlyDir, "readonly_config.xml");
+
+ PersistentSystemFontConfig.Config config = new PersistentSystemFontConfig.Config();
+ config.lastModifiedDate = expectedModifiedDate;
+ writeConfig(config, readonlyFile);
+
+ assertThat(readonlyDir.setWritable(false, false)).isTrue();
+ try {
+ UpdatableFontDir dir = new UpdatableFontDir(
+ mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
+ readonlyFile);
+
+ try {
+ installFontFile(dir, "test,2", GOOD_SIGNATURE);
+ } catch (FontManagerService.SystemFontException e) {
+ assertThat(e.getErrorCode())
+ .isEqualTo(FontManager.ERROR_CODE_FAILED_TO_CREATE_CONFIG_FILE);
+ }
+ assertThat(dir.getSystemFontConfig().getLastModifiedDate())
+ .isEqualTo(expectedModifiedDate);
+ assertThat(dir.getFontFileMap()).isEmpty();
+ } finally {
+ assertThat(readonlyDir.setWritable(true, true)).isTrue();
+ }
+ }
+
+ @Test
+ public void installFontFile_failedToParsePostScript() throws Exception {
+ FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
+ UpdatableFontDir dir = new UpdatableFontDir(
+ mUpdatableFontFilesDir, mPreinstalledFontDirs,
+ new UpdatableFontDir.FontFileParser() {
+ @Override
+ public String getPostScriptName(File file) throws IOException {
+ return null;
+ }
+
+ @Override
+ public long getRevision(File file) throws IOException {
+ return 0;
+ }
+ }, fakeFsverityUtil, mConfigFile);
+
+ try {
+ installFontFile(dir, "foo,1", GOOD_SIGNATURE);
+ fail("Expect SystemFontException");
+ } catch (FontManagerService.SystemFontException e) {
+ assertThat(e.getErrorCode())
+ .isEqualTo(FontManager.ERROR_CODE_MISSING_POST_SCRIPT_NAME);
+ }
+ assertThat(dir.getFontFileMap()).isEmpty();
+ }
+
+ @Test
+ public void installFontFile_failedToParsePostScriptName_invalidFont() throws Exception {
+ FakeFsverityUtil fakeFsverityUtil = new FakeFsverityUtil();
+ UpdatableFontDir dir = new UpdatableFontDir(
+ mUpdatableFontFilesDir, mPreinstalledFontDirs,
+ new UpdatableFontDir.FontFileParser() {
+ @Override
+ public String getPostScriptName(File file) throws IOException {
+ throw new IOException();
+ }
+
+ @Override
+ public long getRevision(File file) throws IOException {
+ return 0;
+ }
+ }, fakeFsverityUtil, mConfigFile);
+
+ try {
+ installFontFile(dir, "foo,1", GOOD_SIGNATURE);
+ fail("Expect SystemFontException");
+ } catch (FontManagerService.SystemFontException e) {
+ assertThat(e.getErrorCode())
+ .isEqualTo(FontManager.ERROR_CODE_INVALID_FONT_FILE);
+ }
+ assertThat(dir.getFontFileMap()).isEmpty();
+ }
+
+ @Test
+ public void installFontFile_renameToPsNameFailure() throws Exception {
+ UpdatableFontDir.FsverityUtil fakeFsverityUtil = new UpdatableFontDir.FsverityUtil() {
+ private final FakeFsverityUtil mFake = new FakeFsverityUtil();
+
+ @Override
+ public boolean hasFsverity(String path) {
+ return mFake.hasFsverity(path);
+ }
+
+ @Override
+ public void setUpFsverity(String path, byte[] pkcs7Signature) throws IOException {
+ mFake.setUpFsverity(path, pkcs7Signature);
+ }
+
+ @Override
+ public boolean rename(File src, File dest) {
+ return false;
+ }
+ };
+ FakeFontFileParser parser = new FakeFontFileParser();
+ UpdatableFontDir dir = new UpdatableFontDir(
+ mUpdatableFontFilesDir, mPreinstalledFontDirs, parser, fakeFsverityUtil,
+ mConfigFile);
+
+ try {
+ installFontFile(dir, "foo,1", GOOD_SIGNATURE);
+ fail("Expect SystemFontException");
+ } catch (FontManagerService.SystemFontException e) {
+ assertThat(e.getErrorCode())
+ .isEqualTo(FontManager.ERROR_CODE_FAILED_TO_WRITE_FONT_FILE);
}
assertThat(dir.getFontFileMap()).isEmpty();
}
private void installFontFile(UpdatableFontDir dir, String content, String signature)
- throws IOException {
+ throws Exception {
File file = File.createTempFile("font", "ttf", mCacheDir);
FileUtils.stringToFile(file, content);
try (FileInputStream in = new FileInputStream(file)) {
dir.installFontFile(in.getFD(), signature.getBytes());
}
}
+
+ private void writeConfig(PersistentSystemFontConfig.Config config,
+ File file) throws IOException {
+ try (FileOutputStream fos = new FileOutputStream(file)) {
+ PersistentSystemFontConfig.writeToXml(fos, config);
+ }
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/location/timezone/OWNERS b/services/tests/servicestests/src/com/android/server/location/timezone/OWNERS
deleted file mode 100644
index 28aff18..0000000
--- a/services/tests/servicestests/src/com/android/server/location/timezone/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-# Bug component: 847766
-nfuller@google.com
-include /core/java/android/app/timedetector/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java
index 1581d9a..691d174 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTestable.java
@@ -82,6 +82,11 @@
}
@Override
+ String getRebootEscrowServerBlob() {
+ return makeDirs(mStorageDir, super.getRebootEscrowServerBlob()).getAbsolutePath();
+ }
+
+ @Override
protected File getSyntheticPasswordDirectoryForUser(int userId) {
return makeDirs(mStorageDir, super.getSyntheticPasswordDirectoryForUser(
userId).getAbsolutePath());
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
index f74e45b..a4ba4c8 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java
@@ -26,6 +26,7 @@
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doNothing;
@@ -52,6 +53,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.widget.RebootEscrowListener;
+import com.android.server.locksettings.ResumeOnRebootServiceProvider.ResumeOnRebootServiceConnection;
import org.junit.Before;
import org.junit.Test;
@@ -92,6 +94,7 @@
private UserManager mUserManager;
private RebootEscrowManager.Callbacks mCallbacks;
private IRebootEscrow mRebootEscrow;
+ private ResumeOnRebootServiceConnection mServiceConnection;
private RebootEscrowKeyStoreManager mKeyStoreManager;
LockSettingsStorageTestable mStorage;
@@ -108,6 +111,7 @@
static class MockInjector extends RebootEscrowManager.Injector {
private final IRebootEscrow mRebootEscrow;
+ private final ResumeOnRebootServiceConnection mServiceConnection;
private final RebootEscrowProviderInterface mRebootEscrowProvider;
private final UserManager mUserManager;
private final MockableRebootEscrowInjected mInjected;
@@ -116,10 +120,11 @@
MockInjector(Context context, UserManager userManager,
IRebootEscrow rebootEscrow,
RebootEscrowKeyStoreManager keyStoreManager,
+ LockSettingsStorageTestable storage,
MockableRebootEscrowInjected injected) {
- super(context);
+ super(context, storage);
mRebootEscrow = rebootEscrow;
-
+ mServiceConnection = null;
RebootEscrowProviderHalImpl.Injector halInjector =
new RebootEscrowProviderHalImpl.Injector() {
@Override
@@ -133,6 +138,22 @@
mInjected = injected;
}
+ MockInjector(Context context, UserManager userManager,
+ ResumeOnRebootServiceConnection serviceConnection,
+ RebootEscrowKeyStoreManager keyStoreManager,
+ LockSettingsStorageTestable storage,
+ MockableRebootEscrowInjected injected) {
+ super(context, storage);
+ mServiceConnection = serviceConnection;
+ mRebootEscrow = null;
+ RebootEscrowProviderServerBasedImpl.Injector injector =
+ new RebootEscrowProviderServerBasedImpl.Injector(serviceConnection);
+ mRebootEscrowProvider = new RebootEscrowProviderServerBasedImpl(storage, injector);
+ mUserManager = userManager;
+ mKeyStoreManager = keyStoreManager;
+ mInjected = injected;
+ }
+
@Override
public UserManager getUserManager() {
return mUserManager;
@@ -165,6 +186,7 @@
mUserManager = mock(UserManager.class);
mCallbacks = mock(RebootEscrowManager.Callbacks.class);
mRebootEscrow = mock(IRebootEscrow.class);
+ mServiceConnection = mock(ResumeOnRebootServiceConnection.class);
mKeyStoreManager = mock(RebootEscrowKeyStoreManager.class);
mAesKey = new SecretKeySpec(TEST_AES_KEY, "AES");
@@ -186,7 +208,12 @@
when(mCallbacks.isUserSecure(SECURE_SECONDARY_USER_ID)).thenReturn(true);
mInjected = mock(MockableRebootEscrowInjected.class);
mService = new RebootEscrowManager(new MockInjector(mContext, mUserManager, mRebootEscrow,
- mKeyStoreManager, mInjected), mCallbacks, mStorage);
+ mKeyStoreManager, mStorage, mInjected), mCallbacks, mStorage);
+ }
+
+ private void setServerBasedRebootEscrowProvider() throws Exception {
+ mService = new RebootEscrowManager(new MockInjector(mContext, mUserManager,
+ mServiceConnection, mKeyStoreManager, mStorage, mInjected), mCallbacks, mStorage);
}
@Test
@@ -202,6 +229,19 @@
}
@Test
+ public void prepareRebootEscrowServerBased_Success() throws Exception {
+ setServerBasedRebootEscrowProvider();
+ RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
+ mService.setRebootEscrowListener(mockListener);
+ mService.prepareRebootEscrow();
+
+ mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN);
+ verify(mockListener).onPreparedForReboot(eq(true));
+ verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong());
+ assertFalse(mStorage.hasRebootEscrowServerBlob());
+ }
+
+ @Test
public void prepareRebootEscrow_ClearCredentials_Success() throws Exception {
RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
mService.setRebootEscrowListener(mockListener);
@@ -246,6 +286,28 @@
}
@Test
+ public void armServiceServerBased_Success() throws Exception {
+ setServerBasedRebootEscrowProvider();
+ RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
+ mService.setRebootEscrowListener(mockListener);
+ mService.prepareRebootEscrow();
+
+ clearInvocations(mServiceConnection);
+ mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN);
+ verify(mockListener).onPreparedForReboot(eq(true));
+ verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong());
+
+ when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong()))
+ .thenAnswer(invocation -> invocation.getArgument(0));
+ assertTrue(mService.armRebootEscrowIfNeeded());
+ verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong());
+
+ assertTrue(mStorage.hasRebootEscrow(PRIMARY_USER_ID));
+ assertFalse(mStorage.hasRebootEscrow(NONSECURE_SECONDARY_USER_ID));
+ assertTrue(mStorage.hasRebootEscrowServerBlob());
+ }
+
+ @Test
public void armService_HalFailure_NonFatal() throws Exception {
RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
mService.setRebootEscrowListener(mockListener);
@@ -346,6 +408,40 @@
}
@Test
+ public void loadRebootEscrowDataIfAvailable_ServerBased_Success() throws Exception {
+ setServerBasedRebootEscrowProvider();
+
+ when(mInjected.getBootCount()).thenReturn(0);
+ RebootEscrowListener mockListener = mock(RebootEscrowListener.class);
+ mService.setRebootEscrowListener(mockListener);
+ mService.prepareRebootEscrow();
+
+ clearInvocations(mServiceConnection);
+ mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN);
+ verify(mockListener).onPreparedForReboot(eq(true));
+ verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong());
+
+ // Use x -> x for both wrap & unwrap functions.
+ when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong()))
+ .thenAnswer(invocation -> invocation.getArgument(0));
+ assertTrue(mService.armRebootEscrowIfNeeded());
+ verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong());
+ assertTrue(mStorage.hasRebootEscrowServerBlob());
+
+ // pretend reboot happens here
+ when(mInjected.getBootCount()).thenReturn(1);
+ ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class);
+ doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture());
+
+ when(mServiceConnection.unwrap(any(), anyLong()))
+ .thenAnswer(invocation -> invocation.getArgument(0));
+ mService.loadRebootEscrowDataIfAvailable();
+ verify(mServiceConnection).unwrap(any(), anyLong());
+ assertTrue(metricsSuccessCaptor.getValue());
+ verify(mKeyStoreManager).clearKeyStoreEncryptionKey();
+ }
+
+ @Test
public void loadRebootEscrowDataIfAvailable_TooManyBootsInBetween_NoMetrics() throws Exception {
when(mInjected.getBootCount()).thenReturn(0);
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowProviderServerBasedImplTests.java b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowProviderServerBasedImplTests.java
new file mode 100644
index 0000000..bc1e025
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowProviderServerBasedImplTests.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.locksettings;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.stubbing.Answer;
+
+import java.io.File;
+import java.io.IOException;
+
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class RebootEscrowProviderServerBasedImplTests {
+ private SecretKey mKeyStoreEncryptionKey;
+ private RebootEscrowKey mRebootEscrowKey;
+ private ResumeOnRebootServiceProvider.ResumeOnRebootServiceConnection mServiceConnection;
+ private LockSettingsStorageTestable mStorage;
+ private RebootEscrowProviderServerBasedImpl mRebootEscrowProvider;
+ private Answer<byte[]> mFakeEncryption;
+
+ private static final byte[] TEST_AES_KEY = new byte[] {
+ 0x48, 0x19, 0x12, 0x54, 0x13, 0x13, 0x52, 0x31,
+ 0x44, 0x74, 0x61, 0x54, 0x29, 0x74, 0x37, 0x61,
+ 0x70, 0x70, 0x75, 0x25, 0x27, 0x31, 0x49, 0x09,
+ 0x26, 0x52, 0x72, 0x63, 0x63, 0x61, 0x78, 0x23,
+ };
+
+ @Before
+ public void setUp() throws Exception {
+ mKeyStoreEncryptionKey = new SecretKeySpec(TEST_AES_KEY, "AES");
+ mRebootEscrowKey = RebootEscrowKey.generate();
+ mServiceConnection = mock(
+ ResumeOnRebootServiceProvider.ResumeOnRebootServiceConnection.class);
+
+ Context context = new ContextWrapper(InstrumentationRegistry.getContext());
+ mStorage = new LockSettingsStorageTestable(context,
+ new File(InstrumentationRegistry.getContext().getFilesDir(), "locksettings"));
+ mRebootEscrowProvider = new RebootEscrowProviderServerBasedImpl(mStorage,
+ new RebootEscrowProviderServerBasedImpl.Injector(mServiceConnection));
+
+ mFakeEncryption = invocation -> {
+ byte[] secret = invocation.getArgument(0);
+ for (int i = 0; i < secret.length; i++) {
+ secret[i] = (byte) (secret[i] ^ 0xf);
+ }
+ return secret;
+ };
+ }
+
+ @Test
+ public void getAndClearRebootEscrowKey_loopback_success() throws Exception {
+ when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong())).thenAnswer(mFakeEncryption);
+ when(mServiceConnection.unwrap(any(), anyLong())).thenAnswer(mFakeEncryption);
+
+ assertTrue(mRebootEscrowProvider.hasRebootEscrowSupport());
+ mRebootEscrowProvider.storeRebootEscrowKey(mRebootEscrowKey, mKeyStoreEncryptionKey);
+ assertTrue(mStorage.hasRebootEscrowServerBlob());
+
+
+ RebootEscrowKey ks = mRebootEscrowProvider.getAndClearRebootEscrowKey(
+ mKeyStoreEncryptionKey);
+ assertThat(ks.getKeyBytes(), is(mRebootEscrowKey.getKeyBytes()));
+ assertFalse(mStorage.hasRebootEscrowServerBlob());
+ }
+
+ @Test
+ public void getAndClearRebootEscrowKey_WrongDecryptionMethod_failure() throws Exception {
+ when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong())).thenAnswer(mFakeEncryption);
+ when(mServiceConnection.unwrap(any(), anyLong())).thenAnswer(
+ invocation -> {
+ byte[] secret = invocation.getArgument(0);
+ for (int i = 0; i < secret.length; i++) {
+ secret[i] = (byte) (secret[i] ^ 0xe);
+ }
+ return secret;
+ }
+ );
+
+ assertTrue(mRebootEscrowProvider.hasRebootEscrowSupport());
+ mRebootEscrowProvider.storeRebootEscrowKey(mRebootEscrowKey, mKeyStoreEncryptionKey);
+ assertTrue(mStorage.hasRebootEscrowServerBlob());
+
+ // Expect to get wrong key bytes
+ RebootEscrowKey ks = mRebootEscrowProvider.getAndClearRebootEscrowKey(
+ mKeyStoreEncryptionKey);
+ assertNotEquals(ks.getKeyBytes(), mRebootEscrowKey.getKeyBytes());
+ assertFalse(mStorage.hasRebootEscrowServerBlob());
+ }
+
+ @Test
+ public void getAndClearRebootEscrowKey_ServiceConnectionException_failure() throws Exception {
+ when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong())).thenAnswer(mFakeEncryption);
+ doThrow(IOException.class).when(mServiceConnection).unwrap(any(), anyLong());
+
+ assertTrue(mRebootEscrowProvider.hasRebootEscrowSupport());
+ mRebootEscrowProvider.storeRebootEscrowKey(mRebootEscrowKey, mKeyStoreEncryptionKey);
+ assertTrue(mStorage.hasRebootEscrowServerBlob());
+
+ // Expect to get null key bytes when the server service fails to unwrap the blob.
+ RebootEscrowKey ks = mRebootEscrowProvider.getAndClearRebootEscrowKey(
+ mKeyStoreEncryptionKey);
+ assertNull(ks);
+ assertFalse(mStorage.hasRebootEscrowServerBlob());
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java
index eaf62cb..0dcd608 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexMetadataHelperTest.java
@@ -23,12 +23,11 @@
import android.content.Context;
import android.content.pm.PackageManager;
-import android.content.pm.PackageParser;
-import android.content.pm.PackageParser.ApkLite;
-import android.content.pm.PackageParser.PackageLite;
import android.content.pm.PackageParser.PackageParserException;
import android.content.pm.dex.DexMetadataHelper;
+import android.content.pm.parsing.ApkLite;
import android.content.pm.parsing.ApkLiteParseUtils;
+import android.content.pm.parsing.PackageLite;
import android.content.pm.parsing.result.ParseResult;
import android.content.pm.parsing.result.ParseTypeImpl;
import android.os.FileUtils;
@@ -38,6 +37,7 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.frameworks.servicestests.R;
+import com.android.server.pm.PackageManagerException;
import com.android.server.pm.parsing.TestPackageParser2;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
import com.android.server.pm.parsing.pkg.ParsedPackage;
@@ -207,28 +207,35 @@
}
@Test
- public void testPackageSizeWithDmFile()
- throws IOException, PackageParserException {
+ public void testPackageSizeWithDmFile() throws IOException {
copyApkToToTmpDir("install_split_base.apk", R.raw.install_split_base);
- File dm = createDexMetadataFile("install_split_base.apk");
- ParseResult<PackageLite> result = ApkLiteParseUtils.parsePackageLite(
+ final File dm = createDexMetadataFile("install_split_base.apk");
+ final ParseResult<PackageLite> result = ApkLiteParseUtils.parsePackageLite(
ParseTypeImpl.forDefaultParsing().reset(), mTmpDir, 0 /* flags */);
if (result.isError()) {
throw new IllegalStateException(result.getErrorMessage(), result.getException());
}
- PackageParser.PackageLite pkg = result.getResult();
+ final PackageLite pkg = result.getResult();
Assert.assertEquals(dm.length(), DexMetadataHelper.getPackageDexMetadataSize(pkg));
}
// This simulates the 'adb shell pm install' flow.
@Test
- public void testPackageSizeWithPartialPackageLite() throws IOException, PackageParserException {
- File base = copyApkToToTmpDir("install_split_base", R.raw.install_split_base);
- File dm = createDexMetadataFile("install_split_base.apk");
+ public void testPackageSizeWithPartialPackageLite() throws IOException,
+ PackageManagerException {
+ final File base = copyApkToToTmpDir("install_split_base", R.raw.install_split_base);
+ final File dm = createDexMetadataFile("install_split_base.apk");
try (FileInputStream is = new FileInputStream(base)) {
- ApkLite baseApk = PackageParser.parseApkLite(is.getFD(), base.getAbsolutePath(), 0);
- PackageLite pkgLite = new PackageLite(null, baseApk.codePath, baseApk, null, null, null,
- null, null, null);
+ final ParseResult<ApkLite> result = ApkLiteParseUtils.parseApkLite(
+ ParseTypeImpl.forDefaultParsing().reset(), is.getFD(),
+ base.getAbsolutePath(), /* flags */ 0);
+ if (result.isError()) {
+ throw new PackageManagerException(result.getErrorCode(),
+ result.getErrorMessage(), result.getException());
+ }
+ final ApkLite baseApk = result.getResult();
+ final PackageLite pkgLite = new PackageLite(null, baseApk.getPath(), baseApk, null,
+ null, null, null, null, null);
Assert.assertEquals(dm.length(), DexMetadataHelper.getPackageDexMetadataSize(pkgLite));
}
diff --git a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
index b6ae855..84b690f 100644
--- a/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java
@@ -67,6 +67,7 @@
private static final String RESIDENCY_FILENAME = "residencytest";
private static final String PROTO_OUTPUT_FILENAME = "powerstats.proto";
private static final String CHANNEL_NAME = "channelname";
+ private static final String CHANNEL_SUBSYSTEM = "channelsubsystem";
private static final String POWER_ENTITY_NAME = "powerentityinfo";
private static final String STATE_NAME = "stateinfo";
private static final String ENERGY_CONSUMER_NAME = "energyconsumer";
@@ -214,6 +215,7 @@
energyMeterList[i] = new Channel();
energyMeterList[i].id = i;
energyMeterList[i].name = new String(CHANNEL_NAME + i);
+ energyMeterList[i].subsystem = new String(CHANNEL_SUBSYSTEM + i);
}
return energyMeterList;
}
@@ -272,6 +274,7 @@
for (int i = 0; i < pssProto.channel.length; i++) {
assertTrue(pssProto.channel[i].id == i);
assertTrue(pssProto.channel[i].name.equals(CHANNEL_NAME + i));
+ assertTrue(pssProto.channel[i].subsystem.equals(CHANNEL_SUBSYSTEM + i));
}
// Validate the energyMeasurement array matches what was written to on-device storage.
@@ -414,6 +417,7 @@
for (int i = 0; i < pssProto.channel.length; i++) {
assertTrue(pssProto.channel[i].id == i);
assertTrue(pssProto.channel[i].name.equals(CHANNEL_NAME + i));
+ assertTrue(pssProto.channel[i].subsystem.equals(CHANNEL_SUBSYSTEM + i));
}
// No energyMeasurements should be written to the incident report since it
@@ -547,6 +551,7 @@
for (int i = 0; i < pssProto.channel.length; i++) {
assertTrue(pssProto.channel[i].id == i);
assertTrue(pssProto.channel[i].name.equals(CHANNEL_NAME + i));
+ assertTrue(pssProto.channel[i].subsystem.equals(CHANNEL_SUBSYSTEM + i));
}
// No energyMeasurements should be written to the incident report since the
diff --git a/services/tests/servicestests/src/com/android/server/location/timezone/ControllerImplTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/location/ControllerImplTest.java
similarity index 97%
rename from services/tests/servicestests/src/com/android/server/location/timezone/ControllerImplTest.java
rename to services/tests/servicestests/src/com/android/server/timezonedetector/location/ControllerImplTest.java
index 972b3bb..4284240 100644
--- a/services/tests/servicestests/src/com/android/server/location/timezone/ControllerImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/location/ControllerImplTest.java
@@ -13,18 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.server.location.timezone;
+package com.android.server.timezonedetector.location;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_UNCERTAIN;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STOPPED;
-import static com.android.server.location.timezone.TestSupport.USER1_CONFIG_GEO_DETECTION_DISABLED;
-import static com.android.server.location.timezone.TestSupport.USER1_CONFIG_GEO_DETECTION_ENABLED;
-import static com.android.server.location.timezone.TestSupport.USER2_CONFIG_GEO_DETECTION_ENABLED;
-import static com.android.server.location.timezone.TimeZoneProviderEvent.createPermanentFailureEvent;
-import static com.android.server.location.timezone.TimeZoneProviderEvent.createUncertainEvent;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_UNCERTAIN;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STOPPED;
+import static com.android.server.timezonedetector.location.TestSupport.USER1_CONFIG_GEO_DETECTION_DISABLED;
+import static com.android.server.timezonedetector.location.TestSupport.USER1_CONFIG_GEO_DETECTION_ENABLED;
+import static com.android.server.timezonedetector.location.TestSupport.USER2_CONFIG_GEO_DETECTION_ENABLED;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -40,10 +38,10 @@
import android.service.timezone.TimeZoneProviderSuggestion;
import android.util.IndentingPrintWriter;
-import com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.ProviderStateEnum;
import com.android.server.timezonedetector.ConfigurationInternal;
import com.android.server.timezonedetector.GeolocationTimeZoneSuggestion;
import com.android.server.timezonedetector.TestState;
+import com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.ProviderStateEnum;
import org.junit.Before;
import org.junit.Test;
@@ -66,9 +64,9 @@
private static final TimeZoneProviderEvent USER1_SUCCESS_LOCATION_TIME_ZONE_EVENT2 =
createSuggestionEvent(asList("Europe/Paris"));
private static final TimeZoneProviderEvent USER1_UNCERTAIN_LOCATION_TIME_ZONE_EVENT =
- createUncertainEvent();
+ TimeZoneProviderEvent.createUncertainEvent();
private static final TimeZoneProviderEvent USER1_PERM_FAILURE_LOCATION_TIME_ZONE_EVENT =
- createPermanentFailureEvent("Test");
+ TimeZoneProviderEvent.createPermanentFailureEvent("Test");
private TestThreadingDomain mTestThreadingDomain;
private TestCallback mTestCallback;
diff --git a/services/tests/servicestests/src/com/android/server/location/timezone/HandlerThreadingDomainTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/location/HandlerThreadingDomainTest.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/location/timezone/HandlerThreadingDomainTest.java
rename to services/tests/servicestests/src/com/android/server/timezonedetector/location/HandlerThreadingDomainTest.java
index 02de24d..e7dd9794 100644
--- a/services/tests/servicestests/src/com/android/server/location/timezone/HandlerThreadingDomainTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/location/HandlerThreadingDomainTest.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.server.location.timezone;
+package com.android.server.timezonedetector.location;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertSame;
@@ -24,7 +24,7 @@
import android.os.HandlerThread;
import android.platform.test.annotations.Presubmit;
-import com.android.server.location.timezone.ThreadingDomain.SingleRunnableQueue;
+import com.android.server.timezonedetector.location.ThreadingDomain.SingleRunnableQueue;
import org.junit.After;
import org.junit.Before;
diff --git a/services/tests/servicestests/src/com/android/server/location/timezone/LocationTimeZoneProviderTest.java b/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderTest.java
similarity index 92%
rename from services/tests/servicestests/src/com/android/server/location/timezone/LocationTimeZoneProviderTest.java
rename to services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderTest.java
index cb292db..095c868 100644
--- a/services/tests/servicestests/src/com/android/server/location/timezone/LocationTimeZoneProviderTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/location/LocationTimeZoneProviderTest.java
@@ -13,17 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.server.location.timezone;
+package com.android.server.timezonedetector.location;
import static android.service.timezone.TimeZoneProviderService.TEST_COMMAND_RESULT_ERROR_KEY;
import static android.service.timezone.TimeZoneProviderService.TEST_COMMAND_RESULT_SUCCESS_KEY;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_UNCERTAIN;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STOPPED;
-import static com.android.server.location.timezone.TestSupport.USER1_CONFIG_GEO_DETECTION_ENABLED;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_UNCERTAIN;
+import static com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STOPPED;
+import static com.android.server.timezonedetector.location.TestSupport.USER1_CONFIG_GEO_DETECTION_ENABLED;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -40,10 +40,10 @@
import android.service.timezone.TimeZoneProviderSuggestion;
import android.util.IndentingPrintWriter;
-import com.android.server.location.timezone.LocationTimeZoneProvider.ProviderListener;
-import com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState;
import com.android.server.timezonedetector.ConfigurationInternal;
import com.android.server.timezonedetector.TestState;
+import com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderListener;
+import com.android.server.timezonedetector.location.LocationTimeZoneProvider.ProviderState;
import org.junit.Before;
import org.junit.Test;
diff --git a/services/tests/servicestests/src/com/android/server/location/timezone/TestSupport.java b/services/tests/servicestests/src/com/android/server/timezonedetector/location/TestSupport.java
similarity index 96%
rename from services/tests/servicestests/src/com/android/server/location/timezone/TestSupport.java
rename to services/tests/servicestests/src/com/android/server/timezonedetector/location/TestSupport.java
index 4810563..d319488 100644
--- a/services/tests/servicestests/src/com/android/server/location/timezone/TestSupport.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/location/TestSupport.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.server.location.timezone;
+package com.android.server.timezonedetector.location;
import android.annotation.UserIdInt;
diff --git a/services/tests/servicestests/src/com/android/server/location/timezone/TestThreadingDomain.java b/services/tests/servicestests/src/com/android/server/timezonedetector/location/TestThreadingDomain.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/location/timezone/TestThreadingDomain.java
rename to services/tests/servicestests/src/com/android/server/timezonedetector/location/TestThreadingDomain.java
index b1a5ff9..e08fea0 100644
--- a/services/tests/servicestests/src/com/android/server/location/timezone/TestThreadingDomain.java
+++ b/services/tests/servicestests/src/com/android/server/timezonedetector/location/TestThreadingDomain.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package com.android.server.location.timezone;
+package com.android.server.timezonedetector.location;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
index 37fb0e9..82ffa765 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -123,15 +123,6 @@
updateDisplayFrames();
}
- void addWindowWithRawInsetsState(WindowState win) {
- addWindow(win);
- // Without mPerformLayout in display content, the window cannot see any insets. Override the
- // insets state with the global one.
- final InsetsState insetsState =
- win.getDisplayContent().getInsetsStateController().getRawInsetsState();
- win.mAboveInsetsState = insetsState;
- }
-
public void setRotation(int rotation, boolean includingWindows) {
mRotation = rotation;
updateDisplayFrames();
@@ -281,7 +272,7 @@
@Test
public void layoutWindowLw_fitStatusBars() {
mWindow.mAttrs.setFitInsetsTypes(Type.statusBars());
- addWindowWithRawInsetsState(mWindow);
+ addWindow(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -292,7 +283,7 @@
@Test
public void layoutWindowLw_fitNavigationBars() {
mWindow.mAttrs.setFitInsetsTypes(Type.navigationBars());
- addWindowWithRawInsetsState(mWindow);
+ addWindow(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -303,7 +294,7 @@
@Test
public void layoutWindowLw_fitAllSides() {
mWindow.mAttrs.setFitInsetsSides(Side.all());
- addWindowWithRawInsetsState(mWindow);
+ addWindow(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -314,7 +305,7 @@
@Test
public void layoutWindowLw_fitTopOnly() {
mWindow.mAttrs.setFitInsetsSides(Side.TOP);
- addWindowWithRawInsetsState(mWindow);
+ addWindow(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -324,12 +315,11 @@
@Test
public void layoutWindowLw_fitInsetsIgnoringVisibility() {
- final InsetsState state =
- mDisplayContent.getInsetsStateController().getRawInsetsState();
+ final InsetsState state = mWindow.getInsetsState();
state.getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false);
state.getSource(InsetsState.ITYPE_NAVIGATION_BAR).setVisible(false);
mWindow.mAttrs.setFitInsetsIgnoringVisibility(true);
- addWindowWithRawInsetsState(mWindow);
+ addWindow(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -339,12 +329,11 @@
@Test
public void layoutWindowLw_fitInsetsNotIgnoringVisibility() {
- final InsetsState state =
- mDisplayContent.getInsetsStateController().getRawInsetsState();
+ final InsetsState state = mWindow.getInsetsState();
state.getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false);
state.getSource(InsetsState.ITYPE_NAVIGATION_BAR).setVisible(false);
mWindow.mAttrs.setFitInsetsIgnoringVisibility(false);
- addWindowWithRawInsetsState(mWindow);
+ addWindow(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -360,7 +349,8 @@
state.getSource(InsetsState.ITYPE_IME).setFrame(
0, DISPLAY_HEIGHT - IME_HEIGHT, DISPLAY_WIDTH, DISPLAY_HEIGHT);
mWindow.mAttrs.privateFlags |= PRIVATE_FLAG_INSET_PARENT_FRAME_BY_IME;
- addWindowWithRawInsetsState(mWindow);
+ mWindow.mBehindIme = true;
+ addWindow(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -374,7 +364,7 @@
mWindow.mAttrs.setFitInsetsTypes(Type.displayCutout());
mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
- addWindowWithRawInsetsState(mWindow);
+ addWindow(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -389,7 +379,7 @@
mWindow.mAttrs.flags =
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
- addWindowWithRawInsetsState(mWindow);
+ addWindow(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -405,7 +395,7 @@
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
- addWindowWithRawInsetsState(mWindow);
+ addWindow(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -421,7 +411,7 @@
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
- addWindowWithRawInsetsState(mWindow);
+ addWindow(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -437,7 +427,7 @@
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
- addWindowWithRawInsetsState(mWindow);
+ addWindow(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -452,7 +442,7 @@
mWindow.mAttrs.flags =
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
- addWindowWithRawInsetsState(mWindow);
+ addWindow(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -467,12 +457,11 @@
mWindow.mAttrs.flags =
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
- mDisplayContent.getInsetsStateController().getRawInsetsState()
- .getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false);
+ mWindow.getInsetsState().getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false);
final InsetsState requestedState = new InsetsState();
requestedState.getSource(ITYPE_STATUS_BAR).setVisible(false);
mWindow.updateRequestedVisibility(requestedState);
- addWindowWithRawInsetsState(mWindow);
+ addWindow(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -487,13 +476,12 @@
mWindow.mAttrs.flags =
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
- mDisplayContent.getInsetsStateController().getRawInsetsState()
- .getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false);
+ mWindow.getInsetsState().getSource(InsetsState.ITYPE_STATUS_BAR).setVisible(false);
final InsetsState requestedState = new InsetsState();
requestedState.getSource(ITYPE_STATUS_BAR).setVisible(false);
mWindow.updateRequestedVisibility(requestedState);
mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
- addWindowWithRawInsetsState(mWindow);
+ addWindow(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -509,7 +497,7 @@
mWindow.mAttrs.flags =
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
- addWindowWithRawInsetsState(mWindow);
+ addWindow(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -525,7 +513,7 @@
mWindow.mAttrs.flags =
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
- addWindowWithRawInsetsState(mWindow);
+ addWindow(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -541,7 +529,7 @@
mWindow.mAttrs.flags =
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
- addWindowWithRawInsetsState(mWindow);
+ addWindow(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -557,7 +545,7 @@
mWindow.mAttrs.type = TYPE_APPLICATION_OVERLAY;
mWindow.mAttrs.width = DISPLAY_WIDTH;
mWindow.mAttrs.height = DISPLAY_HEIGHT;
- addWindowWithRawInsetsState(mWindow);
+ addWindow(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -574,7 +562,7 @@
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
- addWindowWithRawInsetsState(mWindow);
+ addWindow(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -588,7 +576,7 @@
mWindow.mAttrs.flags =
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
- addWindowWithRawInsetsState(mWindow);
+ addWindow(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -604,7 +592,7 @@
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
- addWindowWithRawInsetsState(mWindow);
+ addWindow(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -620,7 +608,7 @@
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
- addWindowWithRawInsetsState(mWindow);
+ addWindow(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -636,7 +624,7 @@
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
mWindow.mAttrs.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
- addWindowWithRawInsetsState(mWindow);
+ addWindow(mWindow);
mDisplayPolicy.layoutWindowLw(mWindow, null, mFrames);
@@ -650,7 +638,7 @@
FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
mWindow.mAttrs.setFitInsetsTypes(0 /* types */);
mWindow.mAttrs.softInputMode = SOFT_INPUT_ADJUST_NOTHING;
- addWindowWithRawInsetsState(mWindow);
+ addWindow(mWindow);
final int forwardedInsetBottom = 50;
mDisplayPolicy.setForwardedInsets(Insets.of(0, 0, 0, forwardedInsetBottom));
@@ -788,13 +776,9 @@
public void testFixedRotationInsetsSourceFrame() {
doReturn((mDisplayContent.getRotation() + 1) % 4).when(mDisplayContent)
.rotationForActivityInDifferentOrientation(eq(mWindow.mActivityRecord));
- mWindow.mAboveInsetsState.addSource(mDisplayContent.getInsetsStateController()
- .getRawInsetsState().peekSource(ITYPE_STATUS_BAR));
- final Rect frame = mDisplayPolicy.getInsetsPolicy().getInsetsForWindow(mWindow)
- .getSource(ITYPE_STATUS_BAR).getFrame();
+ final Rect frame = mWindow.getInsetsState().getSource(ITYPE_STATUS_BAR).getFrame();
mDisplayContent.rotateInDifferentOrientationIfNeeded(mWindow.mActivityRecord);
- final Rect rotatedFrame = mDisplayPolicy.getInsetsPolicy().getInsetsForWindow(mWindow)
- .getSource(ITYPE_STATUS_BAR).getFrame();
+ final Rect rotatedFrame = mWindow.getInsetsState().getSource(ITYPE_STATUS_BAR).getFrame();
assertEquals(DISPLAY_WIDTH, frame.width());
assertEquals(DISPLAY_HEIGHT, rotatedFrame.width());
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
index 499507e..77537a9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
@@ -300,7 +300,6 @@
displayPolicy.addWindowLw(mNavBarWindow, mNavBarWindow.mAttrs);
mNavBarWindow.getControllableInsetProvider().setServerVisible(true);
final InsetsState state = mDisplayContent.getInsetsStateController().getRawInsetsState();
- mImeWindow.mAboveInsetsState = state;
mDisplayContent.mDisplayFrames = new DisplayFrames(mDisplayContent.getDisplayId(),
state, displayInfo, null /* displayCutout */);
diff --git a/services/tests/wmtests/src/com/android/server/wm/FrameRateSelectionPriorityTests.java b/services/tests/wmtests/src/com/android/server/wm/FrameRateSelectionPriorityTests.java
index 032edde..325bca4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/FrameRateSelectionPriorityTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/FrameRateSelectionPriorityTests.java
@@ -18,15 +18,24 @@
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyInt;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
import android.platform.test.annotations.Presubmit;
+import android.view.Display.Mode;
+import android.view.DisplayInfo;
+import android.view.Surface;
+import android.view.SurfaceControl;
+import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -40,16 +49,40 @@
@Presubmit
@RunWith(WindowTestRunner.class)
public class FrameRateSelectionPriorityTests extends WindowTestsBase {
+ private static final float FLOAT_TOLERANCE = 0.01f;
+ private static final int LOW_MODE_ID = 3;
+
+ private DisplayPolicy mDisplayPolicy = mock(DisplayPolicy.class);
+ private RefreshRatePolicy mRefreshRatePolicy;
+ private HighRefreshRateDenylist mDenylist = mock(HighRefreshRateDenylist.class);
+
+ @Before
+ public void setUp() {
+ DisplayInfo di = new DisplayInfo(mDisplayInfo);
+ Mode defaultMode = di.getDefaultMode();
+ di.supportedModes = new Mode[] {
+ new Mode(1, defaultMode.getPhysicalWidth(), defaultMode.getPhysicalHeight(), 90),
+ new Mode(2, defaultMode.getPhysicalWidth(), defaultMode.getPhysicalHeight(), 70),
+ new Mode(LOW_MODE_ID,
+ defaultMode.getPhysicalWidth(), defaultMode.getPhysicalHeight(), 60),
+ };
+ di.defaultModeId = 1;
+ mRefreshRatePolicy = new RefreshRatePolicy(mWm, di, mDenylist);
+ when(mDisplayPolicy.getRefreshRatePolicy()).thenReturn(mRefreshRatePolicy);
+ }
@Test
public void basicTest() {
final WindowState appWindow = createWindow(null, TYPE_APPLICATION, "appWindow");
assertNotNull("Window state is created", appWindow);
+
assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
+ assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
appWindow.updateFrameRateSelectionPriorityIfNeeded();
// Priority doesn't change.
assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
+ assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
// Call the function a few times.
appWindow.updateFrameRateSelectionPriorityIfNeeded();
@@ -57,7 +90,9 @@
// Since nothing changed in the priority state, the transaction should not be updating.
verify(appWindow.getPendingTransaction(), never()).setFrameRateSelectionPriority(
- appWindow.getSurfaceControl(), RefreshRatePolicy.LAYER_PRIORITY_UNSET);
+ any(SurfaceControl.class), anyInt());
+ verify(appWindow.getPendingTransaction(), never()).setFrameRate(
+ any(SurfaceControl.class), anyInt(), anyInt());
}
@Test
@@ -66,10 +101,16 @@
assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
assertEquals(appWindow.getDisplayContent().getDisplayPolicy().getRefreshRatePolicy()
.getPreferredModeId(appWindow), 0);
+ assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
+ assertEquals(appWindow.getDisplayContent().getDisplayPolicy().getRefreshRatePolicy()
+ .getPreferredRefreshRate(appWindow), 0, FLOAT_TOLERANCE);
+
+ assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
appWindow.updateFrameRateSelectionPriorityIfNeeded();
// Priority stays MAX_VALUE.
assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
+ assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
verify(appWindow.getPendingTransaction(), never()).setFrameRateSelectionPriority(
appWindow.getSurfaceControl(), RefreshRatePolicy.LAYER_PRIORITY_UNSET);
@@ -78,31 +119,38 @@
appWindow.updateFrameRateSelectionPriorityIfNeeded();
// Priority changes to 1.
assertEquals(appWindow.mFrameRateSelectionPriority, 1);
+ assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
verify(appWindow.getPendingTransaction()).setFrameRateSelectionPriority(
appWindow.getSurfaceControl(), 1);
+ verify(appWindow.getPendingTransaction(), never()).setFrameRate(
+ any(SurfaceControl.class), anyInt(), anyInt());
}
@Test
public void testApplicationInFocusWithModeId() {
final WindowState appWindow = createWindow(null, TYPE_APPLICATION, "appWindow");
assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
+ assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
// Application is in focus.
appWindow.mToken.mDisplayContent.mCurrentFocus = appWindow;
appWindow.updateFrameRateSelectionPriorityIfNeeded();
// Priority changes.
assertEquals(appWindow.mFrameRateSelectionPriority, 1);
+ assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
// Update the mode ID to a requested number.
appWindow.mAttrs.preferredDisplayModeId = 1;
appWindow.updateFrameRateSelectionPriorityIfNeeded();
// Priority changes.
assertEquals(appWindow.mFrameRateSelectionPriority, 0);
+ assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
// Remove the mode ID request.
appWindow.mAttrs.preferredDisplayModeId = 0;
appWindow.updateFrameRateSelectionPriorityIfNeeded();
// Priority changes.
assertEquals(appWindow.mFrameRateSelectionPriority, 1);
+ assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
// Verify we called actions on Transactions correctly.
verify(appWindow.getPendingTransaction(), never()).setFrameRateSelectionPriority(
@@ -111,12 +159,15 @@
appWindow.getSurfaceControl(), 0);
verify(appWindow.getPendingTransaction(), times(2)).setFrameRateSelectionPriority(
appWindow.getSurfaceControl(), 1);
+ verify(appWindow.getPendingTransaction(), never()).setFrameRate(
+ any(SurfaceControl.class), anyInt(), anyInt());
}
@Test
public void testApplicationNotInFocusWithModeId() {
final WindowState appWindow = createWindow(null, TYPE_APPLICATION, "appWindow");
assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
+ assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
final WindowState inFocusWindow = createWindow(null, TYPE_APPLICATION, "inFocus");
appWindow.mToken.mDisplayContent.mCurrentFocus = inFocusWindow;
@@ -124,23 +175,28 @@
appWindow.updateFrameRateSelectionPriorityIfNeeded();
// The window is not in focus.
assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
+ assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
// Update the mode ID to a requested number.
appWindow.mAttrs.preferredDisplayModeId = 1;
appWindow.updateFrameRateSelectionPriorityIfNeeded();
// Priority changes.
assertEquals(appWindow.mFrameRateSelectionPriority, 2);
+ assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
verify(appWindow.getPendingTransaction()).setFrameRateSelectionPriority(
appWindow.getSurfaceControl(), RefreshRatePolicy.LAYER_PRIORITY_UNSET);
verify(appWindow.getPendingTransaction()).setFrameRateSelectionPriority(
appWindow.getSurfaceControl(), 2);
+ verify(appWindow.getPendingTransaction(), never()).setFrameRate(
+ any(SurfaceControl.class), anyInt(), anyInt());
}
@Test
public void testApplicationNotInFocusWithoutModeId() {
final WindowState appWindow = createWindow(null, TYPE_APPLICATION, "appWindow");
assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
+ assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
final WindowState inFocusWindow = createWindow(null, TYPE_APPLICATION, "inFocus");
appWindow.mToken.mDisplayContent.mCurrentFocus = inFocusWindow;
@@ -148,14 +204,45 @@
appWindow.updateFrameRateSelectionPriorityIfNeeded();
// The window is not in focus.
assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
+ assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
// Make sure that the mode ID is not set.
appWindow.mAttrs.preferredDisplayModeId = 0;
appWindow.updateFrameRateSelectionPriorityIfNeeded();
// Priority doesn't change.
assertEquals(appWindow.mFrameRateSelectionPriority, RefreshRatePolicy.LAYER_PRIORITY_UNSET);
+ assertEquals(appWindow.mDenyListFrameRate, 0, FLOAT_TOLERANCE);
verify(appWindow.getPendingTransaction()).setFrameRateSelectionPriority(
appWindow.getSurfaceControl(), RefreshRatePolicy.LAYER_PRIORITY_UNSET);
+ verify(appWindow.getPendingTransaction(), never()).setFrameRate(
+ any(SurfaceControl.class), anyInt(), anyInt());
+ }
+
+ @Test
+ public void testPreferredRefreshRate() {
+ final WindowState appWindow = createWindow(null, TYPE_APPLICATION, "appWindow");
+ assertNotNull("Window state is created", appWindow);
+ when(appWindow.getDisplayContent().getDisplayPolicy()).thenReturn(mDisplayPolicy);
+
+ appWindow.mAttrs.packageName = "com.android.test";
+ when(mDenylist.isDenylisted("com.android.test")).thenReturn(true);
+
+ assertEquals(0, mRefreshRatePolicy.getPreferredModeId(appWindow));
+ assertEquals(60, mRefreshRatePolicy.getPreferredRefreshRate(appWindow), FLOAT_TOLERANCE);
+
+ appWindow.updateFrameRateSelectionPriorityIfNeeded();
+ assertEquals(RefreshRatePolicy.LAYER_PRIORITY_UNSET, appWindow.mFrameRateSelectionPriority);
+ assertEquals(60, appWindow.mDenyListFrameRate, FLOAT_TOLERANCE);
+
+ // Call the function a few times.
+ appWindow.updateFrameRateSelectionPriorityIfNeeded();
+ appWindow.updateFrameRateSelectionPriorityIfNeeded();
+
+ // Since nothing changed in the priority state, the transaction should not be updating.
+ verify(appWindow.getPendingTransaction(), never()).setFrameRateSelectionPriority(
+ any(SurfaceControl.class), anyInt());
+ verify(appWindow.getPendingTransaction(), times(1)).setFrameRate(
+ appWindow.getSurfaceControl(), 60, Surface.FRAME_RATE_COMPATIBILITY_EXACT);
}
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
index bf3ed69..e0fd379 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
@@ -45,7 +45,6 @@
import android.app.StatusBarManager;
import android.platform.test.annotations.Presubmit;
-import android.view.InsetsSource;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
@@ -273,6 +272,7 @@
final WindowState navBar = addNonFocusableWindow(TYPE_NAVIGATION_BAR, "navBar");
navBar.setHasSurface(true);
navBar.getControllableInsetProvider().setServerVisible(true);
+
final InsetsPolicy policy = spy(mDisplayContent.getInsetsPolicy());
doNothing().when(policy).startAnimation(anyBoolean(), any());
@@ -337,14 +337,11 @@
@UseTestDisplay(addWindows = W_ACTIVITY)
@Test
public void testAbortTransientBars_bothCanBeAborted_appGetsBothRealControls() {
- final InsetsSource statusBarSource = addNonFocusableWindow(TYPE_STATUS_BAR, "statusBar")
- .getControllableInsetProvider().getSource();
- final InsetsSource navBarSource = addNonFocusableWindow(TYPE_NAVIGATION_BAR, "navBar")
- .getControllableInsetProvider().getSource();
- statusBarSource.setVisible(false);
- navBarSource.setVisible(false);
- mAppWindow.mAboveInsetsState.addSource(navBarSource);
- mAppWindow.mAboveInsetsState.addSource(statusBarSource);
+ addNonFocusableWindow(TYPE_STATUS_BAR, "statusBar")
+ .getControllableInsetProvider().getSource().setVisible(false);
+ addNonFocusableWindow(TYPE_NAVIGATION_BAR, "navBar")
+ .getControllableInsetProvider().getSource().setVisible(false);
+
final InsetsPolicy policy = spy(mDisplayContent.getInsetsPolicy());
doNothing().when(policy).startAnimation(anyBoolean(), any());
policy.updateBarControlTarget(mAppWindow);
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index 2107ab1e..2766438 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -59,6 +59,25 @@
public class InsetsStateControllerTest extends WindowTestsBase {
@Test
+ public void testStripForDispatch_notOwn() {
+ final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
+ final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+ getController().getSourceProvider(ITYPE_STATUS_BAR).setWindow(statusBar, null, null);
+ statusBar.setControllableInsetProvider(getController().getSourceProvider(ITYPE_STATUS_BAR));
+ assertNotNull(getController().getInsetsForWindow(app).peekSource(ITYPE_STATUS_BAR));
+ }
+
+ @Test
+ public void testStripForDispatch_own() {
+ final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
+ mDisplayContent.getInsetsStateController().getSourceProvider(ITYPE_STATUS_BAR)
+ .setWindow(statusBar, null, null);
+ statusBar.setControllableInsetProvider(getController().getSourceProvider(ITYPE_STATUS_BAR));
+ final InsetsState state = getController().getInsetsForWindow(statusBar);
+ assertNull(state.peekSource(ITYPE_STATUS_BAR));
+ }
+
+ @Test
public void testStripForDispatch_navBar() {
final WindowState navBar = createWindow(null, TYPE_APPLICATION, "navBar");
final WindowState statusBar = createWindow(null, TYPE_APPLICATION, "statusBar");
@@ -123,15 +142,14 @@
getController().getSourceProvider(ITYPE_IME).setWindow(mImeWindow, null, null);
final WindowState app1 = createWindow(null, TYPE_APPLICATION, "app1");
- final WindowState app2 = createWindow(null, TYPE_APPLICATION, "app2");
+ app1.mBehindIme = true;
- app1.mAboveInsetsState.addSource(getController().getRawInsetsState().getSource(ITYPE_IME));
+ final WindowState app2 = createWindow(null, TYPE_APPLICATION, "app2");
+ app2.mBehindIme = false;
getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true);
- assertFalse(getController().getInsetsForWindow(app2).getSource(ITYPE_IME)
- .isVisible());
- assertTrue(getController().getInsetsForWindow(app1).getSource(ITYPE_IME)
- .isVisible());
+ assertFalse(getController().getInsetsForWindow(app2).getSource(ITYPE_IME).isVisible());
+ assertTrue(getController().getInsetsForWindow(app1).getSource(ITYPE_IME).isVisible());
}
@UseTestDisplay(addWindows = W_INPUT_METHOD)
@@ -140,8 +158,7 @@
getController().getSourceProvider(ITYPE_IME).setWindow(mImeWindow, null, null);
final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
- app.mAboveInsetsState.getSource(ITYPE_IME).setVisible(true);
- app.mAboveInsetsState.getSource(ITYPE_IME).setFrame(mImeWindow.getFrame());
+ app.mBehindIme = true;
getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true);
assertTrue(getController().getInsetsForWindow(app).getSource(ITYPE_IME).isVisible());
@@ -153,10 +170,10 @@
getController().getSourceProvider(ITYPE_IME).setWindow(mImeWindow, null, null);
final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+ app.mBehindIme = false;
getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true);
- assertFalse(getController().getInsetsForWindow(app).getSource(ITYPE_IME)
- .isVisible());
+ assertFalse(getController().getInsetsForWindow(app).getSource(ITYPE_IME).isVisible());
}
@UseTestDisplay(addWindows = W_INPUT_METHOD)
@@ -193,8 +210,7 @@
// app won't get visible IME insets while above IME even when IME is visible.
assertTrue(getController().getRawInsetsState().getSourceOrDefaultVisibility(ITYPE_IME));
- assertFalse(getController().getInsetsForWindow(app).getSource(ITYPE_IME)
- .isVisible());
+ assertFalse(getController().getInsetsForWindow(app).getSource(ITYPE_IME).isVisible());
// Reset invocation counter.
clearInvocations(app);
@@ -203,8 +219,6 @@
app.mAttrs.flags &= ~FLAG_NOT_FOCUSABLE;
mDisplayContent.computeImeTarget(true);
mDisplayContent.applySurfaceChangesTransaction();
- app.mAboveInsetsState.getSource(ITYPE_IME).setVisible(true);
- app.mAboveInsetsState.getSource(ITYPE_IME).setFrame(mImeWindow.getFrame());
// Make sure app got notified.
verify(app, atLeast(1)).notifyInsetsChanged();
@@ -220,8 +234,6 @@
final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
final WindowState child = createWindow(app, TYPE_APPLICATION, "child");
- app.mAboveInsetsState.set(getController().getRawInsetsState());
- child.mAboveInsetsState.set(getController().getRawInsetsState());
child.mAttrs.flags |= FLAG_ALT_FOCUSABLE_IM;
mDisplayContent.computeImeTarget(true);
@@ -230,8 +242,7 @@
getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true);
assertTrue(getController().getInsetsForWindow(app).getSource(ITYPE_IME).isVisible());
- assertFalse(getController().getInsetsForWindow(child).getSource(ITYPE_IME)
- .isVisible());
+ assertFalse(getController().getInsetsForWindow(child).getSource(ITYPE_IME).isVisible());
}
@UseTestDisplay(addWindows = W_INPUT_METHOD)
@@ -241,7 +252,6 @@
final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
final WindowState child = createWindow(app, TYPE_APPLICATION, "child");
- app.mAboveInsetsState.addSource(getController().getRawInsetsState().peekSource(ITYPE_IME));
child.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
child.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
@@ -251,8 +261,7 @@
getController().getRawInsetsState().setSourceVisible(ITYPE_IME, true);
assertTrue(getController().getInsetsForWindow(app).getSource(ITYPE_IME).isVisible());
- assertFalse(getController().getInsetsForWindow(child).getSource(ITYPE_IME)
- .isVisible());
+ assertFalse(getController().getInsetsForWindow(child).getSource(ITYPE_IME).isVisible());
}
@Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
index 77a4b05..ef3c7ae 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RefreshRatePolicyTest.java
@@ -44,7 +44,7 @@
@RunWith(WindowTestRunner.class)
@FlakyTest
public class RefreshRatePolicyTest extends WindowTestsBase {
-
+ private static final float FLOAT_TOLERANCE = 0.01f;
private static final int LOW_MODE_ID = 3;
private RefreshRatePolicy mPolicy;
@@ -70,28 +70,34 @@
"cameraUsingWindow");
cameraUsingWindow.mAttrs.packageName = "com.android.test";
assertEquals(0, mPolicy.getPreferredModeId(cameraUsingWindow));
+ assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
mPolicy.addNonHighRefreshRatePackage("com.android.test");
assertEquals(LOW_MODE_ID, mPolicy.getPreferredModeId(cameraUsingWindow));
+ assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
mPolicy.removeNonHighRefreshRatePackage("com.android.test");
assertEquals(0, mPolicy.getPreferredModeId(cameraUsingWindow));
+ assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
}
@Test
- public void testBlacklist() {
- final WindowState blacklistedWindow = createWindow(null, TYPE_BASE_APPLICATION,
- "blacklistedWindow");
- blacklistedWindow.mAttrs.packageName = "com.android.test";
+ public void testDenyList() {
+ final WindowState denylistedWindow = createWindow(null, TYPE_BASE_APPLICATION,
+ "denylistedWindow");
+ denylistedWindow.mAttrs.packageName = "com.android.test";
when(mDenylist.isDenylisted("com.android.test")).thenReturn(true);
- assertEquals(LOW_MODE_ID, mPolicy.getPreferredModeId(blacklistedWindow));
+ assertEquals(0, mPolicy.getPreferredModeId(denylistedWindow));
+ assertEquals(60, mPolicy.getPreferredRefreshRate(denylistedWindow), FLOAT_TOLERANCE);
}
@Test
public void testAppOverride_blacklist() {
final WindowState overrideWindow = createWindow(null, TYPE_BASE_APPLICATION,
"overrideWindow");
+ overrideWindow.mAttrs.packageName = "com.android.test";
overrideWindow.mAttrs.preferredDisplayModeId = LOW_MODE_ID;
when(mDenylist.isDenylisted("com.android.test")).thenReturn(true);
assertEquals(LOW_MODE_ID, mPolicy.getPreferredModeId(overrideWindow));
+ assertEquals(60, mPolicy.getPreferredRefreshRate(overrideWindow), FLOAT_TOLERANCE);
}
@Test
@@ -102,6 +108,7 @@
overrideWindow.mAttrs.preferredDisplayModeId = LOW_MODE_ID;
mPolicy.addNonHighRefreshRatePackage("com.android.test");
assertEquals(LOW_MODE_ID, mPolicy.getPreferredModeId(overrideWindow));
+ assertEquals(0, mPolicy.getPreferredRefreshRate(overrideWindow), FLOAT_TOLERANCE);
}
@Test
@@ -115,6 +122,7 @@
false /* hidden */, ANIMATION_TYPE_APP_TRANSITION);
mPolicy.addNonHighRefreshRatePackage("com.android.test");
assertEquals(0, mPolicy.getPreferredModeId(overrideWindow));
+ assertEquals(0, mPolicy.getPreferredRefreshRate(overrideWindow), FLOAT_TOLERANCE);
}
@Test
@@ -125,10 +133,12 @@
mPolicy.addNonHighRefreshRatePackage("com.android.test");
assertEquals(LOW_MODE_ID, mPolicy.getPreferredModeId(cameraUsingWindow));
+ assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
cameraUsingWindow.mActivityRecord.mSurfaceAnimator.startAnimation(
cameraUsingWindow.getPendingTransaction(), mock(AnimationAdapter.class),
false /* hidden */, ANIMATION_TYPE_APP_TRANSITION);
assertEquals(0, mPolicy.getPreferredModeId(cameraUsingWindow));
+ assertEquals(0, mPolicy.getPreferredRefreshRate(cameraUsingWindow), FLOAT_TOLERANCE);
}
}
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 db77324..371e680 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -109,7 +109,7 @@
final Rect originalOverrideBounds = new Rect(mActivity.getBounds());
resizeDisplay(mTask.mDisplayContent, 600, 1200);
// The visible activity should recompute configuration according to the last parent bounds.
- mAtm.restartActivityProcessIfVisible(mActivity.appToken);
+ mAtm.mActivityClientController.restartActivityProcessIfVisible(mActivity.appToken);
assertEquals(Task.ActivityState.RESTARTING_PROCESS, mActivity.getState());
assertNotEquals(originalOverrideBounds, mActivity.getBounds());
diff --git a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
index c308fdb..b8d44f6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
+++ b/services/tests/wmtests/src/com/android/server/wm/StubTransaction.java
@@ -245,6 +245,12 @@
}
@Override
+ public SurfaceControl.Transaction setFrameRate(SurfaceControl sc, float frameRate,
+ int compatibility) {
+ return this;
+ }
+
+ @Override
public SurfaceControl.Transaction unsetColor(SurfaceControl sc) {
return this;
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
index a1f89ec..1607f01 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
@@ -275,7 +275,7 @@
imeSource.setFrame(imeFrame);
imeSource.setVisible(true);
w.updateRequestedVisibility(state);
- w.mAboveInsetsState.addSource(imeSource);
+ w.mBehindIme = true;
// With no insets or system decor all the frames incoming from PhoneWindowManager
// are identical.
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 5b03863..472d639 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -314,20 +314,22 @@
public static final String EXTRA_HAS_PICTURE = "android.telecom.extra.HAS_PICTURE";
/**
- * A URI representing the picture that was downloaded when a call is received.
+ * A URI representing the picture that was downloaded when a call is received or uploaded
+ * when a call is placed.
+ *
* This is a content URI within the call log provider which can be used to open a file
* descriptor. This could be set a short time after a call is added to the Dialer app if the
- * download is delayed for some reason. The Dialer app will receive a callback via
+ * download/upload is delayed for some reason. The Dialer app will receive a callback via
* {@link Call.Callback#onDetailsChanged} when this value has changed.
*
* Reference: RCC.20 Section 2.4.3.2
*/
- public static final String EXTRA_INCOMING_PICTURE = "android.telecom.extra.INCOMING_PICTURE";
+ public static final String EXTRA_PICTURE_URI = "android.telecom.extra.PICTURE_URI";
- // TODO(hallliu), This UUID is obtained from TelephonyManager#uploadCallComposerPicture.
/**
* A ParcelUuid used as a token to represent a picture that was uploaded prior to the call
- * being placed.
+ * being placed. The value of this extra should be set using the {@link android.os.ParcelUuid}
+ * obtained from the callback in {@link TelephonyManager#uploadCallComposerPicture}.
*/
public static final String EXTRA_OUTGOING_PICTURE = "android.telecom.extra.OUTGOING_PICTURE";
diff --git a/telephony/java/android/telephony/TelephonyLocalConnection.java b/telephony/java/android/telephony/TelephonyLocalConnection.java
new file mode 100644
index 0000000..1cab267
--- /dev/null
+++ b/telephony/java/android/telephony/TelephonyLocalConnection.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.telephony;
+
+import java.util.UUID;
+
+/**
+ * Shim used for code in frameworks/opt/telephony to be able to call code in
+ * packages/services/Telephony. A singleton instance of this class is set when the phone process
+ * is brought up.
+ * @hide
+ */
+public class TelephonyLocalConnection {
+ public interface ConnectionImpl {
+ String getCallComposerServerUrlForHandle(int subscriptionId, UUID uuid);
+ }
+ private static ConnectionImpl sInstance;
+
+ public static String getCallComposerServerUrlForHandle(int subscriptionId, UUID uuid) {
+ checkInstance();
+ return sInstance.getCallComposerServerUrlForHandle(subscriptionId, uuid);
+ }
+
+ private static void checkInstance() {
+ if (sInstance == null) {
+ throw new IllegalStateException("Connection impl is null!");
+ }
+ }
+
+ public static void setInstance(ConnectionImpl impl) {
+ sInstance = impl;
+ }
+}
diff --git a/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java
index 519d016..5eb75e7 100644
--- a/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java
+++ b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java
@@ -19,6 +19,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.StringDef;
+import android.annotation.SystemApi;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
@@ -34,14 +35,135 @@
* network during a SUBSCRIBE request. See RFC3863 for more information.
* @hide
*/
+@SystemApi
public final class RcsContactPresenceTuple implements Parcelable {
- /** The service id of the MMTEL */
+ /**
+ * The service ID used to indicate that MMTEL service is available.
+ * <p>
+ * See the GSMA RCC.07 specification for more information.
+ */
public static final String SERVICE_ID_MMTEL = "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.mmtel";
- /** The service id of the Call Composer */
+ /**
+ * The service ID used to indicate that the chat(v1.0) is available.
+ * <p>
+ * See the GSMA RCC.07 specification for more information.
+ */
+ public static final String SERVICE_ID_CHAT_V1 = "org.openmobilealliance:IM-session";
+
+ /**
+ * The service ID used to indicate that the chat(v2.0) is available.
+ * <p>
+ * See the GSMA RCC.07 specification for more information.
+ */
+ public static final String SERVICE_ID_CHAT_V2 = "org.openmobilealliance:ChatSession";
+
+ /**
+ * The service ID used to indicate that the File Transfer is available.
+ * <p>
+ * See the GSMA RCC.07 specification for more information.
+ */
+ public static final String SERVICE_ID_FT = "org.openmobilealliance:File-Transfer-HTTP";
+
+ /**
+ * The service ID used to indicate that the File Transfer over SMS is available.
+ * <p>
+ * See the GSMA RCC.07 specification for more information.
+ */
+ public static final String SERVICE_ID_FT_OVER_SMS =
+ "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.ftsms";
+
+ /**
+ * The service ID used to indicate that the Geolocation Push is available.
+ * <p>
+ * See the GSMA RCC.07 specification for more information.
+ */
+ public static final String SERVICE_ID_GEO_PUSH =
+ "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.geopush";
+
+ /**
+ * The service ID used to indicate that the Geolocation Push via SMS is available.
+ * <p>
+ * See the GSMA RCC.07 specification for more information.
+ */
+ public static final String SERVICE_ID_GEO_PUSH_VIA_SMS =
+ "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.geosms";
+
+ /**
+ * The service ID used to indicate that the Call Composer is available.
+ * <p>
+ * See the GSMA RCC.07 specification for more information.
+ */
public static final String SERVICE_ID_CALL_COMPOSER =
- "org.3gpp.urn:urn-7:3gppservice.ims.icsi.gsma.callcomposer";
+ "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.gsma.callcomposer";
+
+ /**
+ * The service ID used to indicate that the Post Call is available.
+ * <p>
+ * See the GSMA RCC.07 specification for more information.
+ */
+ public static final String SERVICE_ID_POST_CALL =
+ "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.gsma.callunanswered";
+
+ /**
+ * The service ID used to indicate that the Shared Map is available.
+ * <p>
+ * See the GSMA RCC.07 specification for more information.
+ */
+ public static final String SERVICE_ID_SHARED_MAP =
+ "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.gsma.sharedmap";
+
+ /**
+ * The service ID used to indicate that the Shared Sketch is available.
+ * <p>
+ * See the GSMA RCC.07 specification for more information.
+ */
+ public static final String SERVICE_ID_SHARED_SKETCH =
+ "org.3gpp.urn:urn-7:3gpp-service.ims.icsi.gsma.sharedsketch";
+
+ /**
+ * The service ID used to indicate that the Chatbot using Session is available.
+ * <p>
+ * See the GSMA RCC.07 specification for more information.
+ */
+ public static final String SERVICE_ID_CHATBOT =
+ "org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.chatbot";
+
+ /**
+ * The service ID used to indicate that the Standalone Messaging is available.
+ * <p>
+ * See the GSMA RCC.07 specification for more information.
+ */
+ public static final String SERVICE_ID_CHATBOT_STANDALONE =
+ " org.3gpp.urn:urn-7:3gpp-application.ims.iari.rcs.chatbot.sa";
+
+ /**
+ * The service ID used to indicate that the Chatbot Role is available.
+ * <p>
+ * See the GSMA RCC.07 specification for more information.
+ */
+ public static final String SERVICE_ID_CHATBOT_ROLE = "org.gsma.rcs.isbot";
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @StringDef(prefix = "SERVICE_ID_", value = {
+ SERVICE_ID_MMTEL,
+ SERVICE_ID_CHAT_V1,
+ SERVICE_ID_CHAT_V2,
+ SERVICE_ID_FT,
+ SERVICE_ID_FT_OVER_SMS,
+ SERVICE_ID_GEO_PUSH,
+ SERVICE_ID_GEO_PUSH_VIA_SMS,
+ SERVICE_ID_CALL_COMPOSER,
+ SERVICE_ID_POST_CALL,
+ SERVICE_ID_SHARED_MAP,
+ SERVICE_ID_SHARED_SKETCH,
+ SERVICE_ID_CHATBOT,
+ SERVICE_ID_CHATBOT_STANDALONE,
+ SERVICE_ID_CHATBOT_ROLE
+ })
+ public @interface ServiceId {}
/** The service capabilities is available. */
public static final String TUPLE_BASIC_STATUS_OPEN = "open";
@@ -149,6 +271,7 @@
in.readStringList(mSupportedDuplexModeList);
in.readStringList(mUnsupportedDuplexModeList);
}
+
@Override
public void writeToParcel(@NonNull Parcel out, int flags) {
out.writeBoolean(mIsAudioCapable);
@@ -217,12 +340,14 @@
/**
* Builds a RcsContactPresenceTuple instance.
+ * @param status The status associated with the service capability. See RFC3865 for more
+ * information.
* @param serviceId The OMA Presence service-id associated with this capability. See the
* OMA Presence SIMPLE specification v1.1, section 10.5.1.
* @param serviceVersion The OMA Presence version associated with the service capability.
* See the OMA Presence SIMPLE specification v1.1, section 10.5.1.
*/
- public Builder(@NonNull @BasicStatus String status, @NonNull String serviceId,
+ public Builder(@NonNull @BasicStatus String status, @NonNull @ServiceId String serviceId,
@NonNull String serviceVersion) {
mPresenceTuple = new RcsContactPresenceTuple(status, serviceId, serviceVersion);
}
@@ -230,16 +355,17 @@
/**
* The optional SIP Contact URI associated with the PIDF tuple element.
*/
- public @NonNull Builder addContactUri(@NonNull Uri contactUri) {
+ public @NonNull Builder setContactUri(@NonNull Uri contactUri) {
mPresenceTuple.mContactUri = contactUri;
return this;
}
/**
* The optional timestamp indicating the data and time of the status change of this tuple.
- * See RFC3863, section 4.1.7 for more information on the expected format.
+ * Per RFC3863 section 4.1.7, the timestamp is formatted as an IMPP datetime format
+ * string per RFC3339.
*/
- public @NonNull Builder addTimeStamp(@NonNull String timestamp) {
+ public @NonNull Builder setTimestamp(@NonNull String timestamp) {
mPresenceTuple.mTimestamp = timestamp;
return this;
}
@@ -248,7 +374,7 @@
* An optional parameter containing the description element of the service-description. See
* OMA Presence SIMPLE specification v1.1
*/
- public @NonNull Builder addDescription(@NonNull String description) {
+ public @NonNull Builder setServiceDescription(@NonNull String description) {
mPresenceTuple.mServiceDescription = description;
return this;
}
@@ -257,7 +383,7 @@
* An optional parameter containing the service capabilities of the presence tuple if they
* are present in the servcaps element.
*/
- public @NonNull Builder addServiceCapabilities(@NonNull ServiceCapabilities caps) {
+ public @NonNull Builder setServiceCapabilities(@NonNull ServiceCapabilities caps) {
mPresenceTuple.mServiceCapabilities = caps;
return this;
}
diff --git a/telephony/java/android/telephony/ims/RcsContactUceCapability.java b/telephony/java/android/telephony/ims/RcsContactUceCapability.java
index d4715bf..fe85502 100644
--- a/telephony/java/android/telephony/ims/RcsContactUceCapability.java
+++ b/telephony/java/android/telephony/ims/RcsContactUceCapability.java
@@ -19,6 +19,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
@@ -33,6 +34,7 @@
* Contains the User Capability Exchange capabilities corresponding to a contact's URI.
* @hide
*/
+@SystemApi
public final class RcsContactUceCapability implements Parcelable {
/** Contains presence information associated with the contact */
@@ -70,52 +72,46 @@
public @interface SourceType {}
/**
+ * Capability information for the requested contact has expired and can not be refreshed due to
+ * a temporary network error. This is a temporary error and the capabilities of the contact
+ * should be queried again at a later time.
+ */
+ public static final int REQUEST_RESULT_UNKNOWN = 0;
+
+ /**
* The requested contact was found to be offline when queried. This is only applicable to
* contact capabilities that were queried via OPTIONS requests and the network returned a
* 408/480 response.
*/
- public static final int REQUEST_RESULT_NOT_ONLINE = 0;
+ public static final int REQUEST_RESULT_NOT_ONLINE = 1;
/**
* Capability information for the requested contact was not found. The contact should not be
* considered an RCS user.
*/
- public static final int REQUEST_RESULT_NOT_FOUND = 1;
+ public static final int REQUEST_RESULT_NOT_FOUND = 2;
/**
* Capability information for the requested contact was found successfully.
*/
- public static final int REQUEST_RESULT_FOUND = 2;
-
- /**
- * Capability information for the requested contact has expired and can not be refreshed due to
- * a temporary network error. This is a temporary error and the capabilities of the contact
- * should be queried again at a later time.
- */
- public static final int REQUEST_RESULT_UNKNOWN = 3;
+ public static final int REQUEST_RESULT_FOUND = 3;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = "REQUEST_RESULT_", value = {
+ REQUEST_RESULT_UNKNOWN,
REQUEST_RESULT_NOT_ONLINE,
REQUEST_RESULT_NOT_FOUND,
- REQUEST_RESULT_FOUND,
- REQUEST_RESULT_UNKNOWN
+ REQUEST_RESULT_FOUND
})
public @interface RequestResult {}
/**
- * The base class of {@link OptionsBuilder} and {@link PresenceBuilder}
- */
- public static abstract class RcsUcsCapabilityBuilder {
- public abstract @NonNull RcsContactUceCapability build();
- }
-
- /**
* Builder to help construct {@link RcsContactUceCapability} instances when capabilities were
* queried through SIP OPTIONS.
+ * @hide
*/
- public static class OptionsBuilder extends RcsUcsCapabilityBuilder {
+ public static final class OptionsBuilder {
private final RcsContactUceCapability mCapabilities;
@@ -162,7 +158,6 @@
/**
* @return the constructed instance.
*/
- @Override
public @NonNull RcsContactUceCapability build() {
return mCapabilities;
}
@@ -172,7 +167,7 @@
* Builder to help construct {@link RcsContactUceCapability} instances when capabilities were
* queried through a presence server.
*/
- public static class PresenceBuilder extends RcsUcsCapabilityBuilder {
+ public static final class PresenceBuilder {
private final RcsContactUceCapability mCapabilities;
@@ -214,7 +209,6 @@
/**
* @return the RcsContactUceCapability instance.
*/
- @Override
public @NonNull RcsContactUceCapability build() {
return mCapabilities;
}
@@ -284,6 +278,7 @@
* <p>
* Note: this is only populated if {@link #getCapabilityMechanism} is
* {@link RcsContactUceCapability#CAPABILITY_MECHANISM_OPTIONS}
+ * @hide
*/
public @NonNull List<String> getOptionsFeatureTags() {
if (mCapabilityMechanism != CAPABILITY_MECHANISM_OPTIONS) {
@@ -299,7 +294,7 @@
* Note: this is only populated if {@link #getCapabilityMechanism} is
* {@link RcsContactUceCapability#CAPABILITY_MECHANISM_PRESENCE}
*/
- public @NonNull List<RcsContactPresenceTuple> getPresenceTuples() {
+ public @NonNull List<RcsContactPresenceTuple> getCapabilityTuples() {
if (mCapabilityMechanism != CAPABILITY_MECHANISM_PRESENCE) {
return Collections.emptyList();
}
@@ -309,13 +304,14 @@
/**
* Get the RcsContactPresenceTuple associated with the given service id.
* @param serviceId The service id to get the presence tuple.
- * @return The RcsContactPresenceTuple which has the given service id.
+ * @return The RcsContactPresenceTuple which has the given service id or {@code null} if the
+ * service id does not exist in the list of presence tuples returned from the network.
*
* <p>
* Note: this is only populated if {@link #getCapabilityMechanism} is
* {@link RcsContactUceCapability#CAPABILITY_MECHANISM_PRESENCE}
*/
- public @Nullable RcsContactPresenceTuple getPresenceTuple(@NonNull String serviceId) {
+ public @Nullable RcsContactPresenceTuple getCapabilityTuple(@NonNull String serviceId) {
if (mCapabilityMechanism != CAPABILITY_MECHANISM_PRESENCE) {
return null;
}
diff --git a/telephony/java/android/telephony/ims/RcsUceAdapter.java b/telephony/java/android/telephony/ims/RcsUceAdapter.java
index 6c31466..070fd799 100644
--- a/telephony/java/android/telephony/ims/RcsUceAdapter.java
+++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java
@@ -63,6 +63,7 @@
* RcsFeature should not publish capabilities or service capability requests.
* @hide
*/
+ @SystemApi
public static final int CAPABILITY_TYPE_PRESENCE_UCE = 1 << 1;
/**@hide*/
@@ -77,12 +78,14 @@
* An unknown error has caused the request to fail.
* @hide
*/
+ @SystemApi
public static final int ERROR_GENERIC_FAILURE = 1;
/**
* The carrier network does not have UCE support enabled for this subscriber.
* @hide
*/
+ @SystemApi
public static final int ERROR_NOT_ENABLED = 2;
/**
@@ -90,12 +93,14 @@
* 1x only currently).
* @hide
*/
+ @SystemApi
public static final int ERROR_NOT_AVAILABLE = 3;
/**
* The network has responded with SIP 403 error and a reason "User not registered."
* @hide
*/
+ @SystemApi
public static final int ERROR_NOT_REGISTERED = 4;
/**
@@ -103,12 +108,14 @@
* presence" for this subscriber.
* @hide
*/
+ @SystemApi
public static final int ERROR_NOT_AUTHORIZED = 5;
/**
* The network has responded to this request with a SIP 403 error and no reason.
* @hide
*/
+ @SystemApi
public static final int ERROR_FORBIDDEN = 6;
/**
@@ -116,6 +123,7 @@
* subscriber to the carrier network.
* @hide
*/
+ @SystemApi
public static final int ERROR_NOT_FOUND = 7;
/**
@@ -123,6 +131,7 @@
* with a lower number of contact numbers. The number varies per carrier.
* @hide
*/
+ @SystemApi
// TODO: Try to integrate this into the API so that the service will split based on carrier.
public static final int ERROR_REQUEST_TOO_LARGE = 8;
@@ -130,18 +139,21 @@
* The network did not respond to the capabilities request before the request timed out.
* @hide
*/
+ @SystemApi
public static final int ERROR_REQUEST_TIMEOUT = 9;
/**
* The request failed due to the service having insufficient memory.
* @hide
*/
+ @SystemApi
public static final int ERROR_INSUFFICIENT_MEMORY = 10;
/**
* The network was lost while trying to complete the request.
* @hide
*/
+ @SystemApi
public static final int ERROR_LOST_NETWORK = 11;
/**
@@ -149,6 +161,7 @@
* time returned in {@link CapabilitiesCallback#onError} has elapsed.
* @hide
*/
+ @SystemApi
public static final int ERROR_SERVER_UNAVAILABLE = 12;
/**@hide*/
@@ -405,6 +418,7 @@
* @see #requestCapabilities(Executor, List, CapabilitiesCallback)
* @hide
*/
+ @SystemApi
public interface CapabilitiesCallback {
/**
@@ -424,10 +438,10 @@
* The pending request has resulted in an error and may need to be retried, depending on the
* error code.
* @param errorCode The reason for the framework being unable to process the request.
- * @param retryAfterMilliseconds The time in milliseconds the requesting application should
+ * @param retryIntervalMillis The time in milliseconds the requesting application should
* wait before retrying, if non-zero.
*/
- void onError(@ErrorCode int errorCode, long retryAfterMilliseconds);
+ void onError(@ErrorCode int errorCode, long retryIntervalMillis);
}
private final Context mContext;
@@ -458,9 +472,9 @@
* {@link RcsFeature.RcsImsCapabilities#CAPABILITY_TYPE_PRESENCE_UCE} is enabled or else
* this operation will fail with {@link #ERROR_NOT_AVAILABLE} or {@link #ERROR_NOT_ENABLED}.
*
+ * @param contactNumbers A list of numbers that the capabilities are being requested for.
* @param executor The executor that will be used when the request is completed and the
* {@link CapabilitiesCallback} is called.
- * @param contactNumbers A list of numbers that the capabilities are being requested for.
* @param c A one-time callback for when the request for capabilities completes or there is an
* error processing the request.
* @throws ImsException if the subscription associated with this instance of
@@ -469,9 +483,10 @@
* becomes inactive. See {@link ImsException#getCode()} for more information on the error codes.
* @hide
*/
+ @SystemApi
@RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
- public void requestCapabilities(@NonNull @CallbackExecutor Executor executor,
- @NonNull List<Uri> contactNumbers,
+ public void requestCapabilities(@NonNull List<Uri> contactNumbers,
+ @NonNull @CallbackExecutor Executor executor,
@NonNull CapabilitiesCallback c) throws ImsException {
if (c == null) {
throw new IllegalArgumentException("Must include a non-null CapabilitiesCallback.");
@@ -495,8 +510,7 @@
public void onCapabilitiesReceived(List<RcsContactUceCapability> contactCapabilities) {
final long callingIdentity = Binder.clearCallingIdentity();
try {
- executor.execute(() ->
- c.onCapabilitiesReceived(contactCapabilities));
+ executor.execute(() -> c.onCapabilitiesReceived(contactCapabilities));
} finally {
restoreCallingIdentity(callingIdentity);
}
@@ -550,13 +564,17 @@
* {@link #ERROR_NOT_AVAILABLE} or {@link #ERROR_NOT_ENABLED}.
*
* @param contactNumber The contact of the capabilities is being requested for.
+ * @param executor The executor that will be used when the request is completed and the
+ * {@link CapabilitiesCallback} is called.
* @param c A one-time callback for when the request for capabilities completes or there is
* an error processing the request.
* @hide
*/
+ @SystemApi
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
- public void requestNetworkAvailability(@NonNull @CallbackExecutor Executor executor,
- @NonNull Uri contactNumber, @NonNull CapabilitiesCallback c) throws ImsException {
+ public void requestAvailability(@NonNull Uri contactNumber,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull CapabilitiesCallback c) throws ImsException {
if (executor == null) {
throw new IllegalArgumentException("Must include a non-null Executor.");
}
@@ -569,7 +587,7 @@
IImsRcsController imsRcsController = getIImsRcsController();
if (imsRcsController == null) {
- Log.e(TAG, "requestNetworkAvailability: IImsRcsController is null");
+ Log.e(TAG, "requestAvailability: IImsRcsController is null");
throw new ImsException("Cannot find remote IMS service",
ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
@@ -579,8 +597,7 @@
public void onCapabilitiesReceived(List<RcsContactUceCapability> contactCapabilities) {
final long callingIdentity = Binder.clearCallingIdentity();
try {
- executor.execute(() ->
- c.onCapabilitiesReceived(contactCapabilities));
+ executor.execute(() -> c.onCapabilitiesReceived(contactCapabilities));
} finally {
restoreCallingIdentity(callingIdentity);
}
@@ -606,12 +623,12 @@
};
try {
- imsRcsController.requestNetworkAvailability(mSubId, mContext.getOpPackageName(),
+ imsRcsController.requestAvailability(mSubId, mContext.getOpPackageName(),
mContext.getAttributionTag(), contactNumber, internalCallback);
} catch (ServiceSpecificException e) {
throw new ImsException(e.toString(), e.errorCode);
} catch (RemoteException e) {
- Log.e(TAG, "Error calling IImsRcsController#requestNetworkAvailability", e);
+ Log.e(TAG, "Error calling IImsRcsController#requestAvailability", e);
throw new ImsException("Remote IMS Service is not available",
ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
@@ -683,7 +700,7 @@
if (imsRcsController == null) {
Log.e(TAG, "addOnPublishStateChangedListener : IImsRcsController is null");
throw new ImsException("Cannot find remote IMS service",
- ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
PublishStateCallbackAdapter stateCallback = addPublishStateCallback(executor, listener);
@@ -694,7 +711,7 @@
} catch (RemoteException e) {
Log.e(TAG, "Error calling IImsRcsController#registerUcePublishStateCallback", e);
throw new ImsException("Remote IMS Service is not available",
- ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
+ ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
}
}
diff --git a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
index 3634989..7a6c28b 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl
@@ -52,7 +52,7 @@
// ImsUceAdapter specific
void requestCapabilities(int subId, String callingPackage, String callingFeatureId,
in List<Uri> contactNumbers, IRcsUceControllerCallback c);
- void requestNetworkAvailability(int subId, String callingPackage,
+ void requestAvailability(int subId, String callingPackage,
String callingFeatureId, in Uri contactNumber,
IRcsUceControllerCallback c);
int getUcePublishState(int subId);
diff --git a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
index c84e23c..7eba709 100644
--- a/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/RcsCapabilityExchangeImplBase.java
@@ -24,6 +24,7 @@
import android.annotation.SystemApi;
import android.net.Uri;
import android.telephony.ims.ImsException;
+import android.telephony.ims.RcsUceAdapter;
import android.telephony.ims.feature.ImsFeature;
import android.telephony.ims.feature.RcsFeature;
import android.util.Log;
@@ -139,18 +140,19 @@
* Provide the framework with a subsequent network response update to
* {@link #publishCapabilities(String, PublishResponseCallback)}.
*
- * @param code The SIP response code sent from the network for the operation
+ * @param sipCode The SIP response code sent from the network for the operation
* token specified.
* @param reason The optional reason response from the network. If there is a reason header
* included in the response, that should take precedence over the reason provided in the
- * status line. If the network provided no reason with the code, the string should be empty.
+ * status line. If the network provided no reason with the sip code, the string should be
+ * empty.
* @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is
* not currently connected to the framework. This can happen if the {@link RcsFeature}
* is not {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not received
* the {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare cases
* when the Telephony stack has crashed.
*/
- void onNetworkResponse(@IntRange(from = 100, to = 699) int code,
+ void onNetworkResponse(@IntRange(from = 100, to = 699) int sipCode,
@NonNull String reason) throws ImsException;
}
@@ -173,7 +175,7 @@
/**
* Send the response of a SIP OPTIONS capability exchange to the framework.
- * @param code The SIP response code that was sent by the network in response
+ * @param sipCode The SIP response code that was sent by the network in response
* to the request sent by {@link #sendOptionsCapabilityRequest}.
* @param reason The optional SIP response reason sent by the network.
* If none was sent, this should be an empty string.
@@ -186,17 +188,20 @@
* {@link ImsFeature#onFeatureReady()} callback. This may also happen in rare
* cases when the Telephony stack has crashed.
*/
- void onNetworkResponse(int code, @NonNull String reason,
+ void onNetworkResponse(int sipCode, @NonNull String reason,
@Nullable List<String> theirCaps) throws ImsException;
}
/**
* Interface used by the framework to receive the response of the subscribe request.
- * @hide
*/
public interface SubscribeResponseCallback {
/**
* Notify the framework that the command associated with this callback has failed.
+ * <p>
+ * Must only be called when there was an error generating a SUBSCRIBE request due to an
+ * IMS stack error. This is a terminating event, so no other callback event will be
+ * expected after this callback.
*
* @param code The reason why the associated command has failed.
* @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is
@@ -211,27 +216,38 @@
/**
* Notify the framework of the response to the SUBSCRIBE request from
* {@link #subscribeForCapabilities(List<Uri>, SubscribeResponseCallback)}.
+ * <p>
+ * If the carrier network responds to the SUBSCRIBE request with a 2XX response, then the
+ * framework will expect the IMS stack to call {@link #onNotifyCapabilitiesUpdate},
+ * {@link #onResourceTerminated}, and {@link #onTerminated} as required for the
+ * subsequent NOTIFY responses to the subscription.
*
- * @param code The SIP response code sent from the network for the operation
+ * @param sipCode The SIP response code sent from the network for the operation
* token specified.
* @param reason The optional reason response from the network. If the network
- * provided no reason with the code, the string should be empty.
+ * provided no reason with the sip code, the string should be empty.
* @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is
* not currently connected to the framework. This can happen if the
* {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the
* {@link RcsFeature} has not received the {@link ImsFeature#onFeatureReady()} callback.
* This may also happen in rare cases when the Telephony stack has crashed.
*/
- void onNetworkResponse(@IntRange(from = 100, to = 699) int code,
+ void onNetworkResponse(@IntRange(from = 100, to = 699) int sipCode,
@NonNull String reason) throws ImsException;
/**
- * Provides the framework with latest XML PIDF documents included in the
- * network response for the requested contacts' capabilities requested by the
- * Framework using {@link #requestCapabilities(List, int)}. This should be
- * called every time a new NOTIFY event is received with new capability
- * information.
+ * Notify the framework of the latest XML PIDF documents included in the network response
+ * for the requested contacts' capabilities requested by the Framework using
+ * {@link RcsUceAdapter#requestCapabilities(Executor, List<Uri>, CapabilitiesCallback)}.
+ * <p>
+ * The expected format for the PIDF XML is defined in RFC3861. Each XML document must be a
+ * "application/pidf+xml" object and start with a root <presence> element. For NOTIFY
+ * responses that contain RLMI information and potentially multiple PIDF XMLs, each
+ * PIDF XML should be separated and added as a separate item in the List. This should be
+ * called every time a new NOTIFY event is received with new capability information.
*
+ * @param pidfXmls The list of the PIDF XML data for the contact URIs that it subscribed
+ * for.
* @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is
* not currently connected to the framework.
* This can happen if the {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the
@@ -242,21 +258,42 @@
void onNotifyCapabilitiesUpdate(@NonNull List<String> pidfXmls) throws ImsException;
/**
- * A resource in the resource list for the presence subscribe event has been terminated.
+ * Notify the framework that a resource in the RLMI XML contained in the NOTIFY response
+ * for the ongoing SUBSCRIBE dialog has been terminated.
* <p>
- * This allows the framework to know that there will not be any capability information for
- * a specific contact URI that they subscribed for.
+ * This will be used to notify the framework that a contact URI that the IMS stack has
+ * subscribed to on the Resource List Server has been terminated as well as the reason why.
+ * Usually this means that there will not be any capability information for the contact URI
+ * that they subscribed for. See RFC 4662 for more information.
+ *
+ * @param uriTerminatedReason The contact URIs which have been terminated. Each pair in the
+ * list is the contact URI and its terminated reason.
+ * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is
+ * not currently connected to the framework.
+ * This can happen if the {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the
+ * {@link RcsFeature} {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not
+ * received the {@link ImsFeature#onFeatureReady()} callback. This may also happen in
+ * rare cases when the Telephony stack has crashed.
*/
void onResourceTerminated(
@NonNull List<Pair<Uri, String>> uriTerminatedReason) throws ImsException;
/**
- * The subscription associated with a previous #requestCapabilities operation
- * has been terminated. This will mostly be due to the subscription expiring,
- * but may also happen due to an error.
- * <p>
- * This allows the framework to know that there will no longer be any
- * capability updates for the requested operationToken.
+ * The subscription associated with a previous
+ * {@link RcsUceAdapter#requestCapabilities(Executor, List<Uri>, CapabilitiesCallback)}
+ * operation has been terminated. This will mostly be due to the network sending a final
+ * NOTIFY response due to the subscription expiring, but this may also happen due to a
+ * network error.
+ *
+ * @param reason The reason for the request being unable to process.
+ * @param retryAfterMilliseconds The time in milliseconds the requesting application should
+ * wait before retrying, if non-zero.
+ * @throws ImsException If this {@link RcsCapabilityExchangeImplBase} instance is
+ * not currently connected to the framework.
+ * This can happen if the {@link RcsFeature} is not {@link ImsFeature#STATE_READY} and the
+ * {@link RcsFeature} {@link ImsFeature#STATE_READY} and the {@link RcsFeature} has not
+ * received the {@link ImsFeature#onFeatureReady()} callback. This may also happen in
+ * rare cases when the Telephony stack has crashed.
*/
void onTerminated(@NonNull String reason, long retryAfterMilliseconds) throws ImsException;
}
@@ -278,18 +315,23 @@
/**
* The user capabilities of one or multiple contacts have been requested by the framework.
* <p>
+ * The implementer must follow up this call with an
+ * {@link SubscribeResponseCallback#onCommandError} call to indicate this operation has failed.
* The response from the network to the SUBSCRIBE request must be sent back to the framework
- * using {@link #onSubscribeNetworkResponse(int, String, int)}. As NOTIFY requests come in from
- * the network, the requested contact’s capabilities should be sent back to the framework using
- * {@link #onSubscribeNotifyRequest} and {@link onSubscribeResourceTerminated}
+ * using {@link SubscribeResponseCallback#onNetworkResponse(int, String)}.
+ * As NOTIFY requests come in from the network, the requested contact’s capabilities should be
+ * sent back to the framework using
+ * {@link SubscribeResponseCallback#onNotifyCapabilitiesUpdate(List<String>}) and
+ * {@link SubscribeResponseCallback#onResourceTerminated(List<Pair<Uri, String>>)}
* should be called with the presence information for the contacts specified.
* <p>
- * Once the subscription is terminated, {@link #onSubscriptionTerminated} must be called for
- * the framework to finish listening for NOTIFY responses.
+ * Once the subscription is terminated,
+ * {@link SubscribeResponseCallback#onTerminated(String, long)} must be called for the
+ * framework to finish listening for NOTIFY responses.
+ *
* @param uris A {@link List} of the {@link Uri}s that the framework is requesting the UCE
* capabilities for.
* @param cb The callback of the subscribe request.
- * @hide
*/
// executor used is defined in the constructor.
@SuppressLint("ExecutorRegistration")
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 2d3c8f2..ee6c36c 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2350,6 +2350,11 @@
*/
String getMobileProvisioningUrl();
+ /*
+ * Remove the EAB contacts from the EAB database.
+ */
+ int removeContactFromEab(int subId, String contacts);
+
/**
* Set a SignalStrengthUpdateRequest to receive notification when Signal Strength breach the
* specified thresholds.
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index b3092b9..137543a 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -64,6 +64,7 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY;
import static android.net.NetworkCapabilities.NET_CAPABILITY_RCS;
@@ -5963,23 +5964,18 @@
callback.expectCapabilitiesThat(mMockVpn,
nc -> nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)
&& nc.hasTransport(TRANSPORT_WIFI));
-
- // BUG: the VPN is no longer suspended, so a RESUMED callback should have been sent.
- // callback.expectCallback(CallbackEntry.RESUMED, mMockVpn);
+ callback.expectCallback(CallbackEntry.RESUMED, mMockVpn);
callback.assertNoCallback();
assertTrue(mCm.getNetworkCapabilities(mMockVpn.getNetwork())
.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED);
assertNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
- assertNetworkInfo(TYPE_VPN, DetailedState.SUSPENDED); // BUG: VPN caps have NOT_SUSPENDED.
+ assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
- // BUG: the device has connectivity, so this should return true.
- assertGetNetworkInfoOfGetActiveNetworkIsConnected(false);
+ assertGetNetworkInfoOfGetActiveNetworkIsConnected(true);
- // Unsuspend cellular and then switch back to it.
- // The same bug happens in the opposite direction: the VPN's capabilities correctly have
- // NOT_SUSPENDED, but the VPN's NetworkInfo is in state SUSPENDED.
+ // Unsuspend cellular and then switch back to it. The VPN remains not suspended.
mCellNetworkAgent.resume();
callback.assertNoCallback();
mWiFiNetworkAgent.disconnect();
@@ -5996,12 +5992,11 @@
.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
assertNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
assertNetworkInfo(TYPE_WIFI, DetailedState.DISCONNECTED);
- assertNetworkInfo(TYPE_VPN, DetailedState.SUSPENDED); // BUG: VPN caps have NOT_SUSPENDED.
+ assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.CONNECTED);
- // BUG: the device has connectivity, so this should return true.
- assertGetNetworkInfoOfGetActiveNetworkIsConnected(false);
+ assertGetNetworkInfoOfGetActiveNetworkIsConnected(true);
- // Re-suspending the current network fixes the problem.
+ // Suspend cellular and expect no connectivity.
mCellNetworkAgent.suspend();
callback.expectCapabilitiesThat(mMockVpn,
nc -> !nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)
@@ -6017,6 +6012,7 @@
assertActiveNetworkInfo(TYPE_MOBILE, DetailedState.SUSPENDED);
assertGetNetworkInfoOfGetActiveNetworkIsConnected(false);
+ // Resume cellular and expect that connectivity comes back.
mCellNetworkAgent.resume();
callback.expectCapabilitiesThat(mMockVpn,
nc -> nc.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)
@@ -6407,10 +6403,7 @@
&& caps.hasTransport(TRANSPORT_CELLULAR)
&& !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
&& !caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
- // While the SUSPENDED callback should in theory be sent here, it is not. This is
- // a bug in ConnectivityService, but as the SUSPENDED and RESUMED callbacks have never
- // been public and are deprecated and slated for removal, there is no sense in spending
- // resources fixing this bug now.
+ vpnNetworkCallback.expectCallback(CallbackEntry.SUSPENDED, mMockVpn);
assertDefaultNetworkCapabilities(userId, mCellNetworkAgent, mWiFiNetworkAgent);
// Use both again.
@@ -6422,8 +6415,7 @@
&& caps.hasTransport(TRANSPORT_CELLULAR) && caps.hasTransport(TRANSPORT_WIFI)
&& !caps.hasCapability(NET_CAPABILITY_NOT_METERED)
&& caps.hasCapability(NET_CAPABILITY_NOT_SUSPENDED));
- // As above, the RESUMED callback not being sent here is a bug, but not a bug that's
- // worth anybody's time to fix.
+ vpnNetworkCallback.expectCallback(CallbackEntry.RESUMED, mMockVpn);
assertDefaultNetworkCapabilities(userId, mCellNetworkAgent, mWiFiNetworkAgent);
// Disconnect cell. Receive update without even removing the dead network from the
@@ -7335,39 +7327,68 @@
b2.expectBroadcast();
}
+ /**
+ * Test mutable and requestable network capabilities such as
+ * {@link NetworkCapabilities#NET_CAPABILITY_TRUSTED} and
+ * {@link NetworkCapabilities#NET_CAPABILITY_NOT_VCN_MANAGED}. Verify that the
+ * {@code ConnectivityService} re-assign the networks accordingly.
+ */
@Test
- public final void testLoseTrusted() throws Exception {
- final NetworkRequest trustedRequest = new NetworkRequest.Builder()
- .addCapability(NET_CAPABILITY_TRUSTED)
- .build();
- final TestNetworkCallback trustedCallback = new TestNetworkCallback();
- mCm.requestNetwork(trustedRequest, trustedCallback);
+ public final void testLoseMutableAndRequestableCaps() throws Exception {
+ final int[] testCaps = new int [] {
+ NET_CAPABILITY_TRUSTED,
+ NET_CAPABILITY_NOT_VCN_MANAGED
+ };
+ for (final int testCap : testCaps) {
+ // Create requests with and without the testing capability.
+ final TestNetworkCallback callbackWithCap = new TestNetworkCallback();
+ final TestNetworkCallback callbackWithoutCap = new TestNetworkCallback();
+ mCm.requestNetwork(new NetworkRequest.Builder().addCapability(testCap).build(),
+ callbackWithCap);
+ mCm.requestNetwork(new NetworkRequest.Builder().removeCapability(testCap).build(),
+ callbackWithoutCap);
- mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
- mCellNetworkAgent.connect(true);
- trustedCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
- verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId));
- reset(mMockNetd);
+ // Setup networks with testing capability and verify the default network changes.
+ mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.addCapability(testCap);
+ mCellNetworkAgent.connect(true);
+ callbackWithCap.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ callbackWithoutCap.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
+ verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId));
+ reset(mMockNetd);
- mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
- mWiFiNetworkAgent.connect(true);
- trustedCallback.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
- verify(mMockNetd).networkSetDefault(eq(mWiFiNetworkAgent.getNetwork().netId));
- reset(mMockNetd);
+ mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI);
+ mWiFiNetworkAgent.addCapability(testCap);
+ mWiFiNetworkAgent.connect(true);
+ callbackWithCap.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
+ callbackWithoutCap.expectAvailableDoubleValidatedCallbacks(mWiFiNetworkAgent);
+ verify(mMockNetd).networkSetDefault(eq(mWiFiNetworkAgent.getNetwork().netId));
+ reset(mMockNetd);
- mWiFiNetworkAgent.removeCapability(NET_CAPABILITY_TRUSTED);
- trustedCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
- verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId));
- reset(mMockNetd);
+ // Remove the testing capability on wifi, verify the callback and default network
+ // changes back to cellular.
+ mWiFiNetworkAgent.removeCapability(testCap);
+ callbackWithCap.expectAvailableCallbacksValidated(mCellNetworkAgent);
+ callbackWithoutCap.expectCapabilitiesWithout(testCap, mWiFiNetworkAgent);
+ // TODO: Test default network changes for NOT_VCN_MANAGED once the default request has
+ // it.
+ if (testCap == NET_CAPABILITY_TRUSTED) {
+ verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId));
+ reset(mMockNetd);
+ }
- mCellNetworkAgent.removeCapability(NET_CAPABILITY_TRUSTED);
- trustedCallback.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
- verify(mMockNetd).networkClearDefault();
+ mCellNetworkAgent.removeCapability(testCap);
+ callbackWithCap.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
+ callbackWithoutCap.assertNoCallback();
+ if (testCap == NET_CAPABILITY_TRUSTED) {
+ verify(mMockNetd).networkClearDefault();
+ }
- mCm.unregisterNetworkCallback(trustedCallback);
+ mCm.unregisterNetworkCallback(callbackWithCap);
+ mCm.unregisterNetworkCallback(callbackWithoutCap);
+ }
}
- @Ignore // 40%+ flakiness : figure out why and re-enable.
@Test
public final void testBatteryStatsNetworkType() throws Exception {
final LinkProperties cellLp = new LinkProperties();
@@ -7375,8 +7396,8 @@
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp);
mCellNetworkAgent.connect(true);
waitForIdle();
- verify(mBatteryStatsService).noteNetworkInterfaceType(cellLp.getInterfaceName(),
- TYPE_MOBILE);
+ verify(mBatteryStatsService).noteNetworkInterfaceForTransports(cellLp.getInterfaceName(),
+ new int[] { TRANSPORT_CELLULAR });
reset(mBatteryStatsService);
final LinkProperties wifiLp = new LinkProperties();
@@ -7384,18 +7405,20 @@
mWiFiNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_WIFI, wifiLp);
mWiFiNetworkAgent.connect(true);
waitForIdle();
- verify(mBatteryStatsService).noteNetworkInterfaceType(wifiLp.getInterfaceName(),
- TYPE_WIFI);
+ verify(mBatteryStatsService).noteNetworkInterfaceForTransports(wifiLp.getInterfaceName(),
+ new int[] { TRANSPORT_WIFI });
reset(mBatteryStatsService);
mCellNetworkAgent.disconnect();
+ mWiFiNetworkAgent.disconnect();
cellLp.setInterfaceName("wifi0");
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp);
mCellNetworkAgent.connect(true);
waitForIdle();
- verify(mBatteryStatsService).noteNetworkInterfaceType(cellLp.getInterfaceName(),
- TYPE_MOBILE);
+ verify(mBatteryStatsService).noteNetworkInterfaceForTransports(cellLp.getInterfaceName(),
+ new int[] { TRANSPORT_CELLULAR });
+ mCellNetworkAgent.disconnect();
}
/**
@@ -7468,8 +7491,8 @@
assertRoutesAdded(cellNetId, ipv6Subnet, defaultRoute);
verify(mMockDnsResolver, times(1)).createNetworkCache(eq(cellNetId));
verify(mMockNetd, times(1)).networkAddInterface(cellNetId, MOBILE_IFNAME);
- verify(mBatteryStatsService).noteNetworkInterfaceType(cellLp.getInterfaceName(),
- TYPE_MOBILE);
+ verify(mBatteryStatsService).noteNetworkInterfaceForTransports(cellLp.getInterfaceName(),
+ new int[] { TRANSPORT_CELLULAR });
networkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent);
verify(mMockDnsResolver, times(1)).startPrefix64Discovery(cellNetId);
@@ -7489,7 +7512,8 @@
// Make sure BatteryStats was not told about any v4- interfaces, as none should have
// come online yet.
waitForIdle();
- verify(mBatteryStatsService, never()).noteNetworkInterfaceType(startsWith("v4-"), anyInt());
+ verify(mBatteryStatsService, never()).noteNetworkInterfaceForTransports(startsWith("v4-"),
+ any());
verifyNoMoreInteractions(mMockNetd);
verifyNoMoreInteractions(mMockDnsResolver);
@@ -7542,8 +7566,8 @@
assertTrue(ArrayUtils.contains(resolvrParams.servers, "8.8.8.8"));
for (final LinkProperties stackedLp : stackedLpsAfterChange) {
- verify(mBatteryStatsService).noteNetworkInterfaceType(stackedLp.getInterfaceName(),
- TYPE_MOBILE);
+ verify(mBatteryStatsService).noteNetworkInterfaceForTransports(
+ stackedLp.getInterfaceName(), new int[] { TRANSPORT_CELLULAR });
}
reset(mMockNetd);
when(mMockNetd.interfaceGetCfg(CLAT_PREFIX + MOBILE_IFNAME))
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java
new file mode 100644
index 0000000..d936183
--- /dev/null
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vcn;
+
+import static com.android.server.vcn.VcnGatewayConnection.VcnIkeSession;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/** Tests for VcnGatewayConnection.ConnectingState */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class VcnGatewayConnectionConnectingStateTest extends VcnGatewayConnectionTestBase {
+ private VcnIkeSession mIkeSession;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+
+ mGatewayConnection.setUnderlyingNetwork(TEST_UNDERLYING_NETWORK_RECORD_1);
+ mGatewayConnection.transitionTo(mGatewayConnection.mConnectingState);
+ mTestLooper.dispatchAll();
+
+ mIkeSession = mGatewayConnection.getIkeSession();
+ }
+
+ @Test
+ public void testEnterStateCreatesNewIkeSession() throws Exception {
+ verify(mDeps).newIkeSession(any(), any(), any(), any(), any());
+ }
+
+ @Test
+ public void testNullNetworkTriggersDisconnect() throws Exception {
+ mGatewayConnection
+ .getUnderlyingNetworkTrackerCallback()
+ .onSelectedUnderlyingNetworkChanged(null);
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState());
+ verify(mIkeSession).kill();
+ }
+
+ @Test
+ public void testNewNetworkTriggersReconnect() throws Exception {
+ mGatewayConnection
+ .getUnderlyingNetworkTrackerCallback()
+ .onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_2);
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState());
+ verify(mIkeSession).close();
+ verify(mIkeSession, never()).kill();
+ }
+
+ @Test
+ public void testSameNetworkDoesNotTriggerReconnect() throws Exception {
+ mGatewayConnection
+ .getUnderlyingNetworkTrackerCallback()
+ .onSelectedUnderlyingNetworkChanged(TEST_UNDERLYING_NETWORK_RECORD_1);
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mConnectingState, mGatewayConnection.getCurrentState());
+ }
+
+ @Test
+ public void testChildSessionClosedTriggersDisconnect() throws Exception {
+ getChildSessionCallback().onClosed();
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState());
+ verify(mIkeSession).close();
+ }
+
+ @Test
+ public void testIkeSessionClosedTriggersDisconnect() throws Exception {
+ getIkeSessionCallback().onClosed();
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mRetryTimeoutState, mGatewayConnection.getCurrentState());
+ verify(mIkeSession).close();
+ }
+}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java
new file mode 100644
index 0000000..d0fec55
--- /dev/null
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vcn;
+
+import static com.android.server.vcn.VcnGatewayConnection.TEARDOWN_TIMEOUT_SECONDS;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.verify;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.TimeUnit;
+
+/** Tests for VcnGatewayConnection.DisconnectedState */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class VcnGatewayConnectionDisconnectingStateTest extends VcnGatewayConnectionTestBase {
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+
+ mGatewayConnection.setIkeSession(mGatewayConnection.buildIkeSession());
+
+ mGatewayConnection.transitionTo(mGatewayConnection.mDisconnectingState);
+ mTestLooper.dispatchAll();
+ }
+
+ @Test
+ public void testIkeSessionClosed() throws Exception {
+ getIkeSessionCallback().onClosed();
+ mTestLooper.dispatchAll();
+
+ assertEquals(mGatewayConnection.mDisconnectedState, mGatewayConnection.getCurrentState());
+ }
+
+ @Test
+ public void testTimeoutExpired() throws Exception {
+ mTestLooper.moveTimeForward(TimeUnit.SECONDS.toMillis(TEARDOWN_TIMEOUT_SECONDS));
+ mTestLooper.dispatchAll();
+
+ verify(mMockIkeSession).kill();
+ }
+
+ @Test
+ public void testTeardown() throws Exception {
+ mGatewayConnection.teardownAsynchronously();
+ mTestLooper.dispatchAll();
+
+ // Should do nothing; already tearing down.
+ assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState());
+ }
+}
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
index 1725dd9..b4d39bf 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
@@ -17,11 +17,13 @@
package com.android.server.vcn;
import static com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord;
+import static com.android.server.vcn.VcnGatewayConnection.VcnIkeSession;
import static com.android.server.vcn.VcnTestUtils.setupIpSecManager;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
import android.annotation.NonNull;
import android.content.Context;
@@ -30,6 +32,8 @@
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
+import android.net.ipsec.ike.ChildSessionCallback;
+import android.net.ipsec.ike.IkeSessionCallback;
import android.net.vcn.VcnGatewayConnectionConfig;
import android.net.vcn.VcnGatewayConnectionConfigTest;
import android.os.ParcelUuid;
@@ -38,6 +42,7 @@
import com.android.server.IpSecService;
import org.junit.Before;
+import org.mockito.ArgumentCaptor;
import java.util.UUID;
@@ -68,6 +73,7 @@
@NonNull protected final IpSecService mIpSecSvc;
+ protected VcnIkeSession mMockIkeSession;
protected VcnGatewayConnection mGatewayConnection;
public VcnGatewayConnectionTestBase() {
@@ -100,6 +106,23 @@
TEST_IPSEC_TUNNEL_IFACE);
doReturn(resp).when(mIpSecSvc).createTunnelInterface(any(), any(), any(), any(), any());
+ mMockIkeSession = mock(VcnIkeSession.class);
+ doReturn(mMockIkeSession).when(mDeps).newIkeSession(any(), any(), any(), any(), any());
+
mGatewayConnection = new VcnGatewayConnection(mVcnContext, TEST_SUB_GRP, mConfig, mDeps);
}
+
+ protected IkeSessionCallback getIkeSessionCallback() {
+ ArgumentCaptor<IkeSessionCallback> captor =
+ ArgumentCaptor.forClass(IkeSessionCallback.class);
+ verify(mDeps).newIkeSession(any(), any(), any(), captor.capture(), any());
+ return captor.getValue();
+ }
+
+ protected ChildSessionCallback getChildSessionCallback() {
+ ArgumentCaptor<ChildSessionCallback> captor =
+ ArgumentCaptor.forClass(ChildSessionCallback.class);
+ verify(mDeps).newIkeSession(any(), any(), any(), any(), captor.capture());
+ return captor.getValue();
+ }
}
diff --git a/tools/powerstats/PowerStatsServiceProtoParser.java b/tools/powerstats/PowerStatsServiceProtoParser.java
index e4135b5..2140954 100644
--- a/tools/powerstats/PowerStatsServiceProtoParser.java
+++ b/tools/powerstats/PowerStatsServiceProtoParser.java
@@ -30,7 +30,7 @@
for (int i = 0; i < proto.getChannelCount(); i++) {
ChannelProto energyMeterInfo = proto.getChannel(i);
csvHeader += "Index,Timestamp,Duration," + energyMeterInfo.getId()
- + "/" + energyMeterInfo.getName() + ",";
+ + "/" + energyMeterInfo.getName() + "/" + energyMeterInfo.getSubsystem() + ",";
}
System.out.println(csvHeader);
}