diff options
117 files changed, 1197 insertions, 5705 deletions
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 000000000000..a1719c9c31d1 --- /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/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java index d44169d16d5d..d4ee9bb813c9 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 @@ public class AlarmManagerService extends SystemService { 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 be51143e4730..000000000000 --- 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 957e10a582a0..000000000000 --- 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 6e67ce92a27e..000000000000 --- 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 6350d54d695e..000000000000 --- 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 Binary files differdeleted file mode 100644 index 9eaf85259637..000000000000 --- a/apex/permission/com.android.permission.avbpubkey +++ /dev/null diff --git a/apex/permission/com.android.permission.pem b/apex/permission/com.android.permission.pem deleted file mode 100644 index 3d584be5440d..000000000000 --- 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 Binary files differdeleted file mode 100644 index d51673dbc2fc..000000000000 --- a/apex/permission/com.android.permission.pk8 +++ /dev/null diff --git a/apex/permission/com.android.permission.x509.pem b/apex/permission/com.android.permission.x509.pem deleted file mode 100644 index 4b146c9edd4f..000000000000 --- 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 e71cc43f13fe..000000000000 --- 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 4ecc98980c43..000000000000 --- 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 d7c9a2395c04..000000000000 --- 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 d802177e249b..000000000000 --- 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 d802177e249b..000000000000 --- 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 6778d4826841..000000000000 --- 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 d802177e249b..000000000000 --- 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 6cf961fad2c4..000000000000 --- 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 8a43d7fa9036..000000000000 --- 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 5fc25f0422e2..000000000000 --- 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 5958debc86dd..000000000000 --- 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 93a7ae0c82c0..000000000000 --- 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 cf7872913f26..000000000000 --- 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 7a97770ecf0f..000000000000 --- 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 ceccc4cfc9f7..000000000000 --- 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 f8f140dd716e..000000000000 --- 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 52a61674a596..000000000000 --- 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 d802177e249b..000000000000 --- 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 d802177e249b..000000000000 --- 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 d802177e249b..000000000000 --- 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 d802177e249b..000000000000 --- 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 d802177e249b..000000000000 --- 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 d802177e249b..000000000000 --- 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 e810db8ecbfe..000000000000 --- 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 4729ed13dc4c..000000000000 --- 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 d0fc5b9d7c14..000000000000 --- 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 d802177e249b..000000000000 --- 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 d802177e249b..000000000000 --- 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 b1869c2c731d..000000000000 --- 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 d802177e249b..000000000000 --- 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 7c711d301f00..000000000000 --- 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 c89cc8eabb92..000000000000 --- 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 569a78c0ab41..000000000000 --- 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 aedba290db1f..000000000000 --- 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 e43f59a3377a..000000000000 --- 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 c6bfc6d32989..000000000000 --- 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 5d5cd78201bd..000000000000 --- 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 7308eec98500..000000000000 --- 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 ea4952404179..000000000000 --- 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 cd6f6057030b..000000000000 --- 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 ba1c3939f73e..000000000000 --- 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/permission/util/package-info.java b/apex/permission/service/java/com/android/permission/util/package-info.java deleted file mode 100644 index 18fada534424..000000000000 --- a/apex/permission/service/java/com/android/permission/util/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.util; 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 e243e2e0db66..000000000000 --- 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 5f7eb22a42a7..000000000000 --- 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 03b7c76d2df5..000000000000 --- 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 78d8d15bbe60..000000000000 --- 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 0d7bc1476bd1..000000000000 --- 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 8b5b2516105f..000000000000 --- 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 2e5a28aa1d6a..000000000000 --- 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 f66257f13ef6..000000000000 --- 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 f61efa0e840d..000000000000 --- 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 79c42299207c..000000000000 --- 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 63bf0a08e956..000000000000 --- 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 bc19a9ea0172..000000000000 --- 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 271e328c1139..000000000000 --- 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 57ee6417aeb3..000000000000 --- 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 2987da087e51..000000000000 --- 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 f9d9d5afb25d..000000000000 --- 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/core/api/current.txt b/core/api/current.txt index 61d7aadce928..ec712d875323 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -36822,9 +36822,10 @@ package android.security.identity { 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 { @@ -39714,7 +39715,6 @@ package android.telecom { 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"; @@ -39723,6 +39723,7 @@ package android.telecom { 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 7c426be80fa4..0f1296ee01fe 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -12964,11 +12964,100 @@ package android.telephony.ims { 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 @@ -12981,6 +13070,18 @@ package android.telephony.ims { 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 @@ -12989,6 +13090,12 @@ package android.telephony.ims { 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); } @@ -13400,6 +13507,7 @@ package android.telephony.ims.stub { 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 @@ -13418,6 +13526,14 @@ package android.telephony.ims.stub { 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/content/integrity/OWNERS b/core/java/android/content/integrity/OWNERS new file mode 100644 index 000000000000..20c758aedd67 --- /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/provider/CallLog.java b/core/java/android/provider/CallLog.java index 33e33ee5fa02..9bfd75ef2170 100644 --- a/core/java/android/provider/CallLog.java +++ b/core/java/android/provider/CallLog.java @@ -249,11 +249,13 @@ public class CallLog { // 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/view/SyncRtSurfaceTransactionApplier.java b/core/java/android/view/SyncRtSurfaceTransactionApplier.java index b10370aa5d4c..acbcbfad1a75 100644 --- a/core/java/android/view/SyncRtSurfaceTransactionApplier.java +++ b/core/java/android/view/SyncRtSurfaceTransactionApplier.java @@ -85,12 +85,13 @@ public class SyncRtSurfaceTransactionApplier { for (int i = params.length - 1; i >= 0; i--) { SurfaceParams surfaceParams = params[i]; SurfaceControl surface = surfaceParams.surface; - if (frame > 0) { - t.deferTransactionUntil(surface, mTargetSc, frame); - } applyParams(t, surfaceParams, mTmpFloat9); } - t.apply(); + if (mTargetViewRootImpl != null) { + mTargetViewRootImpl.mergeWithNextTransaction(t, frame); + } else { + t.apply(); + } } public static void applyParams(Transaction t, SurfaceParams params, float[] tmpFloat9) { diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 844fc267c3cb..52d0062abdf2 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -10109,7 +10109,7 @@ public final class ViewRootImpl implements ViewParent, * Merges the transaction passed in with the next transaction in BLASTBufferQueue. This ensures * you can add transactions to the upcoming frame. */ - void mergeWithNextTransaction(Transaction t, long frameNumber) { + public void mergeWithNextTransaction(Transaction t, long frameNumber) { if (mBlastBufferQueue != null) { mBlastBufferQueue.mergeWithNextTransaction(t, frameNumber); } diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java index a21c68b4f01a..fcf8bb4e748b 100644 --- a/core/java/com/android/internal/os/BatteryStatsHelper.java +++ b/core/java/com/android/internal/os/BatteryStatsHelper.java @@ -41,7 +41,6 @@ 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; @@ -379,6 +378,7 @@ public class BatteryStatsHelper { 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); @@ -556,8 +556,7 @@ public class BatteryStatsHelper { } /** - * 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} @@ -565,7 +564,6 @@ public class BatteryStatsHelper { */ 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); @@ -581,45 +579,11 @@ public class BatteryStatsHelper { 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) { @@ -682,33 +646,6 @@ public class BatteryStatsHelper { } @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/ScreenPowerCalculator.java b/core/java/com/android/internal/os/ScreenPowerCalculator.java index 25f6b4d16971..9c4a26724ba8 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.BatteryStats; 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 @@ public class ScreenPowerCalculator extends PowerCalculator { .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 @@ public class ScreenPowerCalculator extends PowerCalculator { bs.usageTimeMs = durationMs; bs.sumPower(); sippers.add(bs); + + smearScreenBatterySipper(sippers, bs); } } @@ -96,4 +104,60 @@ public class ScreenPowerCalculator extends PowerCalculator { } 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/proto/android/os/incident.proto b/core/proto/android/os/incident.proto index 48094198c2e9..2b665c0fe9fc 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/jobscheduler.proto"; 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/privacy.proto"; 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 ec9bc112c6fb..0c5a36049a29 100644 --- a/core/proto/android/server/powerstatsservice.proto +++ b/core/proto/android/server/powerstatsservice.proto @@ -220,6 +220,9 @@ message ChannelProto { /** 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/tests/coretests/src/android/content/OWNERS b/core/tests/coretests/src/android/content/OWNERS index 912db1e835dc..c61a4b538a44 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 000000000000..4ffc7041a527 --- /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 7b7670696bfa..867336515ce3 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 000000000000..3e79d8ff0bbe --- /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/com/android/internal/os/BatteryStatsHelperTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java index fbe16f2c39d1..d2107eaefefc 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 android.os.BatteryStats.Uid.PROCESS_STATE_TOP; 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 @@ public class BatteryStatsHelperTest extends TestCase { 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 @@ public class BatteryStatsHelperTest extends TestCase { @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 @@ public class BatteryStatsHelperTest extends TestCase { @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 @@ public class BatteryStatsHelperTest extends TestCase { } 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/keystore/java/android/security/KeyStore2.java b/keystore/java/android/security/KeyStore2.java index 92d87aa0fed6..f7477bf92c81 100644 --- a/keystore/java/android/security/KeyStore2.java +++ b/keystore/java/android/security/KeyStore2.java @@ -23,6 +23,7 @@ import android.os.Build; 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 @@ public class KeyStore2 { 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 @@ public class KeyStore2 { } } + 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 7ea9e1438845..a6552dddc630 100644 --- a/keystore/java/android/security/KeyStoreOperation.java +++ b/keystore/java/android/security/KeyStoreOperation.java @@ -73,8 +73,7 @@ public class KeyStoreOperation { ); } 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 3ef4aa5b7ec3..372add9b7ecb 100644 --- a/keystore/java/android/security/KeyStoreSecurityLevel.java +++ b/keystore/java/android/security/KeyStoreSecurityLevel.java @@ -52,7 +52,7 @@ public class KeyStoreSecurityLevel { 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 @@ public class KeyStoreSecurityLevel { } 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 1a88469d7e54..a813e939a720 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/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java index aeea10d47a67..90992fb92324 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 @@ public class PipAnimationController { 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/packages/SystemUI/res/layout/long_screenshot.xml b/packages/SystemUI/res/layout/long_screenshot.xml new file mode 100644 index 000000000000..fc68a64ac9bd --- /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/shared/src/com/android/systemui/shared/plugins/PluginInitializer.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInitializer.java index c3815e4cee78..42bc1d0ea0ff 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 @@ public interface PluginInitializer { * 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 ee7030a8cf5f..1a4e2d1665f6 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.PackageManager.NameNotFoundException; 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 @@ public class PluginInstanceManager<T extends Plugin> { 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 6d67f2147d37..f5ed9da15fa3 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 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage 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 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage 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 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage 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 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage 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 class PluginManagerImpl extends BroadcastReceiver implements PluginManage }); } + public boolean isDebuggable() { + return mIsDebuggable; + } + public String[] getWhitelistedPlugins() { return mWhitelistedPlugins.toArray(new String[0]); } @@ -297,7 +299,7 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage /** 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/SyncRtSurfaceTransactionApplierCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java index 70021b6f3d45..fbabaa489d74 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java @@ -114,10 +114,13 @@ public class SyncRtSurfaceTransactionApplierCompat { for (int i = params.length - 1; i >= 0; i--) { SyncRtSurfaceTransactionApplierCompat.SurfaceParams surfaceParams = params[i]; - t.deferTransactionUntil(surfaceParams.surface, mBarrierSurfaceControl, frame); surfaceParams.applyTo(t); } - t.apply(); + if (mTargetViewRootImpl != null) { + mTargetViewRootImpl.mergeWithNextTransaction(t, frame); + } else { + t.apply(); + } Trace.traceEnd(Trace.TRACE_TAG_VIEW); Message.obtain(mApplyHandler, MSG_UPDATE_SEQUENCE_NUMBER, toApplySeqNo, 0) .sendToTarget(); diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java index 4a28d56a41e1..89c60f1d3f06 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java @@ -56,4 +56,12 @@ public class ViewRootImplCompat { }); } } + + public void mergeWithNextTransaction(SurfaceControl.Transaction t, long frame) { + if (mViewRoot != null) { + mViewRoot.mergeWithNextTransaction(t, frame); + } else { + t.apply(); + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java index 95029c013ab6..7f01d6f1ffa3 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 @@ public class PluginInitializerImpl implements PluginInitializer { }); } } + + @Override + public boolean isDebuggable() { + return Build.IS_DEBUGGABLE; + } } diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageTile.java b/packages/SystemUI/src/com/android/systemui/screenshot/ImageTile.java index 143121af9f2c..212e6c86e9da 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 android.graphics.ColorSpace.Named.SRGB; 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 @@ class ImageTile implements AutoCloseable { * @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 @@ class ImageTile implements AutoCloseable { 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 @@ class ImageTile implements AutoCloseable { } @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 4431b6974b7b..d6413ed63e6e 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java @@ -603,7 +603,18 @@ public class ScreenshotController { cancelTimeout(); ScrollCaptureController controller = new ScrollCaptureController(mContext, connection, mMainExecutor, mBgExecutor, mImageExporter); - controller.start(/* onDismiss */ () -> dismissScreenshot(false)); + controller.attach(mWindow); + controller.start(new TakeScreenshotService.RequestCallback() { + @Override + public void reportError() { + } + + @Override + public void onFinish() { + Log.d(TAG, "onFinish from ScrollCaptureController"); + finishDismiss(); + } + }); } /** diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java index 825c85769e03..c2c67903da86 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 @@ import java.util.function.Consumer; /** * 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 @@ public class ScrollCaptureController { 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 @@ public class ScrollCaptureController { 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 start(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); + } + + + /** 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 startCapture(Session session, final Runnable onDismiss) { + 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 @@ public class ScrollCaptureController { 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 @@ public class ScrollCaptureController { 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/services/core/java/com/android/server/am/PhantomProcessList.java b/services/core/java/com/android/server/am/PhantomProcessList.java index 5167c5719955..37b17411dac5 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 com.android.internal.os.ProcessCpuTracker; import libcore.io.IoUtils; +import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileNotFoundException; @@ -109,10 +110,23 @@ public final class PhantomProcessList { 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 @@ public final class PhantomProcessList { } } + 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/powerstats/PowerStatsHALWrapper.java b/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java index 3e39b4bbe1d0..eb9df756dd0e 100644 --- a/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java +++ b/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java @@ -295,14 +295,14 @@ public final class PowerStatsHALWrapper { @Override public android.hardware.power.stats.EnergyConsumer[] getEnergyConsumerInfo() { if (DEBUG) Slog.d(TAG, "Energy consumer info is not supported"); - return null; + return new android.hardware.power.stats.EnergyConsumer[0]; } @Override public android.hardware.power.stats.EnergyConsumerResult[] getEnergyConsumed( int[] energyConsumerIds) { if (DEBUG) Slog.d(TAG, "Energy consumer results are not supported"); - return null; + return new android.hardware.power.stats.EnergyConsumerResult[0]; } @Override diff --git a/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java b/services/core/java/com/android/server/powerstats/ProtoStreamUtils.java index e71b962baef7..766cf9c1b678 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 @@ public class ProtoStreamUtils { 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 @@ public class ProtoStreamUtils { 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 @@ public class ProtoStreamUtils { 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/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java index 8805fa2f4dbb..703bfab6d868 100644 --- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java +++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java @@ -638,6 +638,22 @@ public class VcnGatewayConnection extends StateMachine { 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) { @@ -664,18 +680,11 @@ public class VcnGatewayConnection extends StateMachine { } } - 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 @@ -768,6 +777,20 @@ public class VcnGatewayConnection extends StateMachine { * 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 enterState() throws Exception { if (mIkeSession == null) { @@ -783,6 +806,7 @@ public class VcnGatewayConnection extends StateMachine { return; } + mIkeSession.close(); sendMessageDelayed( EVENT_TEARDOWN_TIMEOUT_EXPIRED, mCurrentToken, @@ -822,7 +846,7 @@ public class VcnGatewayConnection extends StateMachine { mIkeSession = null; if (mIsRunning && mUnderlying != null) { - transitionTo(mRetryTimeoutState); + transitionTo(mSkipRetryTimeout ? mConnectingState : mRetryTimeoutState); } else { teardownNetwork(); transitionTo(mDisconnectedState); @@ -833,6 +857,11 @@ public class VcnGatewayConnection extends StateMachine { break; } } + + @Override + protected void exitState() throws Exception { + mSkipRetryTimeout = false; + } } /** @@ -843,7 +872,69 @@ public class VcnGatewayConnection extends StateMachine { */ 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 {} @@ -1015,12 +1106,12 @@ public class VcnGatewayConnection extends StateMachine { } private IkeSessionParams buildIkeParams() { - // TODO: Implement this with ConnectingState + // TODO: Implement this once IkeSessionParams is persisted return null; } private ChildSessionParams buildChildParams() { - // TODO: Implement this with ConnectingState + // TODO: Implement this once IkeSessionParams is persisted return null; } diff --git a/services/core/jni/com_android_server_powerstats_PowerStatsService.cpp b/services/core/jni/com_android_server_powerstats_PowerStatsService.cpp index ec2549c5991c..1208354899d5 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 jclass class_C; 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 @@ static jobjectArray nativeGetEnergyMeterInfo(JNIEnv *env, jclass clazz) { 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 @@ static jboolean nativeInit(JNIEnv *env, jclass clazz) { 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/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index fd8dfc32e5d3..d28c3cc8e2b8 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -1705,15 +1705,6 @@ public final class SystemServer implements Dumpable { } 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(); @@ -1815,6 +1806,15 @@ public final class SystemServer implements Dumpable { 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); @@ -2632,15 +2632,6 @@ public final class SystemServer implements Dumpable { 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) { @@ -2659,6 +2650,15 @@ public final class SystemServer implements Dumpable { 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 b85da9460476..17f326fbdbce 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 @@ public class AppChildProcessTest { } 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/powerstats/PowerStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/powerstats/PowerStatsServiceTest.java index b6ae8555b02e..84b690f01b02 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 @@ public class PowerStatsServiceTest { 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 @@ public class PowerStatsServiceTest { 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 @@ public class PowerStatsServiceTest { 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 @@ public class PowerStatsServiceTest { 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 @@ public class PowerStatsServiceTest { 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/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java index 5b03863efc7d..472d63946ebc 100644 --- a/telecomm/java/android/telecom/TelecomManager.java +++ b/telecomm/java/android/telecom/TelecomManager.java @@ -314,20 +314,22 @@ public class TelecomManager { 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 000000000000..1cab267cc817 --- /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 519d0164b0d6..5eb75e762fc9 100644 --- a/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java +++ b/telephony/java/android/telephony/ims/RcsContactPresenceTuple.java @@ -19,6 +19,7 @@ package android.telephony.ims; 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 @@ import java.util.List; * 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 @@ public final class RcsContactPresenceTuple implements Parcelable { in.readStringList(mSupportedDuplexModeList); in.readStringList(mUnsupportedDuplexModeList); } + @Override public void writeToParcel(@NonNull Parcel out, int flags) { out.writeBoolean(mIsAudioCapable); @@ -217,12 +340,14 @@ public final class RcsContactPresenceTuple implements Parcelable { /** * 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 @@ public final class RcsContactPresenceTuple implements Parcelable { /** * 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 @@ public final class RcsContactPresenceTuple implements Parcelable { * 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 @@ public final class RcsContactPresenceTuple implements Parcelable { * 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 d4715bfeeb3e..fe855023f5d0 100644 --- a/telephony/java/android/telephony/ims/RcsContactUceCapability.java +++ b/telephony/java/android/telephony/ims/RcsContactUceCapability.java @@ -19,6 +19,7 @@ package android.telephony.ims; 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 @@ import java.util.List; * 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 final class RcsContactUceCapability implements Parcelable { 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 @@ public final class RcsContactUceCapability implements Parcelable { /** * @return the constructed instance. */ - @Override public @NonNull RcsContactUceCapability build() { return mCapabilities; } @@ -172,7 +167,7 @@ public final class RcsContactUceCapability implements Parcelable { * 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 @@ public final class RcsContactUceCapability implements Parcelable { /** * @return the RcsContactUceCapability instance. */ - @Override public @NonNull RcsContactUceCapability build() { return mCapabilities; } @@ -284,6 +278,7 @@ public final class RcsContactUceCapability implements Parcelable { * <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 @@ public final class RcsContactUceCapability implements Parcelable { * 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 @@ public final class RcsContactUceCapability implements Parcelable { /** * 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 6c31466c2a89..070fd799d6cc 100644 --- a/telephony/java/android/telephony/ims/RcsUceAdapter.java +++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java @@ -63,6 +63,7 @@ public class RcsUceAdapter { * 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 @@ public class RcsUceAdapter { * 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 @@ public class RcsUceAdapter { * 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 @@ public class RcsUceAdapter { * 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 @@ public class RcsUceAdapter { * subscriber to the carrier network. * @hide */ + @SystemApi public static final int ERROR_NOT_FOUND = 7; /** @@ -123,6 +131,7 @@ public class RcsUceAdapter { * 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 @@ public class RcsUceAdapter { * 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 @@ public class RcsUceAdapter { * time returned in {@link CapabilitiesCallback#onError} has elapsed. * @hide */ + @SystemApi public static final int ERROR_SERVER_UNAVAILABLE = 12; /**@hide*/ @@ -405,6 +418,7 @@ public class RcsUceAdapter { * @see #requestCapabilities(Executor, List, CapabilitiesCallback) * @hide */ + @SystemApi public interface CapabilitiesCallback { /** @@ -424,10 +438,10 @@ public class RcsUceAdapter { * 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 @@ public class RcsUceAdapter { * {@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 @@ public class RcsUceAdapter { * 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 class RcsUceAdapter { 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 @@ public class RcsUceAdapter { * {@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 @@ public class RcsUceAdapter { 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 class RcsUceAdapter { 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 @@ public class RcsUceAdapter { }; 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 @@ public class RcsUceAdapter { 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 @@ public class RcsUceAdapter { } 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 36349895c35b..7a6c28bddd09 100644 --- a/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl +++ b/telephony/java/android/telephony/ims/aidl/IImsRcsController.aidl @@ -52,7 +52,7 @@ interface IImsRcsController { // 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 c84e23c38e97..7eba709a11da 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.SuppressLint; 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 @@ public class RcsCapabilityExchangeImplBase { * 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 @@ public class RcsCapabilityExchangeImplBase { /** * 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 @@ public class RcsCapabilityExchangeImplBase { * {@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 @@ public class RcsCapabilityExchangeImplBase { /** * 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 @@ public class RcsCapabilityExchangeImplBase { 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 @@ public class RcsCapabilityExchangeImplBase { /** * 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 2d3c8f29ec9c..ee6c36ca6b76 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -2350,6 +2350,11 @@ interface ITelephony { */ 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/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java new file mode 100644 index 000000000000..d936183e5a0b --- /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/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java index 346785907fcf..b4d39bf74a4b 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java @@ -32,6 +32,7 @@ import android.net.IpSecTunnelInterfaceResponse; 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; @@ -117,4 +118,11 @@ public class VcnGatewayConnectionTestBase { 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 e4135b5584e4..21409542714d 100644 --- a/tools/powerstats/PowerStatsServiceProtoParser.java +++ b/tools/powerstats/PowerStatsServiceProtoParser.java @@ -30,7 +30,7 @@ public class PowerStatsServiceProtoParser { 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); } |