Git Product home page Git Product logo

mobly-snippet-lib's Introduction

Getting Started with Snippets for Mobly

Mobly Snippet Lib is a library for triggering device-side code from host-side Mobly tests. This tutorial teaches you how to use the snippet lib to trigger custom device-side actions.

Note: Mobly and the snippet lib are not official Google products.

Prerequisites

  • These examples and tutorials assume basic familiarity with the Mobly framework, so please follow the Mobly tutorial before doing this one.
  • You should know how to create an Android app and build it with gradle. If not, follow the Android app tutorial.

Overview

The Mobly Snippet Lib allows you to write Java methods that run on Android devices, and trigger the methods from inside a Mobly test case. The Java methods invoked this way are called snippets.

The snippet code can either be written in its own standalone apk, or as a product flavor of an existing apk. This allows you to write snippets that instrument or automate another app.

Under The Hood

A snippet is launched by an am instrument call. Snippets use a custom InstrumentationTestRunner derived from AndroidJUnitRunner. This allows for snippets that interact with a main app's classes, such as Espresso snippets, and allows you to get either the test app's or the main app's context from InstrumentationRegistry.

Once started, the special runner starts a web server which listens for requests to trigger snippets. The server's handler locates the corresponding methods by reflection, runs them, and returns results over the TCP socket. All common built-in variable types are supported as arguments.

Usage

The examples/ folder contains examples of how to use the mobly snippet lib along with detailed tutorials.

  • ex1_standalone_app: Basic example of a snippet which is compiled into its own standalone apk.
  • ex2_espresso: Example of a snippet which instruments a primary app to drive its UI using Espresso.
  • ex3_async_event: Example of how to use the @AsyncRpc annotation to handle asynchronous callbacks.
  • ex4_uiautomator: Example of how to create snippets that automate the UI actions using UIAutomator. Unlike Espresso UIAutomator works even without access to app source code.
  • ex5_schedule_rpc: Example of how to use the 'scheduleRpc' RPC to execute another RPC at a later time, potentially after device disconnection.
  • ex6_complex_type_conversion: Example of how to pass a non-primitive type to the Rpc methods and return non-primitive type from Rpc methods by supplying a type converter.

mobly-snippet-lib's People

Contributors

adorokhine avatar bediakocynoc avatar dancard avatar dthkao avatar jhshi avatar jklein24 avatar kdart avatar ko1in1u avatar kruton avatar l-meng avatar matty3 avatar mhaoli avatar prateikj avatar sebkur avatar xianyuanjia avatar xpconanfan avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

mobly-snippet-lib's Issues

Improve snippet startup procedure to avoid blind waits for connection

