Skip to content

Commit 9bf06fa

Browse files
committed
add special runtime permissions test cases
See the README.md for more info. Run tests with atest SpecialRuntimePermsTestCases
1 parent 7e92979 commit 9bf06fa

File tree

61 files changed

+3949
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

61 files changed

+3949
-0
lines changed
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
android_test {
2+
name: "SpecialRuntimePermsTestCases",
3+
defaults: [
4+
"cts_defaults",
5+
"mts-target-sdk-version-current",
6+
],
7+
libs: [
8+
"android.test.runner.stubs.system",
9+
"android.test.base.stubs.system",
10+
"android.test.mock.stubs.system",
11+
],
12+
static_libs: [
13+
"androidx.test.uiautomator_uiautomator",
14+
"androidx.test.rules",
15+
"compatibility-device-util-axt",
16+
"platform-test-annotations",
17+
"cts-wm-util",
18+
"cts-install-lib",
19+
"android.content.pm.flags-aconfig-java",
20+
"ShortcutManagerTestUtils",
21+
"kotlin-test",
22+
"kotlinx_coroutines_test",
23+
"grapheneos-test-common",
24+
"framework-protos",
25+
// TODO: Figure out how to use IIntentSender in
26+
// cts/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandInstallTest.java
27+
],
28+
resource_dirs: ["res"],
29+
srcs: [
30+
"src/**/*.java",
31+
"src/**/*.aidl",
32+
"src/**/*.kt",
33+
],
34+
data: [
35+
":GosHelloWorldAppV1",
36+
":GosHelloWorldAppV2",
37+
":GosAppThatAccessesInternetOnCommand",
38+
],
39+
per_testcase_directory: true,
40+
platform_apis: true,
41+
sdk_version: "test_current",
42+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
3+
package="grapheneos.srtpermtests" >
4+
5+
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
6+
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
7+
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
8+
9+
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
10+
<uses-permission android:name="android.permission.INTERNET" />
11+
<uses-permission android:name="android.permission.HIGH_SAMPLING_RATE_SENSORS" />
12+
13+
<queries>
14+
<package android:name="com.example.helloworld" />
15+
<package android:name="grapheneos.srtpermtests.internet.appthataccessesinternet" />
16+
</queries>
17+
18+
<application
19+
android:label="GrapheneOS special runtime permissions tests">
20+
<uses-library android:name="android.test.runner" />
21+
22+
<activity android:name="grapheneos.srtpermtests.packageinstaller.BaseInstallerTest$Launcher"
23+
android:enabled="true"
24+
android:exported="true">
25+
<intent-filter>
26+
<action android:name="android.intent.action.MAIN"/>
27+
<category android:name="android.intent.category.LAUNCHER"/>
28+
<category android:name="android.intent.category.HOME"/>
29+
<category android:name="android.intent.category.DEFAULT"/>
30+
</intent-filter>
31+
</activity>
32+
33+
<receiver android:name="grapheneos.srtpermtests.packageinstaller.BaseInstallerTest$UnarchiveBroadcastReceiver"
34+
android:enabled="true"
35+
android:exported="true">
36+
<intent-filter>
37+
<action android:name="android.intent.action.UNARCHIVE_PACKAGE"/>
38+
</intent-filter>
39+
</receiver>
40+
41+
<service android:name="grapheneos.test.common.notifications.GtsNotificationListenerService"
42+
android:exported="true"
43+
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
44+
<intent-filter>
45+
<action android:name="android.service.notification.NotificationListenerService"/>
46+
</intent-filter>
47+
</service>
48+
</application>
49+
50+
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
51+
android:targetPackage="grapheneos.srtpermtests"
52+
android:label="SrtPerms Tests"/>
53+
54+
</manifest>
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<configuration description="Config for GrapheneOS special runtime permissions test cases">
3+
<target_preparer class="com.android.tradefed.targetprep.DeviceSetup">
4+
<option name="force-skip-system-props" value="true" />
5+
<option name="set-global-setting" key="verifier_engprod" value="1" />
6+
<option name="set-global-setting" key="verifier_verify_adb_installs" value="0" />
7+
<option name="restore-settings" value="true" />
8+
<option name="set-global-setting" key="unused_static_shared_lib_min_cache_period" value="0" />
9+
<option name="set-system-setting" key="notification_cooldown_enabled" value="0" />
10+
<option name="enable-testing-secondary-user-on-secondary-display" value="true" />
11+
</target_preparer>
12+
13+
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
14+
<option name="cleanup-apks" value="true" />
15+
<option name="install-arg" value="-r" />
16+
<option name="test-file-name" value="SpecialRuntimePermsTestCases.apk" />
17+
</target_preparer>
18+
19+
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
20+
<option name="package" value="grapheneos.srtpermtests" />
21+
<option name="runtime-hint" value="1m" />
22+
<!-- <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" /> -->
23+
<option name="hidden-api-checks" value="false" />
24+
</test>
25+
<!-- Create place to store apks -->
26+
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
27+
<option name="run-command" value="mkdir -p /data/local/tmp/cts/uninstall" />
28+
<option name="teardown-command" value="rm -rf /data/local/tmp/cts"/>
29+
<!-- Ensure the UI is ready and in an idle state before running tests -->
30+
<option name="run-command" value="settings put global device_provisioned 1" />
31+
<option name="run-command" value="settings put secure user_setup_complete 1" />
32+
<option name="run-command" value="input keyevent KEYCODE_WAKEUP" />
33+
<option name="run-command" value="wm dismiss-keyguard" />
34+
<!-- Collapse notifications -->
35+
<option name="run-command" value="cmd statusbar collapse" />
36+
<!-- dismiss all system dialogs before launch test -->
37+
<option name="run-command" value="am broadcast -a android.intent.action.CLOSE_SYSTEM_DIALOGS" />
38+
39+
<option name="run-command" value="pm uninstall com.android.cts.install.lib.testapp.A" />
40+
<option name="run-command" value="pm uninstall com.android.cts.install.lib.testapp.B" />
41+
<option name="run-command" value="pm uninstall com.android.cts.install.lib.testapp.C" />
42+
<option name="teardown-command" value="pm uninstall com.android.cts.install.lib.testapp.A" />
43+
<option name="teardown-command" value="pm uninstall com.android.cts.install.lib.testapp.B" />
44+
<option name="teardown-command" value="pm uninstall com.android.cts.install.lib.testapp.C" />
45+
</target_preparer>
46+
47+
<!-- Load additional APKs onto device -->
48+
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
49+
<option name="cleanup" value="true" />
50+
<option name="push" value="GosHelloWorldAppV1.apk->/data/local/tmp/cts/uninstall/GosHelloWorldAppV1.apk" />
51+
<option name="push" value="GosHelloWorldAppV2.apk->/data/local/tmp/cts/uninstall/GosHelloWorldAppV2.apk" />
52+
<option name="push" value="GosAppThatAccessesInternetOnCommand.apk->/data/local/tmp/cts/uninstall/GosAppThatAccessesInternetOnCommand.apk" />
53+
</target_preparer>
54+
55+
<target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
56+
<option name="force-root" value="false" />
57+
</target_preparer>
58+
</configuration>
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# SpecialRuntimePermsTestCases
2+
3+
Test cases for GrapheneOS special runtime permissions.
4+
5+
The tests won't compile if the `OTHER_SENSORS` permission isn't implemeneted (compiles using the new
6+
manifest entry for `android.permission.OTHER_SENSORS` and the system message notification id for
7+
`com.android.server.ext.MissingSpecialRuntimePermissionNotification`).
8+
9+
## Running tests
10+
11+
Run an active device with internet access (might be best done with
12+
`emulator -wipe-data -read-only` for a clean slate, and on a userdebug build) and then run
13+
14+
```bash
15+
atest SpecialRuntimePermsTestCases
16+
```
17+
18+
This command will handle building and pushing the APKs onto the device (see
19+
[AndroidTest.xml](AndroidTest.xml) for details).
20+
21+
Specific tests can be run by specifying the test class (and optionally the test function name)
22+
after `:`, e.g.
23+
24+
```bash
25+
atest SpecialRuntimePermsTestCases:InternetAndSensorsPermissionTest
26+
atest SpecialRuntimePermsTestCases:InternetAndSensorsPermissionTest#sensors_granted_get_success
27+
```
28+
29+
## Overview of tests
30+
31+
- Installing apps with auto grant sensor setting in various states
32+
- Updating apps and ensuring runtime permission states are preserved
33+
- Archiving and unarchiving apps and ensuring runtime permission states are preserved in various
34+
cases (based on the tests from https://github.com/GrapheneOS/platform_frameworks_base/pull/163)
35+
- Ensuring the general functionality of special runtime permissions
36+
- AOSP should consider the special runtime permissions to have a dangerous protection level
37+
- Revoking internet permission should result in the app seeing as the network as unavailable
38+
instead of throwing errors
39+
- Revoking sensor permission should result in sensor events not being received, and a notification
40+
should be posted if an app tries to access sensors when the permission is revoked
41+
42+
Much of the test code and apps are based on existing AOSP CTS tests (`cts/` and
43+
`packages/modules/Permission/tests/cts`). Some CTS test failures were used to build test cases here.
44+
45+
## Known issues
46+
47+
- InternetAndSensorsPermissionTest is based on a CTS test that was marked as flaky
48+
(`packages/modules/Permission/tests/cts/permission/src/android/permission/cts/LocationAccessCheckTest.java`).
49+
Sometimes the tests might fail from a `DeadObjectException` or other misc failures.
50+
- Currently doesn't cover usages of `TriggerEventListener` like for
51+
`Sensor.TYPE_SIGNIFICANT_MOTION`. Seems more complicated to test (though
52+
[here's an existing CTS test for it](https://cs.android.com/android/platform/superproject/main/+/main:cts/apps/CtsVerifier/src/com/android/cts/verifier/sensors/SignificantMotionTestActivity.java))
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<!--
2+
~ Copyright (C) 2020 The Android Open Source Project
3+
~
4+
~ Licensed under the Apache License, Version 2.0 (the "License");
5+
~ you may not use this file except in compliance with the License.
6+
~ You may obtain a copy of the License at
7+
~
8+
~ http://www.apache.org/licenses/LICENSE-2.0
9+
~
10+
~ Unless required by applicable law or agreed to in writing, software
11+
~ distributed under the License is distributed on an "AS IS" BASIS,
12+
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
~ See the License for the specific language governing permissions and
14+
~ limitations under the License.
15+
-->
16+
17+
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
18+
android:orientation="vertical"
19+
android:layout_width="match_parent"
20+
android:layout_height="match_parent"
21+
android:padding="8dp"
22+
android:gravity="center">
23+
24+
<TextView android:id="@+id/overlay_description"
25+
android:layout_width="wrap_content"
26+
android:layout_height="wrap_content"
27+
android:textColor="@android:color/black"
28+
android:background="@android:color/white"
29+
android:text="This is an overlay" />
30+
</LinearLayout>
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
package grapheneos.srtpermtests
2+
3+
import android.content.ComponentName
4+
import android.content.Context
5+
import android.content.Intent
6+
import android.content.ServiceConnection
7+
import android.os.IBinder
8+
import android.util.Log
9+
import com.android.compatibility.common.util.SystemUtil
10+
import grapheneos.srtpermtests.internet.appthataccessesinternet.IAccessInternetOnCommand
11+
import grapheneos.srtpermtests.packageinstaller.TestApks
12+
import kotlin.test.assertTrue
13+
import kotlinx.coroutines.suspendCancellableCoroutine
14+
import org.junit.rules.ExternalResource
15+
16+
/**
17+
* A rule meant to be used as a [org.junit.ClassRule] for managing the install, uninstall, and
18+
* service bindings for IPC in the internet access test app.
19+
*/
20+
class AppThatAccessesInternetRule : ExternalResource() {
21+
companion object {
22+
private const val TAG = "AppThatAccessesInternetRule"
23+
val TEST_APP_PKG = TestApks.appThatAccessesInternet.packageName
24+
val TEST_APP_SERVICE = "$TEST_APP_PKG.AccessInternetOnCommand"
25+
}
26+
27+
val context: Context = androidx.test.InstrumentationRegistry.getTargetContext();
28+
private var serviceConn: ServiceConnection? = null
29+
private var accessor: IAccessInternetOnCommand? = null
30+
31+
private fun installBackgroundAccessApp() {
32+
val output = SystemUtil.runShellCommandOrThrow(
33+
// -g means grant all runtime permissions
34+
"pm install -r -g " + TestApks.appThatAccessesInternet.apkPath
35+
)
36+
assertTrue(output.contains("Success"))
37+
}
38+
39+
private fun uninstallBackgroundAccessApp() {
40+
val output = SystemUtil.runShellCommandOrThrow(
41+
"pm uninstall $TEST_APP_PKG"
42+
)
43+
assertTrue(output.contains("Success"))
44+
}
45+
46+
private fun setIdleAllowlist(enabled: Boolean) {
47+
val prefix = if (enabled) "+" else "-"
48+
val command = "cmd deviceidle whitelist $prefix$TEST_APP_PKG"
49+
SystemUtil.runShellCommand(command)
50+
}
51+
52+
suspend fun bindService(): IAccessInternetOnCommand {
53+
if (serviceConn != null && accessor != null) {
54+
return accessor!!
55+
}
56+
57+
return suspendCancellableCoroutine { cont ->
58+
serviceConn = object : ServiceConnection {
59+
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
60+
val acc = IAccessInternetOnCommand.Stub.asInterface(service)
61+
accessor = acc
62+
cont.resume(acc) {
63+
unbindService()
64+
}
65+
}
66+
67+
override fun onServiceDisconnected(name: ComponentName?) {
68+
serviceConn = null
69+
accessor = null
70+
cont.cancel()
71+
}
72+
}
73+
val intent = Intent()
74+
intent.component = ComponentName(TEST_APP_PKG, TEST_APP_SERVICE)
75+
context.bindService(
76+
intent,
77+
serviceConn!!,
78+
// adding Context.BIND_NOT_FOREGROUND will make test app service unable to
79+
// get sensor readings
80+
Context.BIND_AUTO_CREATE
81+
)
82+
}
83+
}
84+
85+
fun unbindService() {
86+
serviceConn?.let {
87+
context.unbindService(it)
88+
serviceConn = null
89+
}
90+
accessor = null
91+
}
92+
93+
fun wakeUpAndDismissKeyguard() {
94+
SystemUtil.runShellCommand("input keyevent KEYCODE_WAKEUP")
95+
SystemUtil.runShellCommand("wm dismiss-keyguard")
96+
}
97+
98+
override fun before() {
99+
Log.d(TAG, "before()")
100+
installBackgroundAccessApp()
101+
// Might be needed to allow test app to do internet calls in Service.
102+
// Note: Commenting this out alone seems to result in all tests still passing.
103+
// Removing this and adding Context.BIND_NOT_FOREGROUND to the service binding,
104+
// will cause some of the internet granted tests to fail.
105+
setIdleAllowlist(true)
106+
}
107+
108+
override fun after() {
109+
setIdleAllowlist(false)
110+
uninstallBackgroundAccessApp()
111+
unbindService()
112+
}
113+
}

0 commit comments

Comments
 (0)