Merge "Migrate apex/permission to packages/modules/Permission" into sc-dev

This commit is contained in:
TreeHugger Robot
2021-01-28 21:34:41 +00:00
committed by Android (Google) Code Review
67 changed files with 1 additions and 5426 deletions

View File

@@ -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"],
}

View File

@@ -1,6 +0,0 @@
svetoslavganov@google.com
moltmann@google.com
eugenesusla@google.com
zhanghai@google.com
evanseverson@google.com
ntmyren@google.com

View File

@@ -1,7 +0,0 @@
{
"presubmit" : [
{
"name" : "PermissionApexTests"
}
]
}

View File

@@ -1,4 +0,0 @@
{
"name": "com.android.permission",
"version": 309999999
}

View File

@@ -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-----

View File

@@ -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-----

View File

@@ -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",
],
}

View File

@@ -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";
}
}

View File

@@ -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);
}
}

View File

@@ -1 +0,0 @@
// Signature format: 2.0

View File

@@ -1 +0,0 @@
// Signature format: 2.0

View File

@@ -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
}
}

View File

@@ -1 +0,0 @@
// Signature format: 2.0

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);
}
});
}
}

View File

@@ -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);
}

View File

@@ -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)));
}
}

View File

@@ -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);
}
}
}
}

View File

@@ -1,12 +0,0 @@
{
"presubmit": [
{
"name": "CtsRoleTestCases",
"options": [
{
"include-filter": "android.app.role.cts.RoleManagerTest"
}
]
}
]
}

View File

@@ -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,
}

View File

@@ -1 +0,0 @@
// Signature format: 2.0

View File

@@ -1 +0,0 @@
// Signature format: 2.0

View File

@@ -1 +0,0 @@
// Signature format: 2.0

View File

@@ -1 +0,0 @@
// Signature format: 2.0

View File

@@ -1 +0,0 @@
// Signature format: 2.0

View File

@@ -1 +0,0 @@
// Signature format: 2.0

View File

@@ -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 {}

View File

@@ -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

View File

@@ -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,
}

View File

@@ -1 +0,0 @@
// Signature format: 2.0

View File

@@ -1 +0,0 @@
// Signature format: 2.0

View File

@@ -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();
}
}

View File

@@ -1 +0,0 @@
// Signature format: 2.0

View File

@@ -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();
}
}

View File

@@ -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;

View File

@@ -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.
}
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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();
}
}
}

View File

@@ -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();
}
}

View File

@@ -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);
}
}

View File

@@ -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"
}
]
}
]
}

View File

@@ -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;

View File

@@ -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();
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}

View File

@@ -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,
}

View File

@@ -1,4 +0,0 @@
{
"name": "com.android.permission",
"version": 2147483647
}

View File

@@ -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",
],
}

View File

@@ -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>

View File

@@ -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"
}
}

View File

@@ -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"
}
}

View File

@@ -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;