Android GrantPermissionRule does not work with api-27 - android

I've the permission rule configured as below in my MainActivityTest class
#Rule
public GrantPermissionRule permissionRule =
GrantPermissionRule.grant(RECORD_AUDIO, WRITE_EXTERNAL_STORAGE);
When I run below command to execute the tests on emulator with api 27
./gradlew connectedCheck
It fails with the below error
com.example.myapplication.MainActivityTest > testLaunch_main_activity[Pixel_XL_API_27(AVD) - 8.1.0] FAILED
androidx.test.espresso.NoActivityResumedException: No activities in stage RESUMED. Did you forget to launch the activity. (test.getActivity() or similar)?
at dalvik.system.VMStack.getThreadStackTrace(Native Method)
Surprisingly the permissions are showing as granted in the app info settings, but still its asking for permissions when the test is run on emulator with api version 27 (or lower)
Can someone please confirm if it is a bug in some android plugin or if I am missing anything here.
Source Code - https://github.com/vivekweb2013/test-android-project

You are using ContextCompat's checkSelfPermission to check whether the app has permission or not. This is backward compatible with the support libraries but not sure with androidx. Alternative to this can be to use PermissionChecker's checkSelfPermission api like,
For API level 22 and below,
int permission = PermissionChecker.checkSelfPermission(context, permission);
if (permission == PermissionChecker.PERMISSION_GRANTED) {
// permission is granted
} else {
// permission not granted
}
But given that these permissions RECORD_AUDIO and WRITE_EXTERNAL_STORAGE are dangerous permission which requires acknowledgement from the user before our app can start consuming it. Below API level 23, these permissions are granted automatically when it is declared in AndroidManifest so the other way to get rid of this issue can be to verify it only for API level 23+ since it makes sense to validate.

I'd suggest you use a RuleChain with an outer rule for permissions around the ActivityScenarioRule
The problem seems to come from a race between launching the activity through the scenario rule and the permission rule, with a RuleChain in place the order of execution becomes explicit and the behavior should be as expected.
Here's the updated code from your example:
public class MainActivityTest {
public GrantPermissionRule permissionRule =
GrantPermissionRule.grant(RECORD_AUDIO, WRITE_EXTERNAL_STORAGE);
public ActivityScenarioRule<MainActivity> rule = new ActivityScenarioRule<>(MainActivity.class);
#Rule
public RuleChain chain = RuleChain.outerRule(permissionRule).around(rule);
#Test
public void testLaunch_main_activity() {
onView(withId(R.id.txt_view)).check(matches(isDisplayed()));
}
}

Related

How to user PackageInstaller.SessionParams setGrantedRuntimePermissions method?

I'm building a system app (but not a signature app) to run as root in "kiosk mode". This app should install/uninstall third party apk's on runtime, according to the demand.
I need to install the third party app and immediately grant runtime permissions that the apk needs. After some research, I found the method (android.content.pm.PackageInstaller):
/**
* Sets which runtime permissions to be granted to the package at installation.
*
* #param permissions The permissions to grant or null to grant all runtime
* permissions.
*
* #hide
*/
#SystemApi
#RequiresPermission(android.Manifest.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS)
public void setGrantedRuntimePermissions(String[] permissions) {
installFlags |= PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS;
this.grantedRuntimePermissions = permissions;
}
As the annotation says, it can only be called if I declare INSTALL_GRANT_RUNTIME_PERMISSIONS on my app manifest. So, in spite I could not find this permission in (android.Manifest), I added it to manifest:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.xxx"
>
[...]
<uses-permission android:name="android.permission.INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS"/>
[...]
When I call the method on my code "params.setGrantedRuntimePermissions(new String[]{ Manifest.permission.ACCESS_COARSE_LOCATION });" I immediately receive an error from Android Studio saying that cannot resolve the method. It is easily explained because there's a #hide instruction there, so I tried to workaround, and call the method by Reflection:
String[] permissions = new String[]{ Manifest.permission.ACCESS_COARSE_LOCATION };
Method m = params.getClass().getMethod("setGrantedRuntimePermissions", new Class[] { String[].class } );
m.invoke(params, (Object)permissions);
But, in spite I have added the permission on Manifest, I still receive the security exception at runtime:
java.lang.SecurityException: You need the android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS permission to use the PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS flag
Extra info:
. The app is installed under /system/priv-app
. Compilation details:
android {
compileSdkVersion 26
buildToolsVersion '26.0.2'
defaultConfig {
applicationId "com.xxx"
minSdkVersion 21
targetSdkVersion 26
versionCode 1
}
. I was using this lib (eu.chainfire.libsuperuser.Shell) to grant the permission and it works but I wonder If I can have it done without using shell:
Shell.SU.run("pm grant com.thirdparty.package android.permission.ACCESS_COARSE_LOCATION");
Any thoughts?

Facebook`s screenshot tests failing with api bigger than 23

I am using Karumi's shot plugin (https://github.com/karumi/shot) to take screenshots from my tests and compare then using facebook's library: http://facebook.github.io/screenshot-tests-for-android/
The library has an issue when running with api bigger then 23 because it needs the WRITE_EXTERNAL_STORAGE permission and since api 23, granting permissions during tests is not a trivial task.
But in espresso 3.0 was added the GrantPermissionRule and with this, you can set permissions previously to the execution of the test easily.
Well, I added the Rule:
#Rule #JvmField
val grantPermissionRule: GrantPermissionRule = GrantPermissionRule.grant(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
And took the screenshot with the following code:
Screenshot.snapActivity(activityTestRule.activity).record()
I have a custom TestRunner that runs:
override fun onCreate(args: Bundle) {
super.onCreate(args)
ScreenshotRunner.onCreate(this, args)
}
override fun finish(resultCode: Int, results: Bundle) {
ScreenshotRunner.onDestroy()
super.finish(resultCode, results)
}
But when I execute the test I receive the following error:
java.lang.RuntimeException: Failed to create the directory for screenshots. Is your sdcard directory read-only?
at com.facebook.testing.screenshot.internal.ScreenshotDirectories.getSdcardDir(ScreenshotDirectories.java:66)
The plugin fails trying to save the screenshots in an API >= 23 because the permission has to be granted in the testing APK and not the APK under test. Using the rule named grant permission test rule does not provide this functionality. This is not supported by the official Facebook library and we don't support it for now :(
I've also answered your question in the GitHub repository https://github.com/Karumi/Shot/issues/19#issuecomment-328334528
Try to use android:requestLegacyExternalStorage="true" in your manifest during test.
Dont forget to remove it once you finished

Permissions are granted by default

I'm not sure i fully understand this. So, for the <= 21 API version we can just use AndroidManifest.xml to request permissions, but Lollipop and higher APIs we have Requesting permission on runtime feature. So i'm using it with this simpe code:
if (Build.VERSION.SDK_INT >= 23) {
mPermissionsToBeAsked.clear();
for (String permission : AudioRecordingThread.PERMISSIONS_NEEDED) {
if (checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
mPermissionsToBeAsked.add(permission);
}
} ....
Then, if that list is not empty i'm requesting them :
if (mPermissionsToBeAsked.size() > 0) {
requestPermissions(mPermissionsToBeAsked.toArray(new String[0]), AUDIO_PERMISSIONS_REQUEST_CODE);
}
But, for some reason, on devices, for example, like Samsung Galaxy S7 with Android 6.0.1, all the permissions grandted by default when app is installed. So i want to know why, BUT, it's there is an even bigger concerne, when i go to my application in Application Manager and manually removing Microphone permision, in the app checkSelfPermission(permission) is still returning GRANTED. So the questions:
Why on devices with API level Lollipop and higher all permissions are still granted by default and above code won't add anything into mPersmissionToBeAsked?
Why if i manually removing permission with title MICROPHONE in Application manager checkSelfPermission(android.permission.RECORD_AUDIO) still returns GRANTED?
Just Cross verify in your app gradle file the targetsdk version is greater than 22.
defaultConfig {
// -----
targetSdkVersion 23
//----
}
If it is less than 23 than permission will automatically been granted to your app.
First of all it's Android M and above that handles permission granting. And that means you should have
targetSdkVersion 23
or above. Otherwise the system considers that the developper did not target this version, meaning that the developper does not check for permissions.

I got MISSING_PERMISSION issue Here maps Android

I'm trying an application just show Here map on screen. I've followed all of steps in document of Here + provided app_id, app_code, license key + provided 6 permissions in AndroidManifest.xml.
But It got the following issue:
"ERROR: Cannot initialize Map Fragment: MISSING_PERMISSION"
I'm using gradle 2.8, targetSdkVersion 23, compileSdkVersion 23
Android 6 / API 23 has a new permission system, that means you have to request critical permissions from the user.
See Android docs: https://developer.android.com/training/permissions/requesting.html
Just adding the critical permissions to the manifest is not enough anymore.
If you don't want to do this, you can still set traget API level to 22 and work in legacy mode, but to be more future proof, you should implement the new Android6 way of requesting permissions.
The critical permissions in the HERE SDK that you have to request are:
ACCESS_FINE_LOCATION and WRITE_EXTERNAL_STORAGE
Make sure you are using android runtime permission on for location access as from marshmallow onward all the OS need permission to run

Android Studio: grant permission between installing test APK and running tests with graphical test runner

I'm trying to automate the disabling of animations as described in this post, but that only seems to work for command-line invocation of connectedAndroidTest. I want to use the graphical test runner in Studio, with the list box showing passed/failed tests. With that runner, the permission grant (adb shell pm grant ... android.permission.SET_ANIMATION_SCALE) is never run, seemingly because the gradle task installDebugAndroidTest is never run, instead the runner is running Gradle as far as assembleDebugAndroidTest (or whatever alternate gradle task I specify in my run configuration), and then installing com.mypackage.test by some other (non-Gradle?) method immediately before running tests. So any prior permission grant is reset by that installation.
How can I grant SET_ANIMATION_SCALE between the graphical test runner's installation of the test package and the running of the test?
You can do it using reflection, adding the permission to the manifest, creating an Espresso TestRule and a task (explained here in detail).
Add the permission to the manifest of a debug/mock variant:
<uses-permission android:name="android.permission.SET_ANIMATION_SCALE"/>
Create your own task depending on installDebug and make connectedDebugAndroidTest depend on your task. You also need to grant the SET_ANIMATION_SCALE permission for testing.
Create a test rule that uses internally reflection to retrieve and restore animation scales (code):
public class AnimationAwareWonderTestRule extends AnimationAwareAwesomeTestRule {
private float[] mAnimationScales;
#Override
protected void before() throws Throwable {
mAnimationScales = AnimationAwareWonder.tryToRetrieveAndDisableAnimationsAndTransitions();
}
#Override
protected void after() throws Throwable {
AnimationAwareWonder.tryToRestoreAndEnableAnimationsAndTransitions(mAnimationScales);
}
}
It works but seems it's not possible at the moment to use this permission in MarshMallow.

Categories

Resources