[The background]
I have been able to use the instrumentation in Android test projects. But now, I am trying to use instrumentation in normal Android app in order to do some automatic GUI control. For example, there is an app X in which instrumentation is used to start app Y and to send virtual keys/touch/click events into app Y. I successfully did it with one activity and one class derived from Instrumentation. These 2 classes are created in one app project. The activity is like this:
public class MainActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
...
String pack = new String("com.example.appX.MainActivity");
ComponentName name = new ComponentName(MainActivity.this,
FilterInstrumentation.class);
Bundle arguments = new Bundle();
arguments.putString("package", pack);
startInstrumentation(name, null, arguments);
...
}
}
The instrumentation sub class is like this:
public class FilterInstrumentation extends Instrumentation {
public void onStart(){
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setClassName(getTargetContext(), pack);
Activity activity = startActivitySync(intent);
}
...
}
[The problem]
The activity and the instrumentation are working successfully. A TCP server is also made to receive commands from external network in order to control the instrument's behaviors. I met the following 2 problems:
Problem 1 when the TCP server in the activity: Sockets can be created but the activity and the instrument can't talk to each other because "startInstrumentation()" is starting the instrument in a new standalone process. The activity can't get the instance of the "FilterInstrumentation" class. Commands received by the TCP server can't be sent to the instrument easily.
Problem 2 when the TCP server in the FilterInstrumentation. It will failed to create sockets due to the permission issue as "java.net.SocketException: socket failed: EACCES (Permission denied)". FilterInstrumentation is running in a dynamically created process, I can't control the permission by Manifest.xml file.
It seems that the test framework of Android can run an instrument in the same process but I don't know how to do this within my code without test framework used.
Can any master give any helpful info on this?
Thanks in advance.
Related
I am working on a project which runs on aosp. I added a system service to aosp. I run this service with using adb shell "service call" command successfully. While creating service, I applied following ways.
Added an aidl file
Added a .java file which derived from aidl
Added jni file.
After creating service, I wrote an basic android app. I want to call this service from this android app. How I can call this service from android app.
Create a service manager in: frameworks/base/core/java/android/os/MySerManager.java next to the .aidl interface definition.
In frameworks/base/core/java/android/content/Context.java
search for a simple service like VIBRATOR_SERVICE and add MYSER_SERIVCE to every occurrence.
In particular add: public static final String MYSER_SERVICE = "myser";
Do the same in frameworks/base/core/java/android/app/SystemServiceRegistry.java
making sure to add:
import android.os.MySerManager;
registerService(Context.MYSER_SERVICE, MySerManager.class,
new CachedServiceFetcher<MySerManager>() {
#Override
public MySerManager createService(ContextImpl ctx) {
return new MySerManager();
}});
This service manager can be used outside of the AOSP by building the sdk: https://android.googlesource.com/platform/sdk/+/master/docs/howto_build_SDK.txt
And within the AOSP by getting the system service from the Context:
MySerManager mySerManager = (MySerManager)getSystemService(Context.MYSER_SERVICE);
I managed to solve how system service calls from android app. After compiling aosp, classes.jar in framework_intermediates file is created to out/target/common/obj/JAVA_LIBRARIES. I added classes.jar to my android app and I used following codes.
IBinder binder = ServiceManager.getService("my_service");
IMyService myService = IMyService.Stub.asInterface(binder);
int result = myService.myFunction();
I searched this question but didn't find answers.
Suppose there is a system app, which is installed together with android system, e.g. Dialer app.
Now I want to unit-test or instrumentation-test this app. I don't want to use AOSP Android.mk. Are there alternative ways? e.g. Can I create a gradle project to test it?
For instrumentation tests I personally create an independent app that consumes uiautomator: https://developer.android.com/topic/libraries/testing-support-library/index.html#UIAutomator
This allows simulation of a user:
#org.junit.Test
public void testCheckContact() throws InterruptedException {
UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
assertThat(device, notNullValue());
device.pressHome();
Context context = InstrumentationRegistry.getInstrumentation().getContext();
Intent intent = context.getPackageManager().getLaunchIntentForPackage("systempackageName");
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
context.startActivity(intent);
...
...
PhoneActivityPage phoneActivityPage = new PhoneActivityPage();
phoneActivityPage.clickContacts();
assertEquals("Contacts", phoneActivityPage.getSelectedTab());
}
Where you can define PhoneActivityPage and the interface in a separate class within this independent test project.
I hope this helps.
You can use the UiAutomator system to test arbitrary apps, including system ones. However, the inputs and outputs are somewhat limited.
I'm trying to carry out junit test for the Android-DDP library.
To initialize the meteor object, we need a reference to a android context which I'm able to achieve using Robolectric. But the web-sockets is probably talking to the server on a different thread because of which the callback methods are not called and the test methods are getting end.
I used netstat to check if the android client is trying to communicate or not. It shows various ping/pong messages. So, Yes it is trying to talk to the server.
I went through this tutorial as well,
Android AsyncTask testing with Android Test Framework. This one tells how to handle the network on UI thread. But nothing seems right.
The sample code, I have worked is:
#Config(constants = BuildConfig.class, sdk = Build.VERSION_CODES.LOLLIPOP)
#RunWith(RobolectricGradleTestRunner.class)
public class MainActivityTest {
private MainActivity activity;
private Meteor meteor;
private String globalUrl = "ws://10.0.3.222:3000/websocket";
#Before
public void setup() {
activity = Robolectric.setupActivity(MainActivity.class);
meteor = new Meteor(activity, globalUrl);
meteor.reconnect();
/*
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
*/
}
#Test
public void validateMeteorIsConnected() {
assertTrue(meteor.isConnected());
}
}
Any help would be appreciable. Thanks in advance.
You defined two methods, setup() and validateMeteorIsConnected(), but where are they called?
First, your setup is not correct. After your call to new Meteor(...), you don't need the reconnect() call because the constructor does already establish the connection.
Moreover, you must set up a listener so that you know when the connection has been established or data comes in. This is done with mMeteor.setCallback(...); where the parameter is this or activity.
As you said, the work is done on a different thread and everything is asynchronous.
So you can't just call validateMeteorIsConnected() immediately after connecting.
You need some timer, as shown in the question that you linked to.
I run my Android tests by running a test implementation which "derives" from a library project (because I have a multi module project with baselib and "concrete app projects"). The test implementation is one of these concrete app projects and is launched by an InstrumentationTestCase. In this test case I mock several parts from the library project by RoboGuice. That means I run a "real" implementation of my baselib with mocked classes (like persistence handling, database handling and so on). To be able to do that, every single test case has to close and restart the whole test instance, because I can't start the same app twice on the device. These test are more integration tests than Junit tests, because I test some kind of workflows, but there is no other possibility to test that, because the possibilities with JUnit on Android testing seem to be very limited.
At the moment I can only run one test case at the same time, because if I run more than 1, the whole test is hanging. I already checked if it's the configuration change (see private method) which causes my test to freeze, but this is not the cause. See my attempts in tearDown method. I can't run
getInstrumentation().finish(0, new Bundle());
because I get
Test failed to run to completion. Reason: 'Test run failed to
complete. Expected 3 tests, received 1'
I also cannot run
getInstrumentation().callActivityOnDestroy(activity);
because I don't have an Activity here. Moreover the Activity "StartTestActivity" which is launched at startup is not the same Activity which runs when the test is finished because StartTestActivity launches another Activity "MainMenuActivity" which is running at the end of the test. I already thought about using Instrumentation.ActivityMonitor but this doesn't provide the needed functionality.
Nevertheless I want to somehow start with the same test conditions at every test case as the whole test itself does at startup, but I'm not sure what InstrumentationTestCase is doing in the background, so I don't know how to restart the whole instrumentation setup. I somehow need to stop and restart the test instance, or maybe there is a better solution? Any ideas?
(by the way: every test itself runs fine, so it's no problem of the test ifself).
public class WorkflowModule1Test extends InstrumentationTestCase
{
private PersistenceManagerMock persistenceManager;
#Override
protected void setUp() throws Exception
{
super.setUp();
}
#Override
protected void tearDown() throws Exception
{
super.tearDown();
if (persistenceManager != null)
{
persistenceManager.clear();
}
}
public void testSaveLocaleEN() throws PersistenceException
{
updateLocaleConfiguration(Locale.ENGLISH);
Intent intent = new Intent(getInstrumentation().getContext(), StartTestActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
getInstrumentation().startActivitySync(intent);
persistenceManager = (PersistenceManagerMock)RoboGuice.getInjector(ContextProvider.getApplication()).getInstance(IPersistenceManager.class);
List<Entity> entities = persistenceManager.getEntities();
assertTrue(entities.size() == 1);
assertTrue(entities.get(0) instanceof LanguageUsageRel);
assertTrue(((LanguageUsageRel)entities.get(0)).getLanguageId().equals("EN"));
}
public void testSaveLocaleDE() throws PersistenceException
{
updateLocaleConfiguration(Locale.GERMAN);
Intent intent = new Intent(getInstrumentation().getContext(), StartTestActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
getInstrumentation().startActivitySync(intent);
persistenceManager = (PersistenceManagerMock)RoboGuice.getInjector(ContextProvider.getApplication()).getInstance(IPersistenceManager.class);
List<Entity> entities = persistenceManager.getEntities();
assertTrue(entities.size() == 1);
assertTrue(entities.get(0) instanceof LanguageUsageRel);
assertTrue(((LanguageUsageRel)entities.get(0)).getLanguageId().equals("DE"));
}
private void updateLocaleConfiguration(Locale locale)
{
Locale.setDefault(locale);
Configuration configuration = new Configuration();
configuration.locale = locale;
getInstrumentation().getContext().getResources().updateConfiguration(configuration, getInstrumentation().getContext().getResources().getDisplayMetrics());
}
}
I think if you extended ActivityInstrumentationTestCase2 instead this would solve a lot of your problems.
Another note: Put your tear down logic before the super.tearDown() call.
I found the solution on my own. I have to set these two flags.
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
I'm using the following code to get the activity of a 3th party application and to put it in my activity:
LocalActivityManager mgr = getLocalActivityManager();
Intent i = new Intent(this, SomeActivity.class);
Window w = mgr.startActivity("unique_per_activity_string", i);
View wd = w != null ? w.getDecorView() : null;
if(wd != null) {
mSomeContainer.addView(wd);
}
Copyright Synic: android: using ActivityGroup to embed activities
However, due Security Restrictions, I'm receiving the following error:
java.lang.SecurityException: Requesting code from com.google.android.youtube (with uid 10065) to be run in process com.xxx.xxx (with uid 10144). (It is possible to show your own activity with your own SharedUID from your own application.)
Now i'm wondering if there is any way I can avoid this. By using rooted devices(?), bug in Android OS, or anything else. If I can get it to work by rooting my device, how would i achieve it? (not the rooting ofc)
I'm using the following code to get the activity of a 3th party application and to put it in my activity
That is not supported, sorry.
Now i'm wondering if there is any way I can avoid this.
You are welcome to grab the source code to Android, modify it to suit, put your altered OS into your own modded ROM, and install that ROM mod on whatever devices you are able to.