aboutsummaryrefslogtreecommitdiff
path: root/third_party/sl4a/src/main/java/com
diff options
context:
space:
mode:
authorl-meng <lmeng@google.com>2017-06-19 12:29:00 -0700
committerGitHub <noreply@github.com>2017-06-19 12:29:00 -0700
commitb53f51e748533c8703828d8921fb185077e8d845 (patch)
tree136645bb4dcfdf4be0e6ef6efa75602e86b1442f /third_party/sl4a/src/main/java/com
parent8c963b0b002c8ac49e7ae4133b4aaaa9380337fb (diff)
downloadmobly-snippet-lib-b53f51e748533c8703828d8921fb185077e8d845.tar.gz
Support of RPC scheduling (#61)
* Support of RPC scheduling To perform operations while device is disconnected (e.g., USB is off), RPCs need to be scheduled with certain delay before disconnection happens. While device is disconnected, as long as the snippet is still running, the scheduled RPCs should be able to be executed. The RPC result could be retrieved as cached events once device get back online.
Diffstat (limited to 'third_party/sl4a/src/main/java/com')
-rw-r--r--third_party/sl4a/src/main/java/com/google/android/mobly/snippet/manager/ReflectionSnippetManagerFactory.java97
-rw-r--r--third_party/sl4a/src/main/java/com/google/android/mobly/snippet/manager/SnippetManager.java83
-rw-r--r--third_party/sl4a/src/main/java/com/google/android/mobly/snippet/manager/SnippetManagerFactory.java25
-rw-r--r--third_party/sl4a/src/main/java/com/google/android/mobly/snippet/rpc/AndroidProxy.java6
-rw-r--r--third_party/sl4a/src/main/java/com/google/android/mobly/snippet/rpc/JsonRpcResult.java14
-rw-r--r--third_party/sl4a/src/main/java/com/google/android/mobly/snippet/rpc/JsonRpcServer.java66
-rw-r--r--third_party/sl4a/src/main/java/com/google/android/mobly/snippet/schedulerpc/ScheduleRpcSnippet.java45
-rw-r--r--third_party/sl4a/src/main/java/com/google/android/mobly/snippet/util/RpcUtil.java138
8 files changed, 288 insertions, 186 deletions
diff --git a/third_party/sl4a/src/main/java/com/google/android/mobly/snippet/manager/ReflectionSnippetManagerFactory.java b/third_party/sl4a/src/main/java/com/google/android/mobly/snippet/manager/ReflectionSnippetManagerFactory.java
deleted file mode 100644
index 5e4259d..0000000
--- a/third_party/sl4a/src/main/java/com/google/android/mobly/snippet/manager/ReflectionSnippetManagerFactory.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2016 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-package com.google.android.mobly.snippet.manager;
-
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.os.Bundle;
-import com.google.android.mobly.snippet.Snippet;
-import com.google.android.mobly.snippet.event.EventSnippet;
-import com.google.android.mobly.snippet.util.Log;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
-public class ReflectionSnippetManagerFactory implements SnippetManagerFactory {
- private static final String METADATA_TAG_NAME = "mobly-snippets";
-
- private final Context mContext;
- private final Set<Class<? extends Snippet>> mClasses;
- private final Map<Integer, SnippetManager> mSnippetManagers;
-
- public ReflectionSnippetManagerFactory(Context context) {
- mContext = context;
- mClasses = loadSnippets();
- mSnippetManagers = new HashMap<>();
- }
-
- @Override
- public SnippetManager create(Integer UID) {
- SnippetManager manager = new SnippetManager(mClasses);
- mSnippetManagers.put(UID, manager);
- return manager;
- }
-
- @Override
- public Map<Integer, SnippetManager> getSnippetManagers() {
- return Collections.unmodifiableMap(mSnippetManagers);
- }
-
- private Set<Class<? extends Snippet>> loadSnippets() {
- ApplicationInfo appInfo;
- try {
- appInfo =
- mContext.getPackageManager()
- .getApplicationInfo(
- mContext.getPackageName(), PackageManager.GET_META_DATA);
- } catch (PackageManager.NameNotFoundException e) {
- throw new IllegalStateException(
- "Failed to find ApplicationInfo with package name: "
- + mContext.getPackageName());
- }
- Bundle metadata = appInfo.metaData;
- String snippets = metadata.getString(METADATA_TAG_NAME);
- if (snippets == null) {
- throw new IllegalStateException(
- "AndroidManifest.xml does not contain a <metadata> tag with "
- + "name=\""
- + METADATA_TAG_NAME
- + "\"");
- }
- String[] snippetClassNames = snippets.split("\\s*,\\s*");
- Set<Class<? extends Snippet>> receiverSet = new HashSet<>();
- /** Add the event snippet class which is provided within the Snippet Lib. */
- receiverSet.add(EventSnippet.class);
- for (String snippetClassName : snippetClassNames) {
- try {
- Log.i("Trying to load Snippet class: " + snippetClassName);
- Class<?> snippetClass = Class.forName(snippetClassName);
- receiverSet.add((Class<? extends Snippet>) snippetClass);
- } catch (ClassNotFoundException e) {
- Log.e("Failed to find class " + snippetClassName);
- throw new RuntimeException(e);
- }
- }
- if (receiverSet.isEmpty()) {
- throw new IllegalStateException("Found no subclasses of Snippet.");
- }
- return receiverSet;
- }
-}
diff --git a/third_party/sl4a/src/main/java/com/google/android/mobly/snippet/manager/SnippetManager.java b/third_party/sl4a/src/main/java/com/google/android/mobly/snippet/manager/SnippetManager.java
index ff0aec3..66e33b3 100644
--- a/third_party/sl4a/src/main/java/com/google/android/mobly/snippet/manager/SnippetManager.java
+++ b/third_party/sl4a/src/main/java/com/google/android/mobly/snippet/manager/SnippetManager.java
@@ -16,11 +16,17 @@
package com.google.android.mobly.snippet.manager;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.os.Build;
+import android.os.Bundle;
import com.google.android.mobly.snippet.Snippet;
+import com.google.android.mobly.snippet.event.EventSnippet;
import com.google.android.mobly.snippet.rpc.MethodDescriptor;
import com.google.android.mobly.snippet.rpc.RpcMinSdk;
import com.google.android.mobly.snippet.rpc.RunOnUiThread;
+import com.google.android.mobly.snippet.schedulerpc.ScheduleRpcSnippet;
import com.google.android.mobly.snippet.util.Log;
import com.google.android.mobly.snippet.util.MainThread;
import com.google.android.mobly.snippet.util.SnippetLibException;
@@ -30,18 +36,24 @@ import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.Callable;
public class SnippetManager {
+ private static final String METADATA_TAG_NAME = "mobly-snippets";
private final Map<Class<? extends Snippet>, Snippet> mSnippets;
/** A map of strings to known RPCs. */
private final Map<String, MethodDescriptor> mKnownRpcs;
- public SnippetManager(Collection<Class<? extends Snippet>> classList) {
+ private static SnippetManager sInstance = null;
+ private boolean mShutdown = false;
+
+ private SnippetManager(Collection<Class<? extends Snippet>> classList) {
// Synchronized for multiple connections on the same session. Can't use ConcurrentHashMap
// because we have to put in a value of 'null' before the class is constructed, but
// ConcurrentHashMap does not allow null values.
@@ -65,6 +77,25 @@ public class SnippetManager {
mKnownRpcs = Collections.unmodifiableMap(knownRpcs);
}
+ public static synchronized SnippetManager initSnippetManager(Context context) {
+ if (sInstance != null) {
+ throw new IllegalStateException("SnippetManager should not be re-initialized");
+ }
+ Collection<Class<? extends Snippet>> classList = findSnippetClassesFromMetadata(context);
+ sInstance = new SnippetManager(classList);
+ return sInstance;
+ }
+
+ public static SnippetManager getInstance() {
+ if (sInstance == null) {
+ throw new IllegalStateException("getInstance() called before init()");
+ }
+ if (sInstance.isShutdown()) {
+ throw new IllegalStateException("shutdown() called before getInstance()");
+ }
+ return sInstance;
+ }
+
public MethodDescriptor getMethodDescriptor(String methodName) {
return mKnownRpcs.get(methodName);
}
@@ -114,6 +145,56 @@ public class SnippetManager {
entry.getValue().shutdown();
}
}
+ mSnippets.clear();
+ mKnownRpcs.clear();
+ mShutdown = true;
+ }
+
+ public boolean isShutdown() {
+ return mShutdown;
+ }
+
+ private static Set<Class<? extends Snippet>> findSnippetClassesFromMetadata(Context context) {
+ ApplicationInfo appInfo;
+ try {
+ appInfo =
+ context.getPackageManager()
+ .getApplicationInfo(
+ context.getPackageName(), PackageManager.GET_META_DATA);
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new IllegalStateException(
+ "Failed to find ApplicationInfo with package name: "
+ + context.getPackageName());
+ }
+ Bundle metadata = appInfo.metaData;
+ String snippets = metadata.getString(METADATA_TAG_NAME);
+ if (snippets == null) {
+ throw new IllegalStateException(
+ "AndroidManifest.xml does not contain a <metadata> tag with "
+ + "name=\""
+ + METADATA_TAG_NAME
+ + "\"");
+ }
+ String[] snippetClassNames = snippets.split("\\s*,\\s*");
+ Set<Class<? extends Snippet>> receiverSet = new HashSet<>();
+ /** Add the event snippet class which is provided within the Snippet Lib. */
+ receiverSet.add(EventSnippet.class);
+ /** Add the schedule RPC snippet class which is provided within the Snippet Lib. */
+ receiverSet.add(ScheduleRpcSnippet.class);
+ for (String snippetClassName : snippetClassNames) {
+ try {
+ Log.i("Trying to load Snippet class: " + snippetClassName);
+ Class<?> snippetClass = Class.forName(snippetClassName);
+ receiverSet.add((Class<? extends Snippet>) snippetClass);
+ } catch (ClassNotFoundException e) {
+ Log.e("Failed to find class " + snippetClassName);
+ throw new RuntimeException(e);
+ }
+ }
+ if (receiverSet.isEmpty()) {
+ throw new IllegalStateException("Found no subclasses of Snippet.");
+ }
+ return receiverSet;
}
private Snippet get(Class<? extends Snippet> clazz) throws Exception {
diff --git a/third_party/sl4a/src/main/java/com/google/android/mobly/snippet/manager/SnippetManagerFactory.java b/third_party/sl4a/src/main/java/com/google/android/mobly/snippet/manager/SnippetManagerFactory.java
deleted file mode 100644
index 50a3552..0000000
--- a/third_party/sl4a/src/main/java/com/google/android/mobly/snippet/manager/SnippetManagerFactory.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2016 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-package com.google.android.mobly.snippet.manager;
-
-import java.util.Map;
-
-public interface SnippetManagerFactory {
- SnippetManager create(Integer UID);
-
- Map<Integer, SnippetManager> getSnippetManagers();
-}
diff --git a/third_party/sl4a/src/main/java/com/google/android/mobly/snippet/rpc/AndroidProxy.java b/third_party/sl4a/src/main/java/com/google/android/mobly/snippet/rpc/AndroidProxy.java
index cb2e772..9428c82 100644
--- a/third_party/sl4a/src/main/java/com/google/android/mobly/snippet/rpc/AndroidProxy.java
+++ b/third_party/sl4a/src/main/java/com/google/android/mobly/snippet/rpc/AndroidProxy.java
@@ -17,18 +17,14 @@
package com.google.android.mobly.snippet.rpc;
import android.content.Context;
-import com.google.android.mobly.snippet.manager.ReflectionSnippetManagerFactory;
-import com.google.android.mobly.snippet.manager.SnippetManagerFactory;
import java.io.IOException;
public class AndroidProxy {
private final JsonRpcServer mJsonRpcServer;
- private final SnippetManagerFactory mSnippetManagerFactory;
public AndroidProxy(Context context) {
- mSnippetManagerFactory = new ReflectionSnippetManagerFactory(context);
- mJsonRpcServer = new JsonRpcServer(mSnippetManagerFactory);
+ mJsonRpcServer = new JsonRpcServer(context);
}
public void startLocal(int port) throws IOException {
diff --git a/third_party/sl4a/src/main/java/com/google/android/mobly/snippet/rpc/JsonRpcResult.java b/third_party/sl4a/src/main/java/com/google/android/mobly/snippet/rpc/JsonRpcResult.java
index de8537c..90cf8f9 100644
--- a/third_party/sl4a/src/main/java/com/google/android/mobly/snippet/rpc/JsonRpcResult.java
+++ b/third_party/sl4a/src/main/java/com/google/android/mobly/snippet/rpc/JsonRpcResult.java
@@ -60,11 +60,7 @@ public class JsonRpcResult {
}
public static JSONObject error(int id, Throwable t) throws JSONException {
- StringWriter stackTraceWriter = new StringWriter();
- stackTraceWriter.write("\n-------------- Java Stacktrace ---------------\n");
- t.printStackTrace(new PrintWriter(stackTraceWriter));
- stackTraceWriter.write("----------------------------------------------");
- String stackTrace = stackTraceWriter.toString();
+ String stackTrace = getStackTrace(t);
JSONObject json = new JSONObject();
json.put("id", id);
json.put("result", JSONObject.NULL);
@@ -72,4 +68,12 @@ public class JsonRpcResult {
json.put("error", stackTrace);
return json;
}
+
+ public static String getStackTrace(Throwable throwable) {
+ StringWriter stackTraceWriter = new StringWriter();
+ stackTraceWriter.write("\n-------------- Java Stacktrace ---------------\n");
+ throwable.printStackTrace(new PrintWriter(stackTraceWriter));
+ stackTraceWriter.write("----------------------------------------------");
+ return stackTraceWriter.toString();
+ }
}
diff --git a/third_party/sl4a/src/main/java/com/google/android/mobly/snippet/rpc/JsonRpcServer.java b/third_party/sl4a/src/main/java/com/google/android/mobly/snippet/rpc/JsonRpcServer.java
index 219c471..1dde423 100644
--- a/third_party/sl4a/src/main/java/com/google/android/mobly/snippet/rpc/JsonRpcServer.java
+++ b/third_party/sl4a/src/main/java/com/google/android/mobly/snippet/rpc/JsonRpcServer.java
@@ -16,9 +16,10 @@
package com.google.android.mobly.snippet.rpc;
+import android.content.Context;
import com.google.android.mobly.snippet.manager.SnippetManager;
-import com.google.android.mobly.snippet.manager.SnippetManagerFactory;
import com.google.android.mobly.snippet.util.Log;
+import com.google.android.mobly.snippet.util.RpcUtil;
import java.io.BufferedReader;
import java.io.PrintWriter;
import java.net.Socket;
@@ -35,33 +36,19 @@ public class JsonRpcServer extends SimpleServer {
private static final String CMD_CLOSE_SESSION = "closeSl4aSession";
private static final String CMD_HELP = "help";
- private final SnippetManagerFactory mSnippetManagerFactory;
+ private final SnippetManager mSnippetManager;
+ private final RpcUtil mRpcUtil;
- /**
- * Construct a {@link JsonRpcServer} connected to the provided {@link SnippetManager}.
- *
- * @param managerFactory the {@link SnippetManager} to register with the server
- */
- public JsonRpcServer(SnippetManagerFactory managerFactory) {
- mSnippetManagerFactory = managerFactory;
+ /** Construct a {@link JsonRpcServer} connected to the provided {@link SnippetManager}. */
+ public JsonRpcServer(Context context) {
+ mSnippetManager = SnippetManager.initSnippetManager(context);
+ mRpcUtil = new RpcUtil();
}
@Override
protected void handleRPCConnection(
Socket sock, Integer UID, BufferedReader reader, PrintWriter writer) throws Exception {
- SnippetManager receiverManager = null;
- Map<Integer, SnippetManager> mgrs = mSnippetManagerFactory.getSnippetManagers();
- synchronized (mgrs) {
- Log.d("UID " + UID);
- Log.d("manager map keys: " + mSnippetManagerFactory.getSnippetManagers().keySet());
- if (mgrs.containsKey(UID)) {
- Log.d("Look up existing session");
- receiverManager = mgrs.get(UID);
- } else {
- Log.d("Create a new session");
- receiverManager = mSnippetManagerFactory.create(UID);
- }
- }
+ Log.d("UID " + UID);
String data;
while ((data = reader.readLine()) != null) {
Log.v("Session " + UID + " Received: " + data);
@@ -72,16 +59,13 @@ public class JsonRpcServer extends SimpleServer {
// Handle builtin commands
if (method.equals(CMD_HELP)) {
- help(writer, id, receiverManager, UID);
+ help(writer, id, mSnippetManager, UID);
continue;
} else if (method.equals(CMD_CLOSE_SESSION)) {
Log.d("Got shutdown signal");
synchronized (writer) {
// Shut down all RPC receivers.
- for (SnippetManager manager :
- mSnippetManagerFactory.getSnippetManagers().values()) {
- manager.shutdown();
- }
+ mSnippetManager.shutdown();
// Shut down this client connection. As soon as this happens, the client will
// kill us by triggering the 'stop' action from another instrumentation, so no
@@ -93,35 +77,11 @@ public class JsonRpcServer extends SimpleServer {
// Shut down this server.
shutdown();
- mgrs.clear();
}
return;
}
-
- MethodDescriptor rpc = receiverManager.getMethodDescriptor(method);
- if (rpc == null) {
- send(writer, JsonRpcResult.error(id, new RpcError("Unknown RPC: " + method)), UID);
- continue;
- }
- try {
- /** If calling an {@link AsyncRpc}, put the message ID as the first param. */
- if (rpc.isAsync()) {
- String callbackId = String.format("%d-%d", UID, id);
- JSONArray newParams = new JSONArray();
- newParams.put(callbackId);
- for (int i = 0; i < params.length(); i++) {
- newParams.put(params.get(i));
- }
- Object returnValue = rpc.invoke(receiverManager, newParams);
- send(writer, JsonRpcResult.callback(id, returnValue, callbackId), UID);
- } else {
- Object returnValue = rpc.invoke(receiverManager, params);
- send(writer, JsonRpcResult.result(id, returnValue), UID);
- }
- } catch (Throwable t) {
- Log.e("Invocation error.", t);
- send(writer, JsonRpcResult.error(id, t), UID);
- }
+ JSONObject returnValue = mRpcUtil.invokeRpc(method, params, id, UID);
+ send(writer, returnValue, UID);
}
}
diff --git a/third_party/sl4a/src/main/java/com/google/android/mobly/snippet/schedulerpc/ScheduleRpcSnippet.java b/third_party/sl4a/src/main/java/com/google/android/mobly/snippet/schedulerpc/ScheduleRpcSnippet.java
new file mode 100644
index 0000000..88dea24
--- /dev/null
+++ b/third_party/sl4a/src/main/java/com/google/android/mobly/snippet/schedulerpc/ScheduleRpcSnippet.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2017 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.android.mobly.snippet.schedulerpc;
+
+import com.google.android.mobly.snippet.Snippet;
+import com.google.android.mobly.snippet.manager.SnippetManager;
+import com.google.android.mobly.snippet.rpc.AsyncRpc;
+import com.google.android.mobly.snippet.util.RpcUtil;
+import org.json.JSONArray;
+
+/** Snippet that provides {@link AsyncRpc} to schedule other RPCs. */
+public class ScheduleRpcSnippet implements Snippet {
+
+ private final SnippetManager mReceiverManager;
+ private final RpcUtil mRpcUtil;
+
+ public ScheduleRpcSnippet() {
+ mReceiverManager = SnippetManager.getInstance();
+ mRpcUtil = new RpcUtil();
+ }
+
+ @AsyncRpc(description = "Delay the given RPC by provided milli-seconds.")
+ public void scheduleRpc(
+ String callbackId, String methodName, long delayTimerMs, JSONArray params)
+ throws Throwable {
+ mRpcUtil.scheduleRpc(callbackId, methodName, delayTimerMs, params);
+ }
+
+ @Override
+ public void shutdown() {}
+}
diff --git a/third_party/sl4a/src/main/java/com/google/android/mobly/snippet/util/RpcUtil.java b/third_party/sl4a/src/main/java/com/google/android/mobly/snippet/util/RpcUtil.java
new file mode 100644
index 0000000..7eba07f
--- /dev/null
+++ b/third_party/sl4a/src/main/java/com/google/android/mobly/snippet/util/RpcUtil.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2016 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.android.mobly.snippet.util;
+
+import com.google.android.mobly.snippet.event.EventCache;
+import com.google.android.mobly.snippet.event.SnippetEvent;
+import com.google.android.mobly.snippet.manager.SnippetManager;
+import com.google.android.mobly.snippet.rpc.JsonRpcResult;
+import com.google.android.mobly.snippet.rpc.MethodDescriptor;
+import com.google.android.mobly.snippet.rpc.RpcError;
+import java.util.Timer;
+import java.util.TimerTask;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+/**
+ * Class that implements APIs to schedule other RPCs.
+ *
+ * <p>If a device is required to be disconnected (e.g., USB power off), no RPCs can be made while
+ * device is offline.
+ *
+ * <p>However, We still need snippet continue to run and execute previously scheduled RPCs
+ *
+ * <p>The return value of the scheduled RPC is cached in {@link EventCache} and can be retrieved
+ * later after device is back online.
+ */
+public class RpcUtil {
+ // RPC ID is used for reporting responses back to the client. However, the results of
+ // scheduled RPCs are reported back to the client via events instead of through synchronous
+ // responses, so the RPC ID is unused. We pass an arbitrary value of 0.
+ private static final int DEFAULT_ID = 0;
+ private final SnippetManager mReceiverManager;
+ private final EventCache mEventCache = EventCache.getInstance();
+
+ public RpcUtil() {
+ mReceiverManager = SnippetManager.getInstance();
+ }
+
+ /**
+ * Schedule given RPC with some delay.
+ *
+ * @param callbackId The callback ID used to cache RPC results.
+ * @param methodName The RPC name to be scheduled.
+ * @param delayMs The delay in ms
+ * @param params Array of the parameters to the RPC
+ */
+ public void scheduleRpc(
+ final String callbackId,
+ final String methodName,
+ final long delayMs,
+ final JSONArray params)
+ throws Throwable {
+ Timer timer = new Timer();
+ TimerTask task =
+ new TimerTask() {
+ @Override
+ public void run() {
+ SnippetEvent event = new SnippetEvent(callbackId, methodName);
+ try {
+ JSONObject obj = invokeRpc(methodName, params, DEFAULT_ID, callbackId);
+ // Cache RPC method return value.
+ for (int i = 0; i < obj.names().length(); i++) {
+ String key = obj.names().getString(i);
+ event.getData().putString(key, obj.get(key).toString());
+ }
+ } catch (JSONException e) {
+ String stackTrace = JsonRpcResult.getStackTrace(e);
+ event.getData().putString("error", stackTrace);
+ } finally {
+ mEventCache.postEvent(event);
+ }
+ }
+ };
+ timer.schedule(task, delayMs);
+ }
+
+ /**
+ * Invoke the RPC.
+ *
+ * @param methodName The RPC name to be invoked.
+ * @param params Array of the parameters to the RPC
+ * @param id The ID that identifies an RPC
+ * @param UID Globally unique session ID.
+ */
+ public JSONObject invokeRpc(String methodName, JSONArray params, int id, Integer UID)
+ throws JSONException {
+ return invokeRpc(methodName, params, id, String.format("%d-%d", UID, id));
+ }
+
+ /**
+ * Invoke the RPC.
+ *
+ * @param methodName The RPC name to be invoked.
+ * @param params Array of the parameters to the RPC
+ * @param id The ID that identifies an RPC
+ * @param callbackId The callback ID used to cache RPC results.
+ */
+ public JSONObject invokeRpc(String methodName, JSONArray params, int id, String callbackId)
+ throws JSONException {
+ MethodDescriptor rpc = mReceiverManager.getMethodDescriptor(methodName);
+ if (rpc == null) {
+ return JsonRpcResult.error(id, new RpcError("Unknown RPC: " + methodName));
+ }
+ try {
+ JSONArray newParams = new JSONArray();
+ /** If calling an {@link AsyncRpc}, put the message ID as the first param. */
+ if (rpc.isAsync()) {
+ newParams.put(callbackId);
+ for (int i = 0; i < params.length(); i++) {
+ newParams.put(params.get(i));
+ }
+ Object returnValue = rpc.invoke(mReceiverManager, newParams);
+ return JsonRpcResult.callback(id, returnValue, callbackId);
+ } else {
+ Object returnValue = rpc.invoke(mReceiverManager, params);
+ return JsonRpcResult.result(id, returnValue);
+ }
+ } catch (Throwable t) {
+ Log.e("Invocation error.", t);
+ return JsonRpcResult.error(id, t);
+ }
+ }
+}