AndroidTestCase assertActivityRequiresPermission() not asserting correctly - android

https://gist.github.com/blundell/ff2ac1d5ff0a41519c36
http://developer.android.com/reference/android/test/AndroidTestCase.html#assertActivityRequiresPermission(java.lang.String, java.lang.String, java.lang.String)
I've written a test for security permissions on Activities, however it's not doing what I expect.
I declare my activity in the manifest needs a permission.
I run the test that asserts for this permission.
Here is an example test that should pass, but doesn't:
AndroidManifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.blundell.myapplication">
<application>
<activity
android:name=".SecondActivity"
android:permission="perm.foo.bar" />
</application>
</manifest>
My test case:
package com.blundell.myapplication;
import android.app.Application;
import android.test.ApplicationTestCase;
/**
* Testing Fundamentals
*/
public class ApplicationTest extends ApplicationTestCase<Application> {
private static final String PACKAGE = "com.blundell.myapplication";
public ApplicationTest() {
super(Application.class);
}
public void testSecondActivityRequiresFooBarPermission() throws Exception {
assertActivityRequiresPermission(PACKAGE, PACKAGE + ".SecondActivity", "perm.foo.bar");
}
}
The stub activity:
package com.blundell.myapplication;
import android.app.Activity;
public class SecondActivity extends Activity {
}
The stack trace when the test fails:
junit.framework.AssertionFailedError: expected security exception for perm.foo.bar
at android.test.AndroidTestCase.assertActivityRequiresPermission(AndroidTestCase.java:99)
at com.blundell.myapplication.ApplicationTest.testSecondActivityRequiresFooBarPermission(ApplicationTest.java:15)
at java.lang.reflect.Method.invokeNative(Native Method)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:191)
at android.test.AndroidTestRunner.runTest(AndroidTestRunner.java:176)
at android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:554)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1701)
EDIT:
I have found with gradle you can add an AndroidManifest to your tests anyway and it will be merged:
#Manish Mulimani I added the instrumentation tag, but this has not made the test pass. (adding an IntentFilter & it does).
i.e.
in /src/androidTest/AndroidManifext.xml :
<instrumentation
android:label="new label to prove tools:replace works"
android:name="android.test.InstrumentationTestRunner"
android:targetPackage="com.blundell.myapplication.test"
tools:replace="targetPackage, label" />

The test application and application under test run in the same process. Hence the test case passes, as the test application does not need to request permissions to launch the activity.
To test the permissions, the test application should be run in a different process. This can be accomplished by assigning the test package name to android:targetPackage in the instrumentation element of test application's manifest.
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.blundell.myapplication.test"
xmlns:android="http://schemas.android.com/apk/res/android">
<application android:debuggable="true">
<uses-library android:name="android.test.runner" />
</application>
<instrumentation android:label="Tests for com.blundell.myapplication.test" android:name="android.test.InstrumentationTestRunner" android:targetPackage="com.blundell.myapplication.test" android:handleProfiling="false" android:functionalTest="false" />
</manifest>
Here is the manifest file of old android package com.android.globalsearch, using it the same way. Here is comment left in that manifest file:
We are self instrumenting so the test can be run, but since this is
for permissions test, we are not instrumenting any app (we want to be
accessing it from the outside).
Note: In case you are using gradle, then you need to manually generate the test package, install and test it. Reason from docs:
The value of the targetPackage attribute of the instrumentation node
in the test application manifest is automatically filled with the
package name of the tested app, even if it is customized through the
defaultConfig and/or the Build Type objects. This is one of the reason
the manifest is generated automatically.
I followed these steps, to manually build and execute the test package.
In app/build.gradle : set applicationId to "com.blundell.myapplication.test"
Build the test package : ./gradlew assembleDebugTest
Install the test package : adb install -r ./app/build/outputs/apk/app-debug-test-unaligned.apk
Run the test : adb shell am instrument -w com.blundell.myapplication.test/android.test.InstrumentationTestRunner
Here is the logcat output:
10-28 21:06:46.749 32649 32662 I TestRunner: started: testActivityPermission(com.blundell.myapplication.ApplicationTest)
10-28 21:06:46.754 983 993 I ActivityManager: START u0 {flg=0x10000000 cmp=com.blundell.myapplication/.SecondActivity} from pid 32649
10-28 21:06:46.754 983 993 W ActivityManager: Permission Denial: starting Intent { flg=0x10000000 cmp=com.blundell.myapplication/.SecondActivity } from ProcessRecord{427f4f78 32649:com.blundell.myapplication.test/u0a10111} (pid=32649, uid=10111) requires perm.foo.bar
10-28 21:06:46.755 32649 32662 I TestRunner: finished: testActivityPermission(com.blundell.myapplication.ApplicationTest)
10-28 21:06:46.755 32649 32662 I TestRunner: passed: testActivityPermission(com.blundell.myapplication.ApplicationTest)

Related

Android, run regular app in instrumentation mode (adb + debug only)

I'd like to run an Android app in a way that it can access InstrumentationRegistry.getInstrumentation() during the runtime.
This is only for debug builds and installing the app through ADB is sufficient. Because I don't want to use the instrumentation test-suite, I don't want to use InstrumentationTestRunner but launch the MainActivity of the app directly.
How is this possible?
What I've tried:
Added regular implementation dependency on test packages (instead of test only dependency) - worked
implementation 'androidx.test.uiautomator:uiautomator:2.2.0'
implementation 'androidx.test:runner:1.1.0'
Added instrumentation to manifest
<instrumentation android:name=".MainActivity"
android:targetPackage="com.android.shell"
android:label="App" />
(Not sure whether this is actually correct.)
Ran the app using (app already installed on the device)
adb shell am instrument -w com.example.app/.MainActivity
This results in:
android.util.AndroidException: INSTRUMENTATION_FAILED: com.example.app/com.example.app.MainActivity
at com.android.commands.am.Instrument.run(Instrument.java:519)
at com.android.commands.am.Am.runInstrument(Am.java:202)
at com.android.commands.am.Am.onRun(Am.java:80)
at com.android.internal.os.BaseCommand.run(BaseCommand.java:60)
at com.android.commands.am.Am.main(Am.java:50)
at com.android.internal.os.RuntimeInit.nativeFinishInit(Native Method)
at com.android.internal.os.RuntimeInit.main(RuntimeInit.java:399)
So essentially my question is, how do I achieve running the app in a way I can use UiAutomator during the runtime?
Thanks in advance!
Yes, it is possible:
1. Add the dependencies to your build.gradle. It should be like this:
implementation 'androidx.test.ext:junit:1.1.2'
implementation 'androidx.test.uiautomator:uiautomator:2.2.0'
implementation 'androidx.test:runner:1.1.0'
2. Add the following uses-library inside application tag and instrumentation inside manifest in you AndroidManifest.xml:
<!-- Add inside application tag -->
<uses-library android:name="android.test.runner" />
Set android:targetPackage to your own application package name.
<!-- Add inside manifest tag -->
<instrumentation
android:name="androidx.test.runner.AndroidJUnitRunner"
android:label="App"
android:targetPackage="com.example.app" />
</manifest>
3. Create your activity. You can use InstrumentationRegistry.getInstrumentation(); there.
4. Create you Test class in the same source set of your application classes (inside src/main/java)
#RunWith(AndroidJUnit4.class)
public class Test {
#org.junit.Test
public void test() {
// Put any code here. It is launching the activity.
Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
Context context = instrumentation.getTargetContext();
Intent intent = new Intent(context, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
}
5. Build and install the app.
6. Run the app using: adb shell am instrument -w -m -e debug false -e class 'com.example.app.Test' com.example.app/androidx.test.runner.AndroidJUnitRunner
7. (Optional) If you want to execute it directly from the run button in Android Studio, then add this inside android in your build.gradle, it will change the place where the test classes must be created, pointing to the same folder as the main code:
sourceSets {
androidTest {
java.srcDir 'src/main/java'
}
}

AndroidTest does not use test application

Hi I faced that when I rewrite application in androidTest manifest file, it does not work. This is my AndroidManifest.xml file in androidTest folder:
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="ru.app"
android:installLocation="auto">
<application
tools:replace="android:name"
android:name=".app.ApplicationAndroidTest" />
</manifest>
This is part of original AndroidManifest.xml from main folder:
<application
android:name=".app.Application"
android:allowBackup="true"
android:hardwareAccelerated="true"
android:icon="#drawable/icon"
android:label="#string/app_name"
android:largeHeap="true"
android:theme="#style/Theme">
...
</application>
In fact I debugged it, and called getApplication() from breakpoint in activity under test, it returns .app.Application instead of ApplicationAndroidTest instance.
Do you have any ideas why android manifest file from androidTest is ignored?
As A workaround I used custom test runner class:
public class UiTestsRunner extends AndroidJUnitRunner {
#Override
#NonNull
public Application newApplication(#NonNull ClassLoader cl, #NonNull String className, #NonNull Context context)
throws InstantiationException, IllegalAccessException, ClassNotFoundException {
return Instrumentation.newApplication(ApplicationAndroidTest.class, context);
}
}
It seams ok for me. Hope it helps someone.
Changing application class at androidTest xml doesn't affect the app itself (tested app) but the additional android test apk that ships to the device for enabling tests. I'm not sure what is the exact meaning of application if any to android test apk.
anyhow busylee workaround is the available solution.
Important note: when defining custom instrumentation runner, it is required to run the tests with the custom runner, that can be done at Android Studio by editing run configuration of test run, under AndroidTests section with a test selected, there is 'Specific instrumentation runner (optional)' option.

Android external Service from a library (AAR, not Shared Service)

I am trying to create an Android Library containing a simple service. For example:
public class BasicService extends Service {
public BasicService() {
Log.d("I", "work");
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
I start the service the following way:
startService(new Intent(this, BasicService.class));
The service is compiled into an AAR file and after adding that to an application I get a ClassNotFoundException.
Caused by: java.lang.ClassNotFoundException: Didn't find class "nl.company.example.library.Services.BasicService" on path: DexPathList[[zip file "/data/app/nl.example.aartest-1/base.apk"],nativeLibraryDirectories=[/vendor/lib, /system/lib]]
I don't need to bind a service as the application does not have to communicate with the service. I also don't need an aidl interface as I don't want to share the service with other apps. I know I need to declare the service at the application side (not library) so I did that the following way:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="nl.company.example.exampleApp"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="17"
android:targetSdkVersion="19" />
<application
android:settings... >
<service
android:name="nl.company.example.library.Services.BasicService"
android:enabled="true"
android:exported="true" />
</application>
</manifest>
This is the manifest of my library:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="nl.company.example.library" >
<application/>
</manifest>
I've done some research at Stackoverflow/Google and I wasn't able to find an answer to my problem.
Android service in library
Also tried different ways of starting the service (on action etc.) but no good so far.
All help is welcome and my apologies if I made an evil duplicate question.
The BaseService was implementing an interface from another library which was not packaged with the aar file. This caused the application to crash right after starting on a device, because it couldn't resolve the dependency.
public class BaseService implements OtherLibraryInterface {
...
}
Instead of throwing a ClassNotFoundException for the OtherLibraryInterface, Android would throw exactly that exception for the BaseService. Which is very confusing and lead us to believe that there is something wrong with the Service.
Adding the OtherLibrary dependency explicitly as a dependency to the main application solved this issue.

Android Gradle Build Variant Deploy

I just finished setting up build variants with Gradle in Android Studio. What a blessing this will be for a typical demo/pro setup. I'm able to launch my 'demoDebug' app perfectly fine. When I switch to 'proDebug' and 'Run" with the same configuration it installs the pro app fine but crashes launching it:
Starting: Intent { act=android.intent.action.MAIN
cat=[android.intent.category.LAUNCHER]
cmp=com.anthonymandra.rawdroidpro/com.anthonymandra.rawdroid.RawDroid
} Error type 3 Error: Activity class
{com.anthonymandra.rawdroidpro/com.anthonymandra.rawdroid.RawDroid}
does not exist.
If I then go to the dashboard I can launch the pro version and it works as expected. So the install works for the appropriate version; there's only something wrong in the launch (on pro).
build.gradle flavors:
productFlavors {
demo {
packageName "com.anthonymandra.rawdroid"
}
pro {
packageName "com.anthonymandra.rawdroidpro"
}
proAmazon {
packageName "com.anthonymandra.rawdroidpro"
}
}
Update
cleaned up extraneous info unrelated to the issue
When I decompiled the apk I confirmed that none of the gradle overrides were being implemented in the Android.manifest.
First you should have packagename in manner like (recommended approach not mandatory ):
youractualapplicationpackage.flavorname
But you can have packagname whatever you want for flavours like in your case:
productFlavors {
demo {
packageName "com.anthonymandra.rawdroiddemo"
}
pro {
packageName "com.anthonymandra.rawdroidpro"
}
proAmazon {
packageName "com.anthonymandra.rawdroidpro"
}
}
make sure com.anthonymandra.rawdroid is the application's java package inside your main/java directory.
Your AndroidManifest.xml should be like this(Only inside main directory):
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.anthonymandra.rawdroid" >
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name=".RawDroid"
android:configChanges="keyboardHidden|orientation|screenSize"
android:label="#string/app_name"
android:theme="#style/GalleryTheme" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
And it should be only in the main directory not anywhere else.
At compilation time gradle will do the necessary package name changes and will make the build.
Same worked fine in my machine and I was able to see two different apps in my device for two different flavors.
The issue was:
'manifestmerger.enabled=false'.
I disabled the function a while back because it caused issues with a library. In the meantime I forgot about it, but apparently the library merging also applies to the gradle merging. I suppose that makes some sense but they really need to allow for separation between gradle merging and library merging.
Thanks to #pyus13 for nonstop support till I came to the typical "Oh s---, are you kidding me..."

GDK HelloWorld Service crash

I am building a HelloWorld-project called 'HelloGlass' using the GDK.
I have a HelloGlassActivity class, which works perfectly fine when launched and compiled. I then try adding another class, HelloGlassService, which extends android.app.Service and implements one abstract function. The moment I add this class, the project suddenly stops compiling with and throws the following error:
Android Dex: [HelloGlass] Unable to execute DX Android Dex:
[HelloGlass] java.nio.BufferOverflowException Android Dex:
[HelloGlass] at java.nio.Buffer.nextPutIndex(Buffer.java:499)
Now, Google and StackOverflow tell me that this error can be resolved by making sure that the AndroidManifest SDK version matches the one my project is dependent on. However, all my dependencies are JDK 1.6 and the GDK Sneak Peek.
As in Google's own Stopwatch example, my project does not include a project.properties file (I removed it after creating the default Android app template provided by IntelliJ and setting it to the GDK Sneak Peek SDK); and that's what my AndroidManifest.xml looks like:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.arik.HelloGlass"
android:versionCode="2"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="15"
android:targetSdkVersion="15" />
<application
android:allowBackup="true"
android:label="Hello Glass App" >
<activity
android:name="com.arik.HelloGlass.HelloGlassActivity"
android:label="Hello Glass Activity"
android:enabled="true" >
</activity>
</application>
</manifest>
As you can see, I did not even include that Service Class in the manifest, and yet it crashes. The Service class looks like this:
package com.arik.HelloGlass;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
public class HelloService extends Service{
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
And here are my module dependencies:
What am I doing wrong?

Categories

Resources