Merge "Migrate apex/permission to packages/modules/Permission" into sc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
018d654a1f
@@ -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"],
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
svetoslavganov@google.com
|
||||
moltmann@google.com
|
||||
eugenesusla@google.com
|
||||
zhanghai@google.com
|
||||
evanseverson@google.com
|
||||
ntmyren@google.com
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"presubmit" : [
|
||||
{
|
||||
"name" : "PermissionApexTests"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"name": "com.android.permission",
|
||||
"version": 309999999
|
||||
}
|
||||
Binary file not shown.
@@ -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-----
|
||||
Binary file not shown.
@@ -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-----
|
||||
@@ -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",
|
||||
],
|
||||
}
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
// Signature format: 2.0
|
||||
@@ -1 +0,0 @@
|
||||
// Signature format: 2.0
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
// Signature format: 2.0
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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)));
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"presubmit": [
|
||||
{
|
||||
"name": "CtsRoleTestCases",
|
||||
"options": [
|
||||
{
|
||||
"include-filter": "android.app.role.cts.RoleManagerTest"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
// Signature format: 2.0
|
||||
@@ -1 +0,0 @@
|
||||
// Signature format: 2.0
|
||||
@@ -1 +0,0 @@
|
||||
// Signature format: 2.0
|
||||
@@ -1 +0,0 @@
|
||||
// Signature format: 2.0
|
||||
@@ -1 +0,0 @@
|
||||
// Signature format: 2.0
|
||||
@@ -1 +0,0 @@
|
||||
// Signature format: 2.0
|
||||
@@ -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 {}
|
||||
@@ -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
|
||||
@@ -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,
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
// Signature format: 2.0
|
||||
@@ -1 +0,0 @@
|
||||
// Signature format: 2.0
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
// Signature format: 2.0
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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.
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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;
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"name": "com.android.permission",
|
||||
"version": 2147483647
|
||||
}
|
||||
@@ -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",
|
||||
],
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user