Currently, we start the snippet and blindly wait until it's available on the given port. This causes several problems:

  • No way to know that a snippet launched (versus crashing immediately due to e.g. classpath issues, so we don't need to bother trying to connect to a dead snippet)
  • No way to know what version of startup and communication protocol it's using so we can configure the client appropriately and manage compatibility.
  • No way to allow the device side to choose a port number, to avoid the client side having to pick a port number for the snippet and hoping it's free.
  • Hard to debug situations where snippet is hanging as we don't know how far it's gotten.

We need to fix the snippet startup procedure.

Fix java doc

Right now if we try to compile javadoc ($ ./gradlew javadoc) for the code base, we get multiple warnings and errors.
We should fix all the errors so javadoc compiles

Create doc for async rpc

In addition to the basic usage, we should also give sample use cases based on our experience with async events.

Odd stdout output when stopping snippet

Is it possible to not print the unit test result? It's a bit misleading...

cmd: adb -s 007a763d1170ac18 shell am instrument -w -e action stop com.google.android.apps.xxx/com.google.android.mobly.snippet.SnippetRunner, stdout: 
com.google.android.mobly.snippet.util.EmptyTestClass:
com.google.android.mobly.snippet.util.EmptyTestClass:

Time: 0.007

OK (0 tests)


, stderr: , ret: 0

fail to build: error: package android.support.test.rule does not exist

When I try to build the examples, it fails due to android.support.test.rule not found:

$ ./gradlew  examples:ex2_espresso:assembleDebug examples:ex4_uiautomator:assembleDebug  

> Task :examples:ex2_espresso:compileSnippetDebugJavaWithJavac FAILED
/Users/darren/.gradle/caches/transforms-1/files-1.1/espresso-core-3.0.2.aar/c05bd1847e045a233c593a0c4bca711a/jars/classes.jar(android/support/test/espresso/Espresso.class): warning: [classfile] MethodParameters attribute introduced in version 52.0 class files is ignored in version 51.0 class files
/Users/darren/.gradle/caches/transforms-1/files-1.1/espresso-core-3.0.2.aar/c05bd1847e045a233c593a0c4bca711a/jars/classes.jar(android/support/test/espresso/assertion/ViewAssertions.class): warning: [classfile] MethodParameters attribute introduced in version 52.0 class files is ignored in version 51.0 class files
/Users/darren/.gradle/caches/transforms-1/files-1.1/espresso-core-3.0.2.aar/c05bd1847e045a233c593a0c4bca711a/jars/classes.jar(android/support/test/espresso/matcher/ViewMatchers.class): warning: [classfile] MethodParameters attribute introduced in version 52.0 class files is ignored in version 51.0 class files
/Users/darren/.gradle/caches/transforms-1/files-1.1/espresso-core-3.0.2.aar/c05bd1847e045a233c593a0c4bca711a/jars/classes.jar(android/support/test/espresso/action/ViewActions.class): warning: [classfile] MethodParameters attribute introduced in version 52.0 class files is ignored in version 51.0 class files
/Users/darren/Sources/go/src/github.com/google/mobly-snippet-lib/examples/ex2_espresso/src/snippet/java/com/google/android/mobly/snippet/example2/EspressoSnippet.java:25: error: package android.support.test.rule does not exist
import android.support.test.rule.ActivityTestRule;
                                ^
/Users/darren/Sources/go/src/github.com/google/mobly-snippet-lib/examples/ex2_espresso/src/snippet/java/com/google/android/mobly/snippet/example2/EspressoSnippet.java:32: error: cannot find symbol
    public ActivityTestRule<MainActivity> mActivityRule =
           ^
  symbol:   class ActivityTestRule
  location: class EspressoSnippet
/Users/darren/Sources/go/src/github.com/google/mobly-snippet-lib/examples/ex2_espresso/src/snippet/java/com/google/android/mobly/snippet/example2/EspressoSnippet.java:33: error: cannot find symbol
            new ActivityTestRule<>(MainActivity.class);
                ^
  symbol:   class ActivityTestRule
  location: class EspressoSnippet
/Users/darren/.gradle/caches/transforms-1/files-1.1/espresso-core-3.0.2.aar/c05bd1847e045a233c593a0c4bca711a/jars/classes.jar(android/support/test/espresso/ViewAssertion.class): warning: [classfile] MethodParameters attribute introduced in version 52.0 class files is ignored in version 51.0 class files
/Users/darren/.gradle/caches/transforms-1/files-1.1/espresso-core-3.0.2.aar/c05bd1847e045a233c593a0c4bca711a/jars/classes.jar(android/support/test/espresso/ViewInteraction.class): warning: [classfile] MethodParameters attribute introduced in version 52.0 class files is ignored in version 51.0 class files
/Users/darren/.gradle/caches/transforms-1/files-1.1/espresso-core-3.0.2.aar/c05bd1847e045a233c593a0c4bca711a/jars/classes.jar(android/support/test/espresso/ViewAction.class): warning: [classfile] MethodParameters attribute introduced in version 52.0 class files is ignored in version 51.0 class files
3 errors
7 warnings

full debug output attached as file debug.log

Anything wrong with my environment setup?

Better management for device-side ports

Right now we assume the host-side port number is also available on device-side at snippet start time.
This assumption is probably going to break at one point.
We should actually check what port is available on the device side instead.

ArrayIndexOutOfBoundsException with Proguard

The exception will be thrown inside Log.getTag(). Message is:

java.lang.ArrayIndexOutOfBoundsException: length=4; index=4.

This was caused by a hard-coded stack depth (4) inside Log.getTag() function. Proguard will do inline optimization hence the call stack is different (shallower) with proguard.

Instead of use a hard-coded value, a better way is to walk up the stack until we see a class name that's neither us nor android.util.Log: that's the real caller.

Rewrite RPC stack to be more reliable

I'm seeing too many of these:

Details: No response from server.
Stack trace:
Traceback (most recent call last):
File "mobly/base_test.py", line 356, in exec_one_test
test_method()
...
File "mobly/controllers/android_device_lib/jsonrpc_client_base.py", line 285, in rpc_call
return self._rpc(name, *args)
File "mobly/controllers/android_device_lib/jsonrpc_client_base.py", line 265, in _rpc
raise ProtocolError(ProtocolError.NO_RESPONSE_FROM_SERVER)
ProtocolError: No response from server.

JSON-RPC is not a good protocol. Something better, probably making use of protobuf, would be a good start. The Python side implementation also needs improvement, since it doesn't support partial reads that has too long a delay between messages.

Backward compatibility mechanism

If we make updates to snippet lib and introduce new internal Rpcs or other protocol changes, the lib needs to have a way to provide the client the appropriate info so the client side can behave according to the lib's version.

Reduce log spam

Right now we see this when launching snippet, which is a spam that adds no value to the log.

com.google.android.mobly.snippet.util.EmptyTestClass:
com.google.android.mobly.snippet.util.EmptyTestClass:

Time: 0.006

OK (0 tests)

We should get rid of this.

Duplicate RPC names aren't detected, but stop the RPC server from booting up.

I recently had to manually debug a case where I'd added a duplicate RPC name. The error I got when doing this is a generic server protocol error, as follows:

mobly.snippet.errors.ServerStartProtocolError: <AndroidDevice|8f152933> INSTRUMENTATION_RESULT: shortMsg=Process crashed.

As my team is planning to add many new RPCs to our local copy of this snippet, it might prevent alot of headache if this particular error could be detected or flagged with a clearer error message, identifying (or at least mentioning) the duplicate RPC name. Is this possible to implement?

Add an way to tag Rpc methods for categorization purposes

Right now help() prints out all methods altogether, as the number of methods increase, we need to be able to categorize Rpc methods. Instead of a long consecutive list in alphabetic order, we probably want help()'s output to be more organized and readable:

Bluetooth:
  bluetoothDisable() returns void  // Disable bluetooth with a 30s timeout.
  bluetoothEnable() returns void  // Enable bluetooth with a 30s timeout.

Telephony:
  getLine1Number() returns String  // Gets the line 1 phone number.
  getSubscriberId() returns String  // Returns the unique subscriber ID, for example, the IMSI for a GSM phone.
  getTelephonyCallState() returns int  // Gets the call state for the default subscription. Call state values are0: IDLE, 1: RINGING, 2: OFFHOOK

Audio:
  getMusicMaxVolume() returns int  // Gets the maximum music stream volume value.
  getMusicVolume() returns int  // Gets the music stream volume.
  getRingMaxVolume() returns int  // Gets the maximum ringer volume value.
  getRingVolume() returns int  // Gets the ringer volume.

Account:
  listAccounts() returns Set  // List all Google (GMail) accounts on the device.

Others:
  makeToast(String) returns void  // Make a toast on screen.
  muteAll() returns void  // Silences all audio streams.

We could add a new field in @Rpc to specify the categorization of each method.

Snippets are not compatible with UIAutomator

'am instrument' only sets up a UiAutomationConnection in -w mode. I don't know why, but snippets are not run in wait mode, so UiAutomationConnection is null. This crashes uiautomator, which needs that proxy object for privileged operations back to the shell.

Change logger tag

Right now the logger tag is hard coded with 'sl4a' in it.
We should probably use a different name like 'snippet-lib'.

Support Async Events

Let's bring the basic event queue features to snippet lib.
We need this for async test cases like:

  1. Start bt discovery
  2. Interrupt before discovery finishes.
  3. Confirm discovery was interrupted by looking at the final Future result.

Having the async event mechanism would make it easier to write this kind of tests.

java.lang.ClassNotFoundException crash

when try the first example in the link: https://github.com/google/mobly/blob/master/docs/tutorial.md
meet below crash info
please help me to debug it

--------- beginning of crash
06-09 17:13:07.894 9627 9646 E AndroidRuntime: FATAL EXCEPTION: Instr: com.google.android.mobly.snippet.SnippetRunner
06-09 17:13:07.894 9627 9646 E AndroidRuntime: Process: com.google.android.mobly.snippet.bundled, PID: 9627
06-09 17:13:07.894 9627 9646 E AndroidRuntime: java.lang.NoClassDefFoundError: Failed resolution of: Lkotlin/jvm/internal/Lambda;
06-09 17:13:07.894 9627 9646 E AndroidRuntime: at androidx.test.platform.io.FileTestStorage.(FileTestStorage.java:45)
06-09 17:13:07.894 9627 9646 E AndroidRuntime: at androidx.test.runner.AndroidJUnitRunner.registerTestStorage(AndroidJUnitRunner.java:664)
06-09 17:13:07.894 9627 9646 E AndroidRuntime: at androidx.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:441)
06-09 17:13:07.894 9627 9646 E AndroidRuntime: at com.google.android.mobly.snippet.SnippetRunner.onStart(SnippetRunner.java:139)
06-09 17:13:07.894 9627 9646 E AndroidRuntime: at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:2278)
06-09 17:13:07.894 9627 9646 E AndroidRuntime: Caused by: java.lang.ClassNotFoundException: Didn't find class "kotlin.jvm.internal.Lambda" on path: DexPathList[[zip file "/data/app/~~dw3QzNPY21E6dpIWJGh3yw==/com.google.android.mobly.snippet.bundled-KiGoYwPDvrQc1DHYYByYJA==/base.apk"],nativeLibraryDirectories=[/data/app/~~dw3QzNPY21E6dpIWJGh3yw==/com.google.android.mobly.snippet.bundled-KiGoYwPDvrQc1DHYYByYJA==/lib/arm64, /system/lib64, /product/lib64]]
06-09 17:13:07.894 9627 9646 E AndroidRuntime: at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:218)
06-09 17:13:07.894 9627 9646 E AndroidRuntime: at java.lang.ClassLoader.loadClass(ClassLoader.java:379)
06-09 17:13:07.894 9627 9646 E AndroidRuntime: at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
06-09 17:13:07.894 9627 9646 E AndroidRuntime: ... 5 more
06-09 17:13:07.895 9627 9646 D MonitoringInstr: Handling an uncaught exception thrown on the thread Instr: com.google.android.mobly.snippet.SnippetRunner.
06-09 17:13:07.895 9627 9646 D MonitoringInstr: java.lang.NoClassDefFoundError: Failed resolution of: Lkotlin/jvm/internal/Lambda;
06-09 17:13:07.895 9627 9646 D MonitoringInstr: at androidx.test.platform.io.FileTestStorage.(FileTestStorage.java:45)
06-09 17:13:07.895 9627 9646 D MonitoringInstr: at androidx.test.runner.AndroidJUnitRunner.registerTestStorage(AndroidJUnitRunner.java:664)
06-09 17:13:07.895 9627 9646 D MonitoringInstr: at androidx.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:441)
06-09 17:13:07.895 9627 9646 D MonitoringInstr: at com.google.android.mobly.snippet.SnippetRunner.onStart(SnippetRunner.java:139)
06-09 17:13:07.895 9627 9646 D MonitoringInstr: at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:2278)
06-09 17:13:07.895 9627 9646 D MonitoringInstr: Caused by: java.lang.ClassNotFoundException: Didn't find class "kotlin.jvm.internal.Lambda" on path: DexPathList[[zip file "/data/app/~~dw3QzNPY21E6dpIWJGh3yw==/com.google.android.mobly.snippet.bundled-KiGoYwPDvrQc1DHYYByYJA==/base.apk"],nativeLibraryDirectories=[/data/app/~~dw3QzNPY21E6dpIWJGh3yw==/com.google.android.mobly.snippet.bundled-KiGoYwPDvrQc1DHYYByYJA==/lib/arm64, /system/lib64, /product/lib64]]
06-09 17:13:07.895 9627 9646 D MonitoringInstr: at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:218)
06-09 17:13:07.895 9627 9646 D MonitoringInstr: at java.lang.ClassLoader.loadClass(ClassLoader.java:379)
06-09 17:13:07.895 9627 9646 D MonitoringInstr: at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
06-09 17:13:07.895 9627 9646 D MonitoringInstr: ... 5 more
06-09 17:13:07.895 9627 9646 W AndroidJUnitRunner: An unhandled exception was thrown by the app.
06-09 17:13:07.895 9627 9646 W AndroidJUnitRunner: java.lang.NoClassDefFoundError: Failed resolution of: Lkotlin/jvm/internal/Lambda;
06-09 17:13:07.895 9627 9646 W AndroidJUnitRunner: at androidx.test.platform.io.FileTestStorage.(FileTestStorage.java:45)
06-09 17:13:07.895 9627 9646 W AndroidJUnitRunner: at androidx.test.runner.AndroidJUnitRunner.registerTestStorage(AndroidJUnitRunner.java:664)
06-09 17:13:07.895 9627 9646 W AndroidJUnitRunner: at androidx.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:441)
06-09 17:13:07.895 9627 9646 W AndroidJUnitRunner: at com.google.android.mobly.snippet.SnippetRunner.onStart(SnippetRunner.java:139)
06-09 17:13:07.895 9627 9646 W AndroidJUnitRunner: at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:2278)
06-09 17:13:07.895 9627 9646 W AndroidJUnitRunner: Caused by: java.lang.ClassNotFoundException: Didn't find class "kotlin.jvm.internal.Lambda" on path: DexPathList[[zip file "/data/app/~~dw3QzNPY21E6dpIWJGh3yw==/com.google.android.mobly.snippet.bundled-KiGoYwPDvrQc1DHYYByYJA==/base.apk"],nativeLibraryDirectories=[/data/app/~~dw3QzNPY21E6dpIWJGh3yw==/com.google.android.mobly.snippet.bundled-KiGoYwPDvrQc1DHYYByYJA==/lib/arm64, /system/lib64, /product/lib64]]
06-09 17:13:07.895 9627 9646 W AndroidJUnitRunner: at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:218)
06-09 17:13:07.895 9627 9646 W AndroidJUnitRunner: at java.lang.ClassLoader.loadClass(ClassLoader.java:379)
06-09 17:13:07.895 9627 9646 W AndroidJUnitRunner: at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
06-09 17:13:07.895 9627 9646 W AndroidJUnitRunner: ... 5 more
06-09 17:13:07.897 9627 9646 D AndroidJUnitRunner: Reporting the crash to an event service.
06-09 17:13:07.897 9627 9646 W TestEventClient: Process crashed before connection to orchestrator
06-09 17:13:07.898 9627 9646 I AndroidJUnitRunner: Bringing down the entire Instrumentation process.
06-09 17:13:07.898 9627 9646 E MonitoringInstr: Exception encountered by: Thread[Instr: com.google.android.mobly.snippet.SnippetRunner,5,main]. Dumping thread state to outputs and pining for the fjords.
06-09 17:13:07.898 9627 9646 E MonitoringInstr: java.lang.NoClassDefFoundError: Failed resolution of: Lkotlin/jvm/internal/Lambda;
06-09 17:13:07.898 9627 9646 E MonitoringInstr: at androidx.test.platform.io.FileTestStorage.(FileTestStorage.java:45)
06-09 17:13:07.898 9627 9646 E MonitoringInstr: at androidx.test.runner.AndroidJUnitRunner.registerTestStorage(AndroidJUnitRunner.java:664)
06-09 17:13:07.898 9627 9646 E MonitoringInstr: at androidx.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:441)
06-09 17:13:07.898 9627 9646 E MonitoringInstr: at com.google.android.mobly.snippet.SnippetRunner.onStart(SnippetRunner.java:139)
06-09 17:13:07.898 9627 9646 E MonitoringInstr: at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:2278)
06-09 17:13:07.898 9627 9646 E MonitoringInstr: Caused by: java.lang.ClassNotFoundException: Didn't find class "kotlin.jvm.internal.Lambda" on path: DexPathList[[zip file "/data/app/~~dw3QzNPY21E6dpIWJGh3yw==/com.google.android.mobly.snippet.bundled-KiGoYwPDvrQc1DHYYByYJA==/base.apk"],nativeLibraryDirectories=[/data/app/~~dw3QzNPY21E6dpIWJGh3yw==/com.google.android.mobly.snippet.bundled-KiGoYwPDvrQc1DHYYByYJA==/lib/arm64, /system/lib64, /product/lib64]]
06-09 17:13:07.898 9627 9646 E MonitoringInstr: at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:218)
06-09 17:13:07.898 9627 9646 E MonitoringInstr: at java.lang.ClassLoader.loadClass(ClassLoader.java:379)
06-09 17:13:07.898 9627 9646 E MonitoringInstr: at java.lang.ClassLoader.loadClass(ClassLoader.java:312)
06-09 17:13:07.898 9627 9646 E MonitoringInstr: ... 5 more
06-09 17:13:07.899 9627 9646 E THREAD_STATE: Thread[Binder:9627_2,5,main]
06-09 17:13:07.899 9627 9646 E THREAD_STATE:
06-09 17:13:07.899 9627 9646 E THREAD_STATE: Thread[FinalizerDaemon,5,system]
06-09 17:13:07.899 9627 9646 E THREAD_STATE: java.lang.Object.wait(Native Method)
06-09 17:13:07.899 9627 9646 E THREAD_STATE: java.lang.Object.wait(Object.java:442)
06-09 17:13:07.899 9627 9646 E THREAD_STATE: java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:190)
06-09 17:13:07.899 9627 9646 E THREAD_STATE: java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:211)
06-09 17:13:07.899 9627 9646 E THREAD_STATE: java.lang.Daemons$FinalizerDaemon.runInternal(Daemons.java:273)
06-09 17:13:07.899 9627 9646 E THREAD_STATE: java.lang.Daemons$Daemon.run(Daemons.java:139)
06-09 17:13:07.899 9627 9646 E THREAD_STATE: java.lang.Thread.run(Thread.java:920)
06-09 17:13:07.899 9627 9646 E THREAD_STATE:
06-09 17:13:07.899 9627 9646 E THREAD_STATE: Thread[Signal Catcher,10,system]
06-09 17:13:07.899 9627 9646 E THREAD_STATE:
06-09 17:13:07.899 9627 9646 E THREAD_STATE: Thread[FinalizerWatchdogDaemon,5,system]
06-09 17:13:07.899 9627 9646 E THREAD_STATE: java.lang.Object.wait(Native Method)
06-09 17:13:07.899 9627 9646 E THREAD_STATE: java.lang.Object.wait(Object.java:442)
06-09 17:13:07.899 9627 9646 E THREAD_STATE: java.lang.Object.wait(Object.java:568)
06-09 17:13:07.899 9627 9646 E THREAD_STATE: java.lang.Daemons$FinalizerWatchdogDaemon.sleepUntilNeeded(Daemons.java:341)
06-09 17:13:07.899 9627 9646 E THREAD_STATE: java.lang.Daemons$FinalizerWatchdogDaemon.runInternal(Daemons.java:321)
06-09 17:13:07.899 9627 9646 E THREAD_STATE: java.lang.Daemons$Daemon.run(Daemons.java:139)
06-09 17:13:07.899 9627 9646 E THREAD_STATE: java.lang.Thread.run(Thread.java:920)
06-09 17:13:07.899 9627 9646 E THREAD_STATE:
06-09 17:13:07.899 9627 9646 E THREAD_STATE: Thread[Profile Saver,5,system]
06-09 17:13:07.899 9627 9646 E THREAD_STATE:
06-09 17:13:07.899 9627 9646 E THREAD_STATE: Thread[Binder:9627_3,5,main]
06-09 17:13:07.899 9627 9646 E THREAD_STATE:
06-09 17:13:07.899 9627 9646 E THREAD_STATE: Thread[Binder:9627_1,5,main]
06-09 17:13:07.899 9627 9646 E THREAD_STATE:
06-09 17:13:07.899 9627 9646 E THREAD_STATE: Thread[InstrumentationConnectionThread,5,main]
06-09 17:13:07.899 9627 9646 E THREAD_STATE: android.os.MessageQueue.nativePollOnce(Native Method)
06-09 17:13:07.899 9627 9646 E THREAD_STATE: android.os.MessageQueue.next(MessageQueue.java:335)
06-09 17:13:07.899 9627 9646 E THREAD_STATE: android.os.Looper.loopOnce(Looper.java:161)
06-09 17:13:07.899 9627 9646 E THREAD_STATE: android.os.Looper.loop(Looper.java:288)
06-09 17:13:07.899 9627 9646 E THREAD_STATE: android.os.HandlerThread.run(HandlerThread.java:67)
06-09 17:13:07.899 9627 9646 E THREAD_STATE:
06-09 17:13:07.899 9627 9646 E THREAD_STATE: Thread[Instr: com.google.android.mobly.snippet.SnippetRunner,5,main]
06-09 17:13:07.899 9627 9646 E THREAD_STATE: dalvik.system.VMStack.getThreadStackTrace(Native Method)
06-09 17:13:07.899 9627 9646 E THREAD_STATE: java.lang.Thread.getStackTrace(Thread.java:1724)
06-09 17:13:07.899 9627 9646 E THREAD_STATE: java.lang.Thread.getAllStackTraces(Thread.java:1800)
06-09 17:13:07.899 9627 9646 E THREAD_STATE: androidx.test.runner.MonitoringInstrumentation.getThreadState(MonitoringInstrumentation.java:748)
06-09 17:13:07.899 9627 9646 E THREAD_STATE: androidx.test.runner.MonitoringInstrumentation.dumpThreadStateToOutputs(MonitoringInstrumentation.java:743)
06-09 17:13:07.899 9627 9646 E THREAD_STATE: androidx.test.runner.MonitoringInstrumentation.onException(MonitoringInstrumentation.java:737)
06-09 17:13:07.899 9627 9646 E THREAD_STATE: androidx.test.runner.AndroidJUnitRunner.onException(AndroidJUnitRunner.java:626)
06-09 17:13:07.899 9627 9646 E THREAD_STATE: androidx.test.runner.MonitoringInstrumentation$3.uncaughtException(MonitoringInstrumentation.java:327)
06-09 17:13:07.899 9627 9646 E THREAD_STATE: java.lang.ThreadGroup.uncaughtException(ThreadGroup.java:1073)
06-09 17:13:07.899 9627 9646 E THREAD_STATE: java.lang.ThreadGroup.uncaughtException(ThreadGroup.java:1068)
06-09 17:13:07.899 9627 9646 E THREAD_STATE: java.lang.Thread.dispatchUncaughtException(Thread.java:2200)
06-09 17:13:07.899 9627 9646 E THREAD_STATE:
06-09 17:13:07.899 9627 9646 E THREAD_STATE: Thread[HeapTaskDaemon,5,system]
06-09 17:13:07.899 9627 9646 E THREAD_STATE:
06-09 17:13:07.899 9627 9646 E THREAD_STATE: Thread[Jit thread pool worker thread 0,5,system]
06-09 17:13:07.899 9627 9646 E THREAD_STATE:
06-09 17:13:07.899 9627 9646 E THREAD_STATE: Thread[ReferenceQueueDaemon,5,system]
06-09 17:13:07.899 9627 9646 E THREAD_STATE: java.lang.Object.wait(Native Method)
06-09 17:13:07.899 9627 9646 E THREAD_STATE: java.lang.Object.wait(Object.java:442)
06-09 17:13:07.899 9627 9646 E THREAD_STATE: java.lang.Object.wait(Object.java:568)
06-09 17:13:07.899 9627 9646 E THREAD_STATE: java.lang.Daemons$ReferenceQueueDaemon.runInternal(Daemons.java:217)
06-09 17:13:07.899 9627 9646 E THREAD_STATE: java.lang.Daemons$Daemon.run(Daemons.java:139)
06-09 17:13:07.899 9627 9646 E THREAD_STATE: java.lang.Thread.run(Thread.java:920)
06-09 17:13:07.899 9627 9646 E THREAD_STATE:
06-09 17:13:07.899 9627 9646 E THREAD_STATE: Thread[main,5,main]
06-09 17:13:07.899 9627 9646 E THREAD_STATE: android.os.MessageQueue.nativePollOnce(Native Method)
06-09 17:13:07.899 9627 9646 E THREAD_STATE: android.os.MessageQueue.next(MessageQueue.java:335)
06-09 17:13:07.899 9627 9646 E THREAD_STATE: android.os.Looper.loopOnce(Looper.java:161)
06-09 17:13:07.899 9627 9646 E THREAD_STATE: android.os.Looper.loop(Looper.java:288)
06-09 17:13:07.899 9627 9646 E THREAD_STATE: android.app.ActivityThread.main(ActivityThread.java:7870)
06-09 17:13:07.899 9627 9646 E THREAD_STATE: java.lang.reflect.Method.invoke(Native Method)
06-09 17:13:07.899 9627 9646 E THREAD_STATE: com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
06-09 17:13:07.899 9627 9646 E THREAD_STATE: com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
06-09 17:13:07.899 9627 9646 E THREAD_STATE:
06-09 17:13:07.899 9627 9646 E MonitoringInstr: Dying now...
06-09 17:13:07.899 9627 9646 W MonitoringInstr: Invoking default uncaught exception handler com.android.internal.os.RuntimeInit$KillApplicationHandler@d99adad (a class com.android.internal.os.RuntimeInit$KillApplicationHandler)
06-09 17:13:07.902 9627 9646 I Process : Sending signal. PID: 9627 SIG: 9

implementation 'com.google.android.mobly:mobly-snippet-lib:1.4.0'

Support default value for params

It would be nice to be able to set default values for params of String types and numeric types.
Sample use case:
Default value for timeout param is useful for methods that need to wait on async events.

Make `@RpcMinSdk` work for classes

Right now @RpcMinSdk only works for individual rpc methods.
Would be nice to enforce it on class level as well since sometimes entire classes are added in Android at an API level.

MAIN_PACKAGE string differs from package

https://github.com/google/mobly-snippet-lib/blame/9cb6d9e93baa6d4e0b8a650126a1fa3ef4c9810e/examples/ex4_uiautomator/src/main/java/com/google/android/mobly/snippet/example4/UiAutomatorSnippet.java#L49-L50

package com.google.android.mobly.snippet.example4;

...
    private static final String MAIN_PACKAGE = "com.google.android.mobly.snippet.example2";

Could a comment be added for why the MAIN_PACKAGE is different from the package? Does this code actually work??

Incorrect log tag on itel_a51

On this device, instead of the correct class name, we see the following returned by logger's getTag
com.google.android.mobly.snippet.bundled.VMStack:-2

I can't even....

got a crash on android R

here is the crash info
08-20 05:18:06.796 5560 5582 E AndroidRuntime: java.lang.NoClassDefFoundError: Failed resolution of: Landroid/test/suitebuilder/annotation/Suppress;
08-20 05:18:06.796 5560 5582 E AndroidRuntime: at android.support.test.internal.runner.TestRequestBuilder.(TestRequestBuilder.java:83)
08-20 05:18:06.796 5560 5582 E AndroidRuntime: at android.support.test.internal.runner.TestRequestBuilder.(TestRequestBuilder.java:546)
08-20 05:18:06.796 5560 5582 E AndroidRuntime: at android.support.test.runner.AndroidJUnitRunner.createTestRequestBuilder(AndroidJUnitRunner.java:501)
08-20 05:18:06.796 5560 5582 E AndroidRuntime: at android.support.test.runner.AndroidJUnitRunner.buildRequest(AndroidJUnitRunner.java:476)
08-20 05:18:06.796 5560 5582 E AndroidRuntime: at android.support.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:373)
08-20 05:18:06.796 5560 5582 E AndroidRuntime: at com.google.android.mobly.snippet.SnippetRunner.onStart(SnippetRunner.java:133)
08-20 05:18:06.796 5560 5582 E AndroidRuntime: at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:2205)

I traced the full method call stack and seeked some Info about android.test.suitebuilder.annotation.Suppress, the module android.test has been removed in API LEVEL 28, I'am an backend developer shot of android application development, so, I wanna get some help on how make it work on Android 28+

`presubmit` option broken (race condition?)

After making code changes, when running ./gradlew presubmit for the first time, it often fails with the following error:

foo/bar/some/file.java: formatted successfully

> Task :mobly-snippet-lib:openLintReport
The file /Users/angli/Developer/mobly-snippet-lib/third_party/sl4a/build/reports/lint-results.html does not exist.


FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':mobly-snippet-lib:openLintReport'.
> Process 'command 'open'' finished with non-zero exit value 1

Immediately run the presubmit command again, it passes.

Seems like there might be a race condition between the formatting task, the linter page generation, and the lint page opening.

@kdart

Better error message for when specified snippet class does not exist

If the snippet manifest's meta-data has classes that don't exist, say:

    <meta-data
        android:name="mobly-snippets"
        android:value="com.my.snippet.DoesNotExist" />

Snippet simply crashes on launching with no proper error message:

$ snippet_shell.py com.my.snippet
INFO:root:Stopping snippet apk with: am instrument -w -e action stop com.my.snippet/com.google.android.mobly.snippet.SnippetRunner
INFO:root:Launching snippet apk with: am instrument -e action start -e port 3429 com.my.snippet/com.google.android.mobly.snippet.SnippetRunner
Traceback (most recent call last):
  File "/usr/local/bin/snippet_shell.py", line 10, in <module>
    execfile(__file__)
  File "/usr/local/google/home/angli/Developer/mobly/tools/snippet_shell.py", line 65, in <module>
    SnippetShell(args.package[0]).main(args.serial)
  File "/usr/local/google/home/angli/Developer/mobly/mobly/controllers/android_device_lib/jsonrpc_shell_base.py", line 86, in main
    self.start_console()
  File "/usr/local/google/home/angli/Developer/mobly/mobly/controllers/android_device_lib/jsonrpc_shell_base.py", line 71, in start_console
    self._start_services(console_env)
  File "/usr/local/google/home/angli/Developer/mobly/tools/snippet_shell.py", line 42, in _start_services
    self._ad.load_snippet(name='snippet', package=self._package)
  File "/usr/local/google/home/angli/Developer/mobly/mobly/controllers/android_device.py", line 566, in load_snippet
    self._start_jsonrpc_client(client, host_port, device_port)
  File "/usr/local/google/home/angli/Developer/mobly/mobly/controllers/android_device.py", line 608, in _start_jsonrpc_client
    client.connect(port=host_port)
  File "/usr/local/google/home/angli/Developer/mobly/mobly/controllers/android_device_lib/jsonrpc_client_base.py", line 212, in connect
    resp = self._cmd(cmd, uid)
  File "/usr/local/google/home/angli/Developer/mobly/mobly/controllers/android_device_lib/jsonrpc_client_base.py", line 242, in _cmd
    return self._client.readline()
  File "/usr/lib/python2.7/socket.py", line 447, in readline
    data = self._sock.recv(self._rbufsize)
socket.error: [Errno 104] Connection reset by peer

About ex5_schedule_rpc

https://github.com/google/mobly-snippet-lib/tree/master/examples/ex5_schedule_rpc#why-this-is-needed:

3、After 10 seconds, the phone call starts while USB is off.

4、Finally, after the phone call is finished, Monsoon data collection completes and USB is re-enabled.

5、The test retrieves any cached events or data from the device.

mobly can not make continue connection to the device after USB is re-enabled.I try to add _forward_device_port in CallbackHandlerBase.waitAndGet, move make_connection_with_forwarded_port action to the CallbackHandlerBase.waitAndGet. But not successful,Have you tried it? Or am I thinking wrong?

09-20 06:16:08.907 10613 10632 V NotificationManager: com.google.android.mobly.snippet.example5: notifyAsUser(1, Notification(channel=msl_channel shortcut=null contentView=null vibrate=null sound=null defaults=0x0 flags=0x22 color=0x00000000 vis=PRIVATE))
09-20 06:16:08.908 10613 10632 I com.google.android.mobly.snippet.example5.SnippetRunner:194: Sending protocol message: SNIPPET SERVING, PORT 35373
09-20 06:16:08.909 10613 10632 I com.google.android.mobly.snippet.example5.SnippetRunner:161: Snippet server started for process 10613 on port 35373
09-20 06:16:08.954 10613 10633 D TrafficStats: tagSocket(84) with statsTag=0xffffffff, statsUid=-1
09-20 06:16:08.955 10613 10633 V com.google.android.mobly.snippet.example5.SimpleServer:211: Received: {"cmd": "initiate", "uid": -1}
09-20 06:16:08.956 10613 10633 D com.google.android.mobly.snippet.example5.SimpleServer:218: Initiate a new session
09-20 06:16:08.957 10613 10633 V com.google.android.mobly.snippet.example5.SimpleServer:253: Sent: {"uid":1,"status":true}
09-20 06:16:08.957 10613 10634 V com.google.android.mobly.snippet.example5.SimpleServer$ConnectionThread:105: Server thread 44 started.
09-20 06:16:08.957 10613 10634 D com.google.android.mobly.snippet.example5.SimpleServer$ConnectionThread:108: Handling RPC connection in 44
09-20 06:16:08.957 10613 10634 D com.google.android.mobly.snippet.example5.JsonRpcServer:51: UID 1
09-20 06:16:08.958 10613 10634 V com.google.android.mobly.snippet.example5.JsonRpcServer:54: Session 1 Received: {"id": 0, "method": "scheduleRpc", "params": ["asyncMakeToast", 15000, ["message"]]}
09-20 06:16:08.959 10613 10634 D com.google.android.mobly.snippet.example5.SnippetManager:259: Constructing class com.google.android.mobly.snippet.schedulerpc.ScheduleRpcSnippet
09-20 06:16:08.959 10613 10634 D com.google.android.mobly.snippet.example5.SnippetManager:286: Invoking RPC method class com.google.android.mobly.snippet.schedulerpc.ScheduleRpcSnippet#scheduleRpc
09-20 06:16:08.960 10613 10634 V com.google.android.mobly.snippet.example5.JsonRpcServer:116: Session 1 Sent: {"id":0,"result":null,"callback":"1-0","error":null}
09-20 06:16:18.984 10613 10633 D TrafficStats: tagSocket(86) with statsTag=0xffffffff, statsUid=-1
09-20 06:16:18.986 10613 10633 V com.google.android.mobly.snippet.example5.SimpleServer:211: Received: {"cmd": "continue", "uid": 1}
09-20 06:16:18.986 10613 10633 D com.google.android.mobly.snippet.example5.SimpleServer:230: Continue an existing session
09-20 06:16:18.986 10613 10633 D com.google.android.mobly.snippet.example5.SimpleServer:231: keys: [1]
09-20 06:16:18.987 10613 10633 V com.google.android.mobly.snippet.example5.SimpleServer:253: Sent: {"uid":1,"status":true}
09-20 06:16:18.987 10613 10650 V com.google.android.mobly.snippet.example5.SimpleServer$ConnectionThread:105: Server thread 46 started.
09-20 06:16:18.987 10613 10650 D com.google.android.mobly.snippet.example5.SimpleServer$ConnectionThread:108: Handling RPC connection in 46
09-20 06:16:18.987 10613 10650 D com.google.android.mobly.snippet.example5.JsonRpcServer:51: UID 1
09-20 06:16:18.989 10613 10650 V com.google.android.mobly.snippet.example5.JsonRpcServer:54: Session 1 Received: {"id": 0, "method": "eventWaitAndGet", "params": ["1-0", "asyncMakeToast", 120000]}
09-20 06:16:18.990 10613 10650 D com.google.android.mobly.snippet.example5.SnippetManager:259: Constructing class com.google.android.mobly.snippet.event.EventSnippet
09-20 06:16:18.990 10613 10650 D com.google.android.mobly.snippet.example5.SnippetManager:286: Invoking RPC method class com.google.android.mobly.snippet.event.EventSnippet#eventWaitAndGet
09-20 06:16:23.962 10613 10636 D com.google.android.mobly.snippet.example5.SnippetManager:259: Constructing class com.google.android.mobly.snippet.example5.ExampleScheduleRpcSnippet
09-20 06:16:23.963 10613 10636 D com.google.android.mobly.snippet.example5.SnippetManager:286: Invoking RPC method class com.google.android.mobly.snippet.example5.ExampleScheduleRpcSnippet#asyncMakeToast
09-20 06:16:23.964 10613 10636 V com.google.android.mobly.snippet.example5.EventCache:94: Posted event(1-0|asyncMakeToast)
09-20 06:16:23.965 10613 10659 D com.google.android.mobly.snippet.example5.ExampleScheduleRpcSnippet$AsyncTask:59: Sleeping for 10s before posting an event.
09-20 06:16:23.967 10613 10650 V com.google.android.mobly.snippet.example5.JsonRpcServer:116: Session 1 Sent: {"id":0,"result":{"callbackId":"1-0","name":"asyncMakeToast","time":1695161783961,"data":{"result":"null","callback":"1-0","id":"0","error":"null"}},"callback":null,"error":null}
09-20 06:16:24.182 2755 3672 I ActivityManager: Killing 10613:com.google.android.mobly.snippet.example5/u0a265 (adj 0): stop com.google.android.mobly.snippet.example5 due to start instr
09-20 06:16:23.527 1 1 I : [1:1127:logd.klogd] MZ VendorHooks: [3672:binder:2755_3] sig 9 to [10613:nippet.example5] state=S

Support of RPC scheduling while device is disconnected

We may still need to perform operations while device is disconnected (e.g., USB is off). In this case, we can:

  • Schedule RPC calls with some delay before device got disconnected.
  • While device is disconnected, as long as the snippet server is not stopped, the scheduled RPC will be executed with some delay
  • After device back online, the execute result could be retrieved from cached event.

Potential racing condition in `SnippetManager.get`

In the context of SnippetManager.get:
"runMainThreadCaller() will wait for it to finish. The only conflict I can think of is if you manually create a new connection to the apk (to get another connection thread) and call two RPCs defined in the same object from those two threads at the same time. In this case it would get constructed twice and one instance will be thrown out. Is this the same race condition you were picturing?"

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.