summaryrefslogtreecommitdiffstats
path: root/remoting/android
diff options
context:
space:
mode:
authorlambroslambrou <lambroslambrou@chromium.org>2016-02-11 11:39:12 -0800
committerCommit bot <commit-bot@chromium.org>2016-02-11 19:40:20 +0000
commit40e8dff71ae626f479e7356515d7bfce0b9b68b0 (patch)
treef3db7ed516cf184d6e5af62afe59ec11815a5f32 /remoting/android
parentf55088d33f76bb51e066e322d54fceabd18b463c (diff)
downloadchromium_src-40e8dff71ae626f479e7356515d7bfce0b9b68b0.zip
chromium_src-40e8dff71ae626f479e7356515d7bfce0b9b68b0.tar.gz
chromium_src-40e8dff71ae626f479e7356515d7bfce0b9b68b0.tar.bz2
Reland: Refactor Chromoting JNI code to use jni/Client (Java changes only).
CL was reverted as the FindBugs step broke on an internal builder. Relanding with FB suppression. Original CL description: This removes globals from jni/JniInterface in favor of passing a jni/Client instance to code that needs to access the Chromoting client connection. The C++ code is unchanged - JniInterface class is still used to marshall Java <-> C++ calls. JniInterface still holds a singleton Client connection, but all other globals have been removed (including the singleton CapabilityManager instance). Each Activity's onCreate() method gets the singleton Client instance, and then passes it to objects that need it (for input injection and so on). This should make it easier to use a fake Client instance for unit-instrumentation tests. BUG=526336,585799 Review URL: https://codereview.chromium.org/1537183002 Cr-Commit-Position: refs/heads/master@{#374924}
Diffstat (limited to 'remoting/android')
-rw-r--r--remoting/android/java/src/org/chromium/chromoting/CapabilityManager.java24
-rw-r--r--remoting/android/java/src/org/chromium/chromoting/Chromoting.java27
-rw-r--r--remoting/android/java/src/org/chromium/chromoting/Desktop.java53
-rw-r--r--remoting/android/java/src/org/chromium/chromoting/DesktopView.java28
-rw-r--r--remoting/android/java/src/org/chromium/chromoting/SessionAuthenticator.java16
-rw-r--r--remoting/android/java/src/org/chromium/chromoting/SessionConnector.java11
-rw-r--r--remoting/android/java/src/org/chromium/chromoting/SimulatedTouchInputStrategy.java12
-rw-r--r--remoting/android/java/src/org/chromium/chromoting/TouchInputStrategy.java13
-rw-r--r--remoting/android/java/src/org/chromium/chromoting/TrackpadInputStrategy.java12
-rw-r--r--remoting/android/java/src/org/chromium/chromoting/cardboard/CardboardRenderer.java16
-rw-r--r--remoting/android/java/src/org/chromium/chromoting/cardboard/Cursor.java13
-rw-r--r--remoting/android/java/src/org/chromium/chromoting/cardboard/Desktop.java11
-rw-r--r--remoting/android/java/src/org/chromium/chromoting/cardboard/DesktopActivity.java22
-rw-r--r--remoting/android/java/src/org/chromium/chromoting/jni/Client.java376
-rw-r--r--remoting/android/java/src/org/chromium/chromoting/jni/JniInterface.java309
-rw-r--r--remoting/android/javatests/src/org/chromium/chromoting/TouchInputStrategyTest.java5
16 files changed, 568 insertions, 380 deletions
diff --git a/remoting/android/java/src/org/chromium/chromoting/CapabilityManager.java b/remoting/android/java/src/org/chromium/chromoting/CapabilityManager.java
index e332e83..0a551bf4e 100644
--- a/remoting/android/java/src/org/chromium/chromoting/CapabilityManager.java
+++ b/remoting/android/java/src/org/chromium/chromoting/CapabilityManager.java
@@ -21,10 +21,6 @@ import java.util.List;
* The CapabilityManager mirrors how the Chromoting host handles extension messages. For each
* incoming extension message, runs through a list of HostExtensionSession objects, giving each one
* a chance to handle the message.
- *
- * The CapabilityManager is a singleton class so we can manage client extensions on an application
- * level. The singleton object may be used from multiple Activities, thus allowing it to support
- * different capabilities at different stages of the application.
*/
public class CapabilityManager {
/** Used to allow objects to receive notifications when the host capabilites are received. */
@@ -50,12 +46,6 @@ public class CapabilityManager {
private static final String TAG = "Chromoting";
- /** Lazily-initialized singleton object that can be used from different Activities. */
- private static CapabilityManager sInstance;
-
- /** Protects access to |sInstance|. */
- private static final Object sInstanceLock = new Object();
-
/** List of all capabilities that are supported by the application. */
private List<String> mLocalCapabilities;
@@ -68,7 +58,7 @@ public class CapabilityManager {
/** Maintains a list of listeners to notify when host capabilities are received. */
private List<CapabilitiesChangedListener> mCapabilitiesChangedListeners;
- private CapabilityManager() {
+ public CapabilityManager() {
mLocalCapabilities = new ArrayList<String>();
mClientExtensions = new ArrayList<ClientExtension>();
@@ -79,18 +69,6 @@ public class CapabilityManager {
}
/**
- * Returns the singleton object. Thread-safe.
- */
- public static CapabilityManager getInstance() {
- synchronized (sInstanceLock) {
- if (sInstance == null) {
- sInstance = new CapabilityManager();
- }
- return sInstance;
- }
- }
-
- /**
* Cleans up host specific state when the connection has been terminated.
*/
public void onHostDisconnect() {
diff --git a/remoting/android/java/src/org/chromium/chromoting/Chromoting.java b/remoting/android/java/src/org/chromium/chromoting/Chromoting.java
index a10a17f..f53926c 100644
--- a/remoting/android/java/src/org/chromium/chromoting/Chromoting.java
+++ b/remoting/android/java/src/org/chromium/chromoting/Chromoting.java
@@ -36,6 +36,7 @@ import org.chromium.chromoting.accountswitcher.AccountSwitcher;
import org.chromium.chromoting.accountswitcher.AccountSwitcherFactory;
import org.chromium.chromoting.help.HelpContext;
import org.chromium.chromoting.help.HelpSingleton;
+import org.chromium.chromoting.jni.Client;
import org.chromium.chromoting.jni.ConnectionListener;
import org.chromium.chromoting.jni.JniInterface;
@@ -117,6 +118,9 @@ public class Chromoting extends AppCompatActivity implements ConnectionListener,
private AccountSwitcher mAccountSwitcher;
+ /** The currently-connected Client, if any. */
+ private Client mClient;
+
/** Shows a warning explaining that a Google account is required, then closes the activity. */
private void showNoAccountsDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
@@ -349,8 +353,14 @@ public class Chromoting extends AppCompatActivity implements ConnectionListener,
@Override
public void onDestroy() {
super.onDestroy();
- JniInterface.disconnectFromHost();
mAccountSwitcher.destroy();
+
+ // TODO(lambroslambrou): Determine whether we really need to tear down the connection here,
+ // so we can remove this code.
+ if (mClient != null) {
+ mClient.destroy();
+ mClient = null;
+ }
}
/** Called when a child Activity exits and sends a result back to this Activity. */
@@ -444,6 +454,11 @@ public class Chromoting extends AppCompatActivity implements ConnectionListener,
}
private void connectToHost(HostInfo host) {
+ if (mClient != null) {
+ mClient.destroy();
+ }
+
+ mClient = new Client();
mProgressIndicator = ProgressDialog.show(
this,
host.name,
@@ -453,11 +468,15 @@ public class Chromoting extends AppCompatActivity implements ConnectionListener,
new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
- JniInterface.disconnectFromHost();
+ if (mClient != null) {
+ mClient.destroy();
+ mClient = null;
+ }
}
});
- SessionConnector connector = new SessionConnector(this, this, mHostListLoader);
- mAuthenticator = new SessionAuthenticator(this, host);
+
+ SessionConnector connector = new SessionConnector(mClient, this, this, mHostListLoader);
+ mAuthenticator = new SessionAuthenticator(this, mClient, host);
connector.connectToHost(mAccount, mToken, host, mAuthenticator,
getPreferences(MODE_PRIVATE).getString(PREFERENCE_EXPERIMENTAL_FLAGS, ""));
}
diff --git a/remoting/android/java/src/org/chromium/chromoting/Desktop.java b/remoting/android/java/src/org/chromium/chromoting/Desktop.java
index 93e54b1..f9f3c73 100644
--- a/remoting/android/java/src/org/chromium/chromoting/Desktop.java
+++ b/remoting/android/java/src/org/chromium/chromoting/Desktop.java
@@ -31,7 +31,7 @@ import android.view.inputmethod.InputMethodManager;
import org.chromium.chromoting.cardboard.DesktopActivity;
import org.chromium.chromoting.help.HelpContext;
import org.chromium.chromoting.help.HelpSingleton;
-import org.chromium.chromoting.jni.JniInterface;
+import org.chromium.chromoting.jni.Client;
import java.util.List;
import java.util.Set;
@@ -69,6 +69,8 @@ public class Desktop
/** The surface that displays the remote host's desktop feed. */
private DesktopView mRemoteHostDesktop;
+ private Client mClient;
+
/** Set of pressed keys for which we've sent TextEvent. */
private Set<Integer> mPressedTextKeys = new TreeSet<Integer>();
@@ -102,11 +104,14 @@ public class Desktop
super.onCreate(savedInstanceState);
setContentView(R.layout.desktop);
+ mClient = Client.getInstance();
+
mToolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(mToolbar);
mRemoteHostDesktop = (DesktopView) findViewById(R.id.desktop_view);
mRemoteHostDesktop.setDesktop(this);
+ mRemoteHostDesktop.setClient(mClient);
mSwitchToCardboardDesktopActivity = false;
getSupportActionBar().setDisplayShowTitleEnabled(false);
@@ -124,7 +129,7 @@ public class Desktop
View decorView = getWindow().getDecorView();
decorView.setOnSystemUiVisibilityChangeListener(this);
- mActivityLifecycleListener = CapabilityManager.getInstance().onActivityAcceptingListener(
+ mActivityLifecycleListener = mClient.getCapabilityManager().onActivityAcceptingListener(
this, Capabilities.CAST_CAPABILITY);
mActivityLifecycleListener.onActivityCreated(this, savedInstanceState);
@@ -163,9 +168,9 @@ public class Desktop
protected void onStart() {
super.onStart();
mActivityLifecycleListener.onActivityStarted(this);
- JniInterface.enableVideoChannel(true);
+ mClient.enableVideoChannel(true);
mRemoteHostDesktop.attachRedrawCallback();
- CapabilityManager.getInstance().addListener(this);
+ mClient.getCapabilityManager().addListener(this);
}
@Override
@@ -173,7 +178,7 @@ public class Desktop
if (isFinishing()) mActivityLifecycleListener.onActivityPaused(this);
super.onPause();
if (!mSwitchToCardboardDesktopActivity) {
- JniInterface.enableVideoChannel(false);
+ mClient.enableVideoChannel(false);
}
stopActionBarAutoHideTimer();
}
@@ -182,19 +187,19 @@ public class Desktop
public void onResume() {
super.onResume();
mActivityLifecycleListener.onActivityResumed(this);
- JniInterface.enableVideoChannel(true);
+ mClient.enableVideoChannel(true);
startActionBarAutoHideTimer();
}
@Override
protected void onStop() {
- CapabilityManager.getInstance().removeListener(this);
+ mClient.getCapabilityManager().removeListener(this);
mActivityLifecycleListener.onActivityStopped(this);
super.onStop();
if (mSwitchToCardboardDesktopActivity) {
mSwitchToCardboardDesktopActivity = false;
} else {
- JniInterface.enableVideoChannel(false);
+ mClient.enableVideoChannel(false);
}
}
@@ -489,7 +494,7 @@ public class Desktop
return true;
}
if (id == R.id.actionbar_disconnect || id == android.R.id.home) {
- JniInterface.disconnectFromHost();
+ mClient.destroy();
return true;
}
if (id == R.id.actionbar_send_ctrl_alt_del) {
@@ -499,10 +504,10 @@ public class Desktop
KeyEvent.KEYCODE_FORWARD_DEL,
};
for (int key : keys) {
- JniInterface.sendKeyEvent(0, key, true);
+ mClient.sendKeyEvent(0, key, true);
}
for (int key : keys) {
- JniInterface.sendKeyEvent(0, key, false);
+ mClient.sendKeyEvent(0, key, false);
}
return true;
}
@@ -609,7 +614,7 @@ public class Desktop
// Dispatch the back button to the system to handle navigation
if (keyCode == KeyEvent.KEYCODE_BACK) {
- JniInterface.disconnectFromHost();
+ mClient.destroy();
return super.dispatchKeyEvent(event);
}
@@ -621,7 +626,7 @@ public class Desktop
// the keyboard layout selected on the client doesn't affect the key
// codes sent to the host.
if (event.getDeviceId() != KeyCharacterMap.VIRTUAL_KEYBOARD) {
- return JniInterface.sendKeyEvent(event.getScanCode(), 0, pressed);
+ return mClient.sendKeyEvent(event.getScanCode(), 0, pressed);
}
// Events received from software keyboards generate TextEvent in two
@@ -632,7 +637,7 @@ public class Desktop
// correspond to what user sees on the screen, while physical keyboard
// acts as if it is connected to the remote host.
if (event.getAction() == KeyEvent.ACTION_MULTIPLE) {
- JniInterface.sendTextEvent(event.getCharacters());
+ mClient.sendTextEvent(event.getCharacters());
return true;
}
@@ -646,7 +651,7 @@ public class Desktop
if (pressed && unicode != 0 && no_modifiers) {
mPressedTextKeys.add(keyCode);
int[] codePoints = { unicode };
- JniInterface.sendTextEvent(new String(codePoints, 0, 1));
+ mClient.sendTextEvent(new String(codePoints, 0, 1));
return true;
}
@@ -661,28 +666,28 @@ public class Desktop
// third-party keyboards that may still generate these events. See
// https://source.android.com/devices/input/keyboard-devices.html#legacy-unsupported-keys
case KeyEvent.KEYCODE_AT:
- JniInterface.sendKeyEvent(0, KeyEvent.KEYCODE_SHIFT_LEFT, pressed);
- JniInterface.sendKeyEvent(0, KeyEvent.KEYCODE_2, pressed);
+ mClient.sendKeyEvent(0, KeyEvent.KEYCODE_SHIFT_LEFT, pressed);
+ mClient.sendKeyEvent(0, KeyEvent.KEYCODE_2, pressed);
return true;
case KeyEvent.KEYCODE_POUND:
- JniInterface.sendKeyEvent(0, KeyEvent.KEYCODE_SHIFT_LEFT, pressed);
- JniInterface.sendKeyEvent(0, KeyEvent.KEYCODE_3, pressed);
+ mClient.sendKeyEvent(0, KeyEvent.KEYCODE_SHIFT_LEFT, pressed);
+ mClient.sendKeyEvent(0, KeyEvent.KEYCODE_3, pressed);
return true;
case KeyEvent.KEYCODE_STAR:
- JniInterface.sendKeyEvent(0, KeyEvent.KEYCODE_SHIFT_LEFT, pressed);
- JniInterface.sendKeyEvent(0, KeyEvent.KEYCODE_8, pressed);
+ mClient.sendKeyEvent(0, KeyEvent.KEYCODE_SHIFT_LEFT, pressed);
+ mClient.sendKeyEvent(0, KeyEvent.KEYCODE_8, pressed);
return true;
case KeyEvent.KEYCODE_PLUS:
- JniInterface.sendKeyEvent(0, KeyEvent.KEYCODE_SHIFT_LEFT, pressed);
- JniInterface.sendKeyEvent(0, KeyEvent.KEYCODE_EQUALS, pressed);
+ mClient.sendKeyEvent(0, KeyEvent.KEYCODE_SHIFT_LEFT, pressed);
+ mClient.sendKeyEvent(0, KeyEvent.KEYCODE_EQUALS, pressed);
return true;
default:
// We try to send all other key codes to the host directly.
- return JniInterface.sendKeyEvent(0, keyCode, pressed);
+ return mClient.sendKeyEvent(0, keyCode, pressed);
}
}
}
diff --git a/remoting/android/java/src/org/chromium/chromoting/DesktopView.java b/remoting/android/java/src/org/chromium/chromoting/DesktopView.java
index 0c2330b..e79065d3 100644
--- a/remoting/android/java/src/org/chromium/chromoting/DesktopView.java
+++ b/remoting/android/java/src/org/chromium/chromoting/DesktopView.java
@@ -25,7 +25,7 @@ import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
import org.chromium.base.Log;
-import org.chromium.chromoting.jni.JniInterface;
+import org.chromium.chromoting.jni.Client;
/**
* The user interface for viewing and interacting with a specific remote host.
@@ -46,6 +46,10 @@ public class DesktopView extends SurfaceView implements DesktopViewInterface,
/** The parent Desktop activity. */
private Desktop mDesktop;
+ /** The Client connection, used to inject input and fetch the video frames. */
+ private Client mClient;
+
+
// Flag to prevent multiple repaint requests from being backed up. Requests for repainting will
// be dropped if this is already set to true. This is used by the main thread and the painting
// thread, so the access should be synchronized on |mRenderData|.
@@ -179,6 +183,10 @@ public class DesktopView extends SurfaceView implements DesktopViewInterface,
mDesktop = desktop;
}
+ public void setClient(Client client) {
+ mClient = client;
+ }
+
/** See {@link TouchInputHandler#onSoftInputMethodVisibilityChanged} for API details. */
public void onSoftInputMethodVisibilityChanged(boolean inputMethodVisible, Rect bounds) {
mInputHandler.onSoftInputMethodVisibilityChanged(inputMethodVisible, bounds);
@@ -192,7 +200,7 @@ public class DesktopView extends SurfaceView implements DesktopViewInterface,
}
mRepaintPending = true;
}
- JniInterface.redrawGraphics();
+ mClient.redrawGraphics();
}
/**
@@ -207,7 +215,7 @@ public class DesktopView extends SurfaceView implements DesktopViewInterface,
Log.w(TAG, "Canvas being redrawn on UI thread");
}
- Bitmap image = JniInterface.getVideoFrame();
+ Bitmap image = mClient.getVideoFrame();
if (image == null) {
// This can happen if the client is connected, but a complete video frame has not yet
// been decoded.
@@ -220,7 +228,7 @@ public class DesktopView extends SurfaceView implements DesktopViewInterface,
synchronized (mRenderData) {
if (mRenderData.imageWidth != width || mRenderData.imageHeight != height) {
// TODO(lambroslambrou): Move this code into a sizeChanged() callback, to be
- // triggered from JniInterface (on the display thread) when the remote screen size
+ // triggered from native code (on the display thread) when the remote screen size
// changes.
mRenderData.imageWidth = width;
mRenderData.imageHeight = height;
@@ -266,9 +274,9 @@ public class DesktopView extends SurfaceView implements DesktopViewInterface,
}
if (drawCursor) {
- Bitmap cursorBitmap = JniInterface.getCursorBitmap();
+ Bitmap cursorBitmap = mClient.getCursorBitmap();
if (cursorBitmap != null) {
- Point hotspot = JniInterface.getCursorHotspot();
+ Point hotspot = mClient.getCursorHotspot();
canvas.drawBitmap(cursorBitmap, cursorPosition.x - hotspot.x,
cursorPosition.y - hotspot.y, new Paint());
}
@@ -319,7 +327,7 @@ public class DesktopView extends SurfaceView implements DesktopViewInterface,
}
public void attachRedrawCallback() {
- JniInterface.provideRedrawCallback(new Runnable() {
+ mClient.provideRedrawCallback(new Runnable() {
@Override
public void run() {
paint();
@@ -415,15 +423,15 @@ public class DesktopView extends SurfaceView implements DesktopViewInterface,
switch (inputMode) {
case TRACKPAD:
- mInputHandler.setInputStrategy(new TrackpadInputStrategy(mRenderData));
+ mInputHandler.setInputStrategy(new TrackpadInputStrategy(mRenderData, mClient));
break;
case TOUCH:
if (hostTouchCapability.isSupported()) {
- mInputHandler.setInputStrategy(new TouchInputStrategy(mRenderData));
+ mInputHandler.setInputStrategy(new TouchInputStrategy(mRenderData, mClient));
} else {
mInputHandler.setInputStrategy(
- new SimulatedTouchInputStrategy(mRenderData, getContext()));
+ new SimulatedTouchInputStrategy(mRenderData, mClient, getContext()));
}
break;
diff --git a/remoting/android/java/src/org/chromium/chromoting/SessionAuthenticator.java b/remoting/android/java/src/org/chromium/chromoting/SessionAuthenticator.java
index a18c2f4..ce50a04 100644
--- a/remoting/android/java/src/org/chromium/chromoting/SessionAuthenticator.java
+++ b/remoting/android/java/src/org/chromium/chromoting/SessionAuthenticator.java
@@ -16,7 +16,7 @@ import android.widget.CheckBox;
import android.widget.TextView;
import android.widget.Toast;
-import org.chromium.chromoting.jni.JniInterface;
+import org.chromium.chromoting.jni.Client;
/**
* This class performs the user-interaction needed to authenticate the session connection. This
@@ -29,14 +29,18 @@ public class SessionAuthenticator {
*/
private Chromoting mApplicationContext;
+ /** Client connection being authenticated. */
+ private final Client mClient;
+
/** Provides the tokenUrlPatterns for this host during fetchThirdPartyTokens(). */
private HostInfo mHost;
/** Object for fetching OAuth2 access tokens from third party authorization servers. */
private ThirdPartyTokenFetcher mTokenFetcher;
- public SessionAuthenticator(Chromoting context, HostInfo host) {
+ public SessionAuthenticator(Chromoting context, Client client, HostInfo host) {
mApplicationContext = context;
+ mClient = client;
mHost = host;
}
@@ -62,8 +66,8 @@ public class SessionAuthenticator {
R.string.connect_button, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
- if (JniInterface.isConnected()) {
- JniInterface.handleAuthenticationResponse(
+ if (mClient.isConnected()) {
+ mClient.handleAuthenticationResponse(
String.valueOf(pinTextView.getText()),
pinCheckBox.isChecked(), Build.MODEL);
} else {
@@ -78,7 +82,7 @@ public class SessionAuthenticator {
R.string.cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
- JniInterface.disconnectFromHost();
+ mClient.destroy();
}
});
@@ -138,7 +142,7 @@ public class SessionAuthenticator {
// authenticate itself with the host using spake.
String sharedSecret = accessToken;
- JniInterface.onThirdPartyTokenFetched(token, sharedSecret);
+ mClient.onThirdPartyTokenFetched(token, sharedSecret);
}
};
mTokenFetcher = new ThirdPartyTokenFetcher(mApplicationContext, mHost.getTokenUrlPatterns(),
diff --git a/remoting/android/java/src/org/chromium/chromoting/SessionConnector.java b/remoting/android/java/src/org/chromium/chromoting/SessionConnector.java
index 4fa9522..38e4634 100644
--- a/remoting/android/java/src/org/chromium/chromoting/SessionConnector.java
+++ b/remoting/android/java/src/org/chromium/chromoting/SessionConnector.java
@@ -4,15 +4,15 @@
package org.chromium.chromoting;
+import org.chromium.chromoting.jni.Client;
import org.chromium.chromoting.jni.ConnectionListener;
-import org.chromium.chromoting.jni.JniInterface;
/**
* This class manages making a connection to a host, with logic for reloading the host list and
* retrying the connection in the case of a stale host JID.
*/
-public class SessionConnector implements ConnectionListener,
- HostListLoader.Callback {
+public class SessionConnector implements ConnectionListener, HostListLoader.Callback {
+ private Client mClient;
private ConnectionListener mConnectionListener;
private HostListLoader.Callback mHostListCallback;
private HostListLoader mHostListLoader;
@@ -38,8 +38,9 @@ public class SessionConnector implements ConnectionListener,
* @param hostListCallback Object to be notified whenever the host list is reloaded.
* @param hostListLoader The object used for reloading the host list.
*/
- public SessionConnector(ConnectionListener connectionListener,
+ public SessionConnector(Client client, ConnectionListener connectionListener,
HostListLoader.Callback hostListCallback, HostListLoader hostListLoader) {
+ mClient = client;
mConnectionListener = connectionListener;
mHostListCallback = hostListCallback;
mHostListLoader = hostListLoader;
@@ -65,7 +66,7 @@ public class SessionConnector implements ConnectionListener,
}
private void doConnect() {
- JniInterface.connectToHost(mAccountName, mAuthToken, mHost.jabberId, mHost.id,
+ mClient.connectToHost(mAccountName, mAuthToken, mHost.jabberId, mHost.id,
mHost.publicKey, mAuthenticator, mFlags, this);
}
diff --git a/remoting/android/java/src/org/chromium/chromoting/SimulatedTouchInputStrategy.java b/remoting/android/java/src/org/chromium/chromoting/SimulatedTouchInputStrategy.java
index 2c3ded2..ccc0dbd 100644
--- a/remoting/android/java/src/org/chromium/chromoting/SimulatedTouchInputStrategy.java
+++ b/remoting/android/java/src/org/chromium/chromoting/SimulatedTouchInputStrategy.java
@@ -10,7 +10,7 @@ import android.os.SystemClock;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
-import org.chromium.chromoting.jni.JniInterface;
+import org.chromium.chromoting.jni.Client;
/**
* This class receives local touch events and translates them into the appropriate mouse based
@@ -22,6 +22,7 @@ public class SimulatedTouchInputStrategy implements InputStrategyInterface {
private static final float DOUBLE_TAP_SLOP_SCALE_FACTOR = 0.25f;
private final RenderData mRenderData;
+ private final Client mClient;
/**
* Stores the time of the most recent left button single tap processed.
@@ -48,8 +49,9 @@ public class SimulatedTouchInputStrategy implements InputStrategyInterface {
/** Mouse-button currently held down, or BUTTON_UNDEFINED otherwise. */
private int mHeldButton = TouchInputHandlerInterface.BUTTON_UNDEFINED;
- public SimulatedTouchInputStrategy(RenderData renderData, Context context) {
+ public SimulatedTouchInputStrategy(RenderData renderData, Client client, Context context) {
mRenderData = renderData;
+ mClient = client;
ViewConfiguration config = ViewConfiguration.get(context);
mDoubleTapDurationInMs = config.getDoubleTapTimeout();
@@ -121,7 +123,7 @@ public class SimulatedTouchInputStrategy implements InputStrategyInterface {
@Override
public void onScroll(float distanceX, float distanceY) {
- JniInterface.sendMouseWheelEvent((int) -distanceX, (int) -distanceY);
+ mClient.sendMouseWheelEvent((int) -distanceX, (int) -distanceY);
}
@Override
@@ -135,7 +137,7 @@ public class SimulatedTouchInputStrategy implements InputStrategyInterface {
@Override
public void injectCursorMoveEvent(int x, int y) {
- JniInterface.sendMouseEvent(x, y, TouchInputHandlerInterface.BUTTON_UNDEFINED, false);
+ mClient.sendMouseEvent(x, y, TouchInputHandlerInterface.BUTTON_UNDEFINED, false);
}
@Override
@@ -180,6 +182,6 @@ public class SimulatedTouchInputStrategy implements InputStrategyInterface {
}
private void injectMouseButtonEvent(int button, boolean pressed, Point tapPoint) {
- JniInterface.sendMouseEvent(tapPoint.x, tapPoint.y, button, pressed);
+ mClient.sendMouseEvent(tapPoint.x, tapPoint.y, button, pressed);
}
}
diff --git a/remoting/android/java/src/org/chromium/chromoting/TouchInputStrategy.java b/remoting/android/java/src/org/chromium/chromoting/TouchInputStrategy.java
index 488760b..19d70e3 100644
--- a/remoting/android/java/src/org/chromium/chromoting/TouchInputStrategy.java
+++ b/remoting/android/java/src/org/chromium/chromoting/TouchInputStrategy.java
@@ -8,7 +8,7 @@ import android.graphics.Matrix;
import android.view.MotionEvent;
import org.chromium.base.VisibleForTesting;
-import org.chromium.chromoting.jni.JniInterface;
+import org.chromium.chromoting.jni.Client;
import org.chromium.chromoting.jni.TouchEventData;
import java.util.ArrayList;
@@ -37,15 +37,15 @@ public class TouchInputStrategy implements InputStrategyInterface {
/**
* This class provides the default implementation for injecting remote events.
*/
- private static class DefaultInputInjector implements RemoteInputInjector {
+ private class DefaultInputInjector implements RemoteInputInjector {
@Override
public void injectMouseEvent(int x, int y, int button, boolean buttonDown) {
- JniInterface.sendMouseEvent(x, y, button, buttonDown);
+ mClient.sendMouseEvent(x, y, button, buttonDown);
}
@Override
public void injectTouchEvent(TouchEventData.EventType eventType, TouchEventData[] data) {
- JniInterface.sendTouchEvent(eventType, data);
+ mClient.sendTouchEvent(eventType, data);
}
}
@@ -77,10 +77,13 @@ public class TouchInputStrategy implements InputStrategyInterface {
private final RenderData mRenderData;
+ private final Client mClient;
+
private RemoteInputInjector mRemoteInputInjector;
- public TouchInputStrategy(RenderData renderData) {
+ public TouchInputStrategy(RenderData renderData, Client client) {
mRenderData = renderData;
+ mClient = client;
mRemoteInputInjector = new DefaultInputInjector();
mQueuedEvents = new LinkedList<MotionEvent>();
diff --git a/remoting/android/java/src/org/chromium/chromoting/TrackpadInputStrategy.java b/remoting/android/java/src/org/chromium/chromoting/TrackpadInputStrategy.java
index 326eff9..c9dcf4b 100644
--- a/remoting/android/java/src/org/chromium/chromoting/TrackpadInputStrategy.java
+++ b/remoting/android/java/src/org/chromium/chromoting/TrackpadInputStrategy.java
@@ -7,7 +7,7 @@ package org.chromium.chromoting;
import android.graphics.Point;
import android.view.MotionEvent;
-import org.chromium.chromoting.jni.JniInterface;
+import org.chromium.chromoting.jni.Client;
/**
* Defines a set of behavior and methods to simulate trackpad behavior when responding to
@@ -16,12 +16,14 @@ import org.chromium.chromoting.jni.JniInterface;
*/
public class TrackpadInputStrategy implements InputStrategyInterface {
private final RenderData mRenderData;
+ private final Client mClient;
/** Mouse-button currently held down, or BUTTON_UNDEFINED otherwise. */
private int mHeldButton = TouchInputHandlerInterface.BUTTON_UNDEFINED;
- public TrackpadInputStrategy(RenderData renderData) {
+ public TrackpadInputStrategy(RenderData renderData, Client client) {
mRenderData = renderData;
+ mClient = client;
synchronized (mRenderData) {
mRenderData.drawCursor = true;
@@ -44,7 +46,7 @@ public class TrackpadInputStrategy implements InputStrategyInterface {
@Override
public void onScroll(float distanceX, float distanceY) {
- JniInterface.sendMouseWheelEvent((int) -distanceX, (int) -distanceY);
+ mClient.sendMouseWheelEvent((int) -distanceX, (int) -distanceY);
}
@Override
@@ -58,7 +60,7 @@ public class TrackpadInputStrategy implements InputStrategyInterface {
@Override
public void injectCursorMoveEvent(int x, int y) {
- JniInterface.sendMouseEvent(x, y, TouchInputHandlerInterface.BUTTON_UNDEFINED, false);
+ mClient.sendMouseEvent(x, y, TouchInputHandlerInterface.BUTTON_UNDEFINED, false);
}
@Override
@@ -81,6 +83,6 @@ public class TrackpadInputStrategy implements InputStrategyInterface {
synchronized (mRenderData) {
cursorPosition = mRenderData.getCursorPosition();
}
- JniInterface.sendMouseEvent(cursorPosition.x, cursorPosition.y, button, pressed);
+ mClient.sendMouseEvent(cursorPosition.x, cursorPosition.y, button, pressed);
}
}
diff --git a/remoting/android/java/src/org/chromium/chromoting/cardboard/CardboardRenderer.java b/remoting/android/java/src/org/chromium/chromoting/cardboard/CardboardRenderer.java
index 843edd1..e8a05f0 100644
--- a/remoting/android/java/src/org/chromium/chromoting/cardboard/CardboardRenderer.java
+++ b/remoting/android/java/src/org/chromium/chromoting/cardboard/CardboardRenderer.java
@@ -15,7 +15,7 @@ import com.google.vrtoolkit.cardboard.Eye;
import com.google.vrtoolkit.cardboard.HeadTransform;
import com.google.vrtoolkit.cardboard.Viewport;
-import org.chromium.chromoting.jni.JniInterface;
+import org.chromium.chromoting.jni.Client;
import javax.microedition.khronos.egl.EGLConfig;
@@ -65,6 +65,7 @@ public class CardboardRenderer implements CardboardView.StereoRenderer {
private static final float EPSILON = 1e-5f;
private final Activity mActivity;
+ private final Client mClient;
private float mCameraPosition;
@@ -103,8 +104,9 @@ public class CardboardRenderer implements CardboardView.StereoRenderer {
// Flag to indicate whether to show menu bar.
private boolean mMenuBarVisible;
- public CardboardRenderer(Activity activity) {
+ public CardboardRenderer(Activity activity, Client client) {
mActivity = activity;
+ mClient = client;
mCameraPosition = 0.0f;
mCameraMatrix = new float[16];
@@ -122,7 +124,7 @@ public class CardboardRenderer implements CardboardView.StereoRenderer {
private void initializeRedrawCallback() {
mActivity.runOnUiThread(new Runnable() {
public void run() {
- JniInterface.provideRedrawCallback(new Runnable() {
+ mClient.provideRedrawCallback(new Runnable() {
@Override
public void run() {
mDesktop.reloadTexture();
@@ -130,7 +132,7 @@ public class CardboardRenderer implements CardboardView.StereoRenderer {
}
});
- JniInterface.redrawGraphics();
+ mClient.redrawGraphics();
}
});
}
@@ -146,10 +148,10 @@ public class CardboardRenderer implements CardboardView.StereoRenderer {
// Enable depth testing.
GLES20.glEnable(GLES20.GL_DEPTH_TEST);
- mDesktop = new Desktop();
+ mDesktop = new Desktop(mClient);
mMenuBar = new MenuBar(mActivity);
mPhotosphere = new Photosphere(mActivity);
- mCursor = new Cursor();
+ mCursor = new Cursor(mClient);
initializeRedrawCallback();
}
@@ -429,4 +431,4 @@ public class CardboardRenderer implements CardboardView.StereoRenderer {
return Math.abs(phi) > FARAWAY_ANGLE_RATIO * Math.abs(theta);
}
-} \ No newline at end of file
+}
diff --git a/remoting/android/java/src/org/chromium/chromoting/cardboard/Cursor.java b/remoting/android/java/src/org/chromium/chromoting/cardboard/Cursor.java
index 34403d6..534728a 100644
--- a/remoting/android/java/src/org/chromium/chromoting/cardboard/Cursor.java
+++ b/remoting/android/java/src/org/chromium/chromoting/cardboard/Cursor.java
@@ -13,7 +13,7 @@ import android.graphics.PointF;
import android.opengl.GLES20;
import org.chromium.chromoting.TouchInputHandler;
-import org.chromium.chromoting.jni.JniInterface;
+import org.chromium.chromoting.jni.Client;
import java.nio.FloatBuffer;
@@ -52,6 +52,8 @@ public class Cursor {
// Threshold to determine whether to send the mouse move event.
private static final float CURSOR_MOVE_THRESHOLD = 1.0f;
+ private final Client mClient;
+
private FloatBuffer mPositionCoordinates;
private int mVertexShaderHandle;
@@ -76,7 +78,8 @@ public class Cursor {
private PointF mCursorPosition;
- public Cursor() {
+ public Cursor(Client client) {
+ mClient = client;
mHalfFrameSize = new PointF(0.0f, 0.0f);
mCursorPosition = new PointF(0.0f, 0.0f);
@@ -120,7 +123,7 @@ public class Cursor {
*/
public void moveTo(PointF position) {
if (moveCursor(position)) {
- JniInterface.sendMouseEvent((int) position.x, (int) position.y,
+ mClient.sendMouseEvent((int) position.x, (int) position.y,
TouchInputHandler.BUTTON_UNDEFINED, false);
}
mCursorPosition = position;
@@ -137,7 +140,7 @@ public class Cursor {
}
}
- Bitmap cursorBitmap = JniInterface.getCursorBitmap();
+ Bitmap cursorBitmap = mClient.getCursorBitmap();
if (cursorBitmap == mCursorBitmap) {
// Case when cursor image has not changed.
@@ -148,7 +151,7 @@ public class Cursor {
}
mCursorBitmap = cursorBitmap;
- updatePosition(desktop, mCursorBitmap, JniInterface.getCursorHotspot());
+ updatePosition(desktop, mCursorBitmap, mClient.getCursorHotspot());
TextureHelper.linkTexture(mTextureDataHandle, cursorBitmap);
diff --git a/remoting/android/java/src/org/chromium/chromoting/cardboard/Desktop.java b/remoting/android/java/src/org/chromium/chromoting/cardboard/Desktop.java
index ded464b..d987f6b 100644
--- a/remoting/android/java/src/org/chromium/chromoting/cardboard/Desktop.java
+++ b/remoting/android/java/src/org/chromium/chromoting/cardboard/Desktop.java
@@ -11,7 +11,7 @@ import android.graphics.Bitmap;
import android.graphics.Point;
import android.opengl.GLES20;
-import org.chromium.chromoting.jni.JniInterface;
+import org.chromium.chromoting.jni.Client;
import java.nio.FloatBuffer;
@@ -61,6 +61,8 @@ public class Desktop {
// Number of vertices passed to glDrawArrays().
private static final int VERTICES_NUMBER = 6;
+ private final Client mClient;
+
private int mVertexShaderHandle;
private int mFragmentShaderHandle;
private int mProgramHandle;
@@ -87,7 +89,8 @@ public class Desktop {
// Lock to allow multithreaded access to mReloadTexture.
private final Object mReloadTextureLock = new Object();
- public Desktop() {
+ public Desktop(Client client) {
+ mClient = client;
mVertexShaderHandle =
ShaderHelper.compileShader(GLES20.GL_VERTEX_SHADER, VERTEX_SHADER);
mFragmentShaderHandle =
@@ -220,7 +223,7 @@ public class Desktop {
}
// TODO(shichengfeng): Record the time desktop drawing takes.
- Bitmap bitmap = JniInterface.getVideoFrame();
+ Bitmap bitmap = mClient.getVideoFrame();
if (bitmap == null) {
// This can happen if the client is connected, but a complete video frame has not yet
@@ -244,4 +247,4 @@ public class Desktop {
mReloadTexture = true;
}
}
-} \ No newline at end of file
+}
diff --git a/remoting/android/java/src/org/chromium/chromoting/cardboard/DesktopActivity.java b/remoting/android/java/src/org/chromium/chromoting/cardboard/DesktopActivity.java
index 1f37082..7800b4e 100644
--- a/remoting/android/java/src/org/chromium/chromoting/cardboard/DesktopActivity.java
+++ b/remoting/android/java/src/org/chromium/chromoting/cardboard/DesktopActivity.java
@@ -16,7 +16,7 @@ import com.google.vrtoolkit.cardboard.CardboardView;
import org.chromium.chromoting.R;
import org.chromium.chromoting.TouchInputHandler;
-import org.chromium.chromoting.jni.JniInterface;
+import org.chromium.chromoting.jni.Client;
import java.util.ArrayList;
@@ -28,6 +28,7 @@ public class DesktopActivity extends CardboardActivity {
// desktop activity.
private boolean mSwitchToDesktopActivity;
+ private Client mClient;
private CardboardRenderer mRenderer;
private SpeechRecognizer mSpeechRecognizer;
@@ -38,9 +39,12 @@ public class DesktopActivity extends CardboardActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.cardboard_desktop);
+
+ mClient = Client.getInstance();
+
mSwitchToDesktopActivity = false;
CardboardView cardboardView = (CardboardView) findViewById(R.id.cardboard_view);
- mRenderer = new CardboardRenderer(this);
+ mRenderer = new CardboardRenderer(this, mClient);
mIsListening = false;
// Associate a CardboardView.StereoRenderer with cardboard view.
@@ -75,9 +79,9 @@ public class DesktopActivity extends CardboardActivity {
} else {
if (mRenderer.isLookingAtDesktop()) {
PointF coordinates = mRenderer.getMouseCoordinates();
- JniInterface.sendMouseEvent((int) coordinates.x, (int) coordinates.y,
+ mClient.sendMouseEvent((int) coordinates.x, (int) coordinates.y,
TouchInputHandler.BUTTON_LEFT, true);
- JniInterface.sendMouseEvent((int) coordinates.x, (int) coordinates.y,
+ mClient.sendMouseEvent((int) coordinates.x, (int) coordinates.y,
TouchInputHandler.BUTTON_LEFT, false);
} else {
if (mRenderer.isLookingFarawayFromDesktop()) {
@@ -92,14 +96,14 @@ public class DesktopActivity extends CardboardActivity {
@Override
protected void onStart() {
super.onStart();
- JniInterface.enableVideoChannel(true);
+ mClient.enableVideoChannel(true);
}
@Override
protected void onPause() {
super.onPause();
if (!mSwitchToDesktopActivity) {
- JniInterface.enableVideoChannel(false);
+ mClient.enableVideoChannel(false);
}
if (mSpeechRecognizer != null) {
mSpeechRecognizer.stopListening();
@@ -109,7 +113,7 @@ public class DesktopActivity extends CardboardActivity {
@Override
protected void onResume() {
super.onResume();
- JniInterface.enableVideoChannel(true);
+ mClient.enableVideoChannel(true);
}
@Override
@@ -118,7 +122,7 @@ public class DesktopActivity extends CardboardActivity {
if (mSwitchToDesktopActivity) {
mSwitchToDesktopActivity = false;
} else {
- JniInterface.enableVideoChannel(false);
+ mClient.enableVideoChannel(false);
}
if (mSpeechRecognizer != null) {
mSpeechRecognizer.stopListening();
@@ -186,7 +190,7 @@ public class DesktopActivity extends CardboardActivity {
ArrayList<String> data =
results.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);
if (!data.isEmpty()) {
- JniInterface.sendTextEvent(data.get(0));
+ mClient.sendTextEvent(data.get(0));
}
}
diff --git a/remoting/android/java/src/org/chromium/chromoting/jni/Client.java b/remoting/android/java/src/org/chromium/chromoting/jni/Client.java
index 6c67a8c..451a5d4 100644
--- a/remoting/android/java/src/org/chromium/chromoting/jni/Client.java
+++ b/remoting/android/java/src/org/chromium/chromoting/jni/Client.java
@@ -4,7 +4,18 @@
package org.chromium.chromoting.jni;
+import android.graphics.Bitmap;
+import android.graphics.Point;
+import android.os.Looper;
+
+import org.chromium.base.Log;
import org.chromium.base.annotations.JNINamespace;
+import org.chromium.base.annotations.SuppressFBWarnings;
+import org.chromium.chromoting.CapabilityManager;
+import org.chromium.chromoting.SessionAuthenticator;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
/**
* Class to manage a client connection to the host. This class controls the lifetime of the
@@ -14,18 +25,379 @@ import org.chromium.base.annotations.JNINamespace;
*/
@JNINamespace("remoting")
public class Client {
+ private static final String TAG = "Chromoting";
+
// Pointer to the C++ object, cast to a |long|.
private long mNativeJniClient;
- public void init() {
+ // The global Client instance (may be null). This needs to be a global singleton so that the
+ // Client can be passed between Activities.
+ private static Client sClient;
+
+ // Called on the UI thread.
+ public Client() {
+ if (sClient != null) {
+ throw new RuntimeException("Client instance already created.");
+ }
+
+ sClient = this;
mNativeJniClient = nativeInit();
}
private native long nativeInit();
+ // Called on the UI thread. Suppress FindBugs warning, since |sClient| is only used on the
+ // UI thread.
+ @SuppressFBWarnings("LI_LAZY_INIT_STATIC")
public void destroy() {
- nativeDestroy(mNativeJniClient);
+ if (sClient != null) {
+ disconnectFromHost();
+ nativeDestroy(mNativeJniClient);
+ sClient = null;
+ }
}
private native void nativeDestroy(long nativeJniClient);
+
+ /** Returns the current Client instance, or null. */
+ public static Client getInstance() {
+ return sClient;
+ }
+
+ /** Used for authentication-related UX during connection. Accessed on the UI thread. */
+ private SessionAuthenticator mAuthenticator;
+
+ /** Whether the native code is attempting a connection. Accessed on the UI thread. */
+ private boolean mConnected;
+
+ /** Notified upon successful connection or disconnection. Accessed on the UI thread. */
+ private ConnectionListener mConnectionListener;
+
+ /**
+ * Callback invoked on the graphics thread to repaint the desktop. Accessed on the UI and
+ * graphics threads.
+ */
+ private Runnable mRedrawCallback;
+
+ /** Bitmap holding a copy of the latest video frame. Accessed on the UI and graphics threads. */
+ private Bitmap mFrameBitmap;
+
+ /** Protects access to {@link mFrameBitmap}. */
+ private final Object mFrameLock = new Object();
+
+ /** Position of cursor hot-spot. Accessed on the graphics thread. */
+ private Point mCursorHotspot = new Point();
+
+ /** Bitmap holding the cursor shape. Accessed on the graphics thread. */
+ private Bitmap mCursorBitmap;
+
+ /** Capability Manager through which capabilities and extensions are handled. */
+ private CapabilityManager mCapabilityManager = new CapabilityManager();
+
+ public CapabilityManager getCapabilityManager() {
+ return mCapabilityManager;
+ }
+
+ /** Returns whether the client is connected. */
+ public boolean isConnected() {
+ return mConnected;
+ }
+
+ /** Attempts to form a connection to the user-selected host. Called on the UI thread. */
+ public void connectToHost(String username, String authToken, String hostJid,
+ String hostId, String hostPubkey, SessionAuthenticator authenticator, String flags,
+ ConnectionListener listener) {
+ disconnectFromHost();
+
+ mConnectionListener = listener;
+ mAuthenticator = authenticator;
+ JniInterface.nativeConnect(username, authToken, hostJid, hostId, hostPubkey,
+ mAuthenticator.getPairingId(hostId), mAuthenticator.getPairingSecret(hostId),
+ mCapabilityManager.getLocalCapabilities(), flags);
+ mConnected = true;
+ }
+
+ /** Severs the connection and cleans up. Called on the UI thread. */
+ public void disconnectFromHost() {
+ if (!mConnected) {
+ return;
+ }
+
+ mConnectionListener.onConnectionState(
+ ConnectionListener.State.CLOSED, ConnectionListener.Error.OK);
+
+ disconnectFromHostWithoutNotification();
+ }
+
+ /** Same as disconnectFromHost() but without notifying the ConnectionListener. */
+ private void disconnectFromHostWithoutNotification() {
+ if (!mConnected) {
+ return;
+ }
+
+ JniInterface.nativeDisconnect();
+ mConnectionListener = null;
+ mConnected = false;
+ mCapabilityManager.onHostDisconnect();
+
+ // Drop the reference to free the Bitmap for GC.
+ synchronized (mFrameLock) {
+ mFrameBitmap = null;
+ }
+ }
+
+ /** Called by native code whenever the connection status changes. Called on the UI thread. */
+ void onConnectionState(int stateCode, int errorCode) {
+ ConnectionListener.State state = ConnectionListener.State.fromValue(stateCode);
+ ConnectionListener.Error error = ConnectionListener.Error.fromValue(errorCode);
+ mConnectionListener.onConnectionState(state, error);
+ if (state == ConnectionListener.State.FAILED || state == ConnectionListener.State.CLOSED) {
+ // Disconnect from the host here, otherwise the next time connectToHost() is called,
+ // it will try to disconnect, triggering an incorrect status notification.
+
+ // TODO(lambroslambrou): Connection state notifications for separate sessions should
+ // go to separate Client instances. Once this is true, we can remove this line and
+ // simplify the disconnectFromHost() code.
+ disconnectFromHostWithoutNotification();
+ }
+ }
+
+ /**
+ * Called by JniInterface (from native code) to prompt the user to enter a PIN. Called on the
+ * UI thread.
+ */
+ void displayAuthenticationPrompt(boolean pairingSupported) {
+ mAuthenticator.displayAuthenticationPrompt(pairingSupported);
+ }
+
+ /**
+ * Called by the SessionAuthenticator after the user enters a PIN.
+ * @param pin The entered PIN.
+ * @param createPair Whether to create a new pairing for this client.
+ * @param deviceName The device name to appear in the pairing registry. Only used if createPair
+ * is true.
+ */
+ public void handleAuthenticationResponse(
+ String pin, boolean createPair, String deviceName) {
+ assert mConnected;
+ JniInterface.nativeAuthenticationResponse(pin, createPair, deviceName);
+ }
+
+ /**
+ * Called by JniInterface (from native code), to save newly-received pairing credentials to
+ * permanent storage. Called on the UI thread.
+ */
+ void commitPairingCredentials(String host, String id, String secret) {
+ mAuthenticator.commitPairingCredentials(host, id, secret);
+ }
+
+ /**
+ * Moves the mouse cursor, possibly while clicking the specified (nonnegative) button. Called
+ * on the UI thread.
+ */
+ public void sendMouseEvent(int x, int y, int whichButton, boolean buttonDown) {
+ if (!mConnected) {
+ return;
+ }
+
+ JniInterface.nativeSendMouseEvent(x, y, whichButton, buttonDown);
+ }
+
+ /** Injects a mouse-wheel event with delta values. Called on the UI thread. */
+ public void sendMouseWheelEvent(int deltaX, int deltaY) {
+ if (!mConnected) {
+ return;
+ }
+
+ JniInterface.nativeSendMouseWheelEvent(deltaX, deltaY);
+ }
+
+ /**
+ * Presses or releases the specified key. Called on the UI thread. If scanCode is not zero then
+ * keyCode is ignored.
+ */
+ public boolean sendKeyEvent(int scanCode, int keyCode, boolean keyDown) {
+ if (!mConnected) {
+ return false;
+ }
+
+ return JniInterface.nativeSendKeyEvent(scanCode, keyCode, keyDown);
+ }
+
+ /** Sends TextEvent to the host. Called on the UI thread. */
+ public void sendTextEvent(String text) {
+ if (!mConnected) {
+ return;
+ }
+
+ JniInterface.nativeSendTextEvent(text);
+ }
+
+ /** Sends an array of TouchEvents to the host. Called on the UI thread. */
+ public void sendTouchEvent(TouchEventData.EventType eventType, TouchEventData[] data) {
+ if (!mConnected) {
+ return;
+ }
+
+ JniInterface.nativeSendTouchEvent(eventType.value(), data);
+ }
+
+ /**
+ * Enables or disables the video channel. Called on the UI thread in response to Activity
+ * lifecycle events.
+ */
+ public void enableVideoChannel(boolean enable) {
+ if (!mConnected) {
+ return;
+ }
+
+ JniInterface.nativeEnableVideoChannel(enable);
+ }
+
+ /**
+ * Sets the redraw callback to the provided functor. Provide a value of null whenever the
+ * window is no longer visible so that we don't continue to draw onto it. Called on the UI
+ * thread.
+ */
+ public void provideRedrawCallback(Runnable redrawCallback) {
+ mRedrawCallback = redrawCallback;
+ }
+
+ /** Forces the native graphics thread to redraw to the canvas. Called on the UI thread. */
+ public boolean redrawGraphics() {
+ if (!mConnected || mRedrawCallback == null) return false;
+
+ JniInterface.nativeScheduleRedraw();
+ return true;
+ }
+
+ /**
+ * Called by JniInterface to perform the redrawing callback requested by
+ * {@link #redrawGraphics}. This is a no-op if the window isn't visible (the callback is null).
+ * Called on the graphics thread.
+ */
+ void redrawGraphicsInternal() {
+ Runnable callback = mRedrawCallback;
+ if (callback != null) {
+ callback.run();
+ }
+ }
+
+ /**
+ * Returns a bitmap of the latest video frame. Called on the native graphics thread when
+ * DesktopView is repainted.
+ */
+ public Bitmap getVideoFrame() {
+ if (Looper.myLooper() == Looper.getMainLooper()) {
+ Log.w(TAG, "Canvas being redrawn on UI thread");
+ }
+
+ synchronized (mFrameLock) {
+ return mFrameBitmap;
+ }
+ }
+
+ /**
+ * Called by JniInterface (from native code) to set a new video frame. Called on the native
+ * graphics thread when a new frame is allocated.
+ */
+ void setVideoFrame(Bitmap bitmap) {
+ if (Looper.myLooper() == Looper.getMainLooper()) {
+ Log.w(TAG, "Video frame updated on UI thread");
+ }
+
+ synchronized (mFrameLock) {
+ mFrameBitmap = bitmap;
+ }
+ }
+
+ /**
+ * Creates a new Bitmap to hold video frame pixels. Called by JniInterface (from native code),
+ * and the returned Bitmap is referenced by native code which writes the decoded frame pixels
+ * to it.
+ */
+ static Bitmap newBitmap(int width, int height) {
+ return Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ }
+
+ /**
+ * Called by JniInterface (from native code) to update the cursor shape. This is called on the
+ * graphics thread when receiving a new cursor shape from the host.
+ */
+ void updateCursorShape(
+ int width, int height, int hotspotX, int hotspotY, ByteBuffer buffer) {
+ mCursorHotspot = new Point(hotspotX, hotspotY);
+
+ int[] data = new int[width * height];
+ buffer.order(ByteOrder.LITTLE_ENDIAN);
+ buffer.asIntBuffer().get(data, 0, data.length);
+ mCursorBitmap = Bitmap.createBitmap(data, width, height, Bitmap.Config.ARGB_8888);
+ }
+
+ /** Position of cursor hotspot within cursor image. Called on the graphics thread. */
+ public Point getCursorHotspot() {
+ return mCursorHotspot;
+ }
+
+ /** Returns the current cursor shape. Called on the graphics thread. */
+ public Bitmap getCursorBitmap() {
+ return mCursorBitmap;
+ }
+
+ //
+ // Third Party Authentication
+ //
+
+ /**
+ * Called by JniInterface (from native code), to pop up a third party login page to fetch the
+ * token required for authentication.
+ */
+ void fetchThirdPartyToken(String tokenUrl, String clientId, String scope) {
+ mAuthenticator.fetchThirdPartyToken(tokenUrl, clientId, scope);
+ }
+
+ /**
+ * Called by the SessionAuthenticator to pass the |token| and |sharedSecret| to native code to
+ * continue authentication.
+ */
+ public void onThirdPartyTokenFetched(String token, String sharedSecret) {
+ if (!mConnected) {
+ return;
+ }
+
+ JniInterface.nativeOnThirdPartyTokenFetched(token, sharedSecret);
+ }
+
+ //
+ // Host and Client Capabilities
+ //
+
+ /**
+ * Called by JniInterface (from native code) to set the list of negotiated capabilities between
+ * host and client. Called on the UI thread.
+ */
+ void setCapabilities(String capabilities) {
+ mCapabilityManager.setNegotiatedCapabilities(capabilities);
+ }
+
+ //
+ // Extension Message Handling
+ //
+
+ /**
+ * Called by JniInterface (from native code), to pass on the deconstructed ExtensionMessage to
+ * the app. Called on the UI thread.
+ */
+ void handleExtensionMessage(String type, String data) {
+ mCapabilityManager.onExtensionMessage(type, data);
+ }
+
+ /** Sends an extension message to the Chromoting host. Called on the UI thread. */
+ public void sendExtensionMessage(String type, String data) {
+ if (!mConnected) {
+ return;
+ }
+
+ JniInterface.nativeSendExtensionMessage(type, data);
+ }
}
diff --git a/remoting/android/java/src/org/chromium/chromoting/jni/JniInterface.java b/remoting/android/java/src/org/chromium/chromoting/jni/JniInterface.java
index 0238702..92a1447 100644
--- a/remoting/android/java/src/org/chromium/chromoting/jni/JniInterface.java
+++ b/remoting/android/java/src/org/chromium/chromoting/jni/JniInterface.java
@@ -6,18 +6,12 @@ package org.chromium.chromoting.jni;
import android.content.Context;
import android.graphics.Bitmap;
-import android.graphics.Point;
-import android.os.Looper;
import org.chromium.base.ContextUtils;
-import org.chromium.base.Log;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.JNINamespace;
-import org.chromium.chromoting.CapabilityManager;
-import org.chromium.chromoting.SessionAuthenticator;
import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
/**
* Initializes the Chromium remoting library, and provides JNI calls into it.
@@ -25,47 +19,12 @@ import java.nio.ByteOrder;
*/
@JNINamespace("remoting")
public class JniInterface {
- private static final String TAG = "Chromoting";
-
/*
* Library-loading state machine.
*/
/** Whether the library has been loaded. Accessed on the UI thread. */
private static boolean sLoaded = false;
- /** Used for authentication-related UX during connection. Accessed on the UI thread. */
- private static SessionAuthenticator sAuthenticator;
-
- /*
- * Connection-initiating state machine.
- */
- /** Whether the native code is attempting a connection. Accessed on the UI thread. */
- private static boolean sConnected = false;
-
- /** Notified upon successful connection or disconnection. Accessed on the UI thread. */
- private static ConnectionListener sConnectionListener = null;
-
- /**
- * Callback invoked on the graphics thread to repaint the desktop. Accessed on the UI and
- * graphics threads.
- */
- private static Runnable sRedrawCallback = null;
-
- /** Bitmap holding a copy of the latest video frame. Accessed on the UI and graphics threads. */
- private static Bitmap sFrameBitmap = null;
-
- /** Protects access to sFrameBitmap. */
- private static final Object sFrameLock = new Object();
-
- /** Position of cursor hot-spot. Accessed on the graphics thread. */
- private static Point sCursorHotspot = new Point();
-
- /** Bitmap holding the cursor shape. Accessed on the graphics thread. */
- private static Bitmap sCursorBitmap = null;
-
- /** Capability Manager through which capabilities and extensions are handled. */
- private static CapabilityManager sCapabilityManager = CapabilityManager.getInstance();
-
/**
* To be called once from the main Activity. Loads and initializes the native code.
* Called on the UI thread.
@@ -90,203 +49,65 @@ public class JniInterface {
public static native String nativeGetClientId();
public static native String nativeGetClientSecret();
- /** Returns whether the client is connected. */
- public static boolean isConnected() {
- return sConnected;
- }
-
- /** Attempts to form a connection to the user-selected host. Called on the UI thread. */
- public static void connectToHost(String username, String authToken, String hostJid,
- String hostId, String hostPubkey, SessionAuthenticator authenticator, String flags,
- ConnectionListener listener) {
- disconnectFromHost();
-
- sConnectionListener = listener;
- sAuthenticator = authenticator;
- nativeConnect(username, authToken, hostJid, hostId, hostPubkey,
- sAuthenticator.getPairingId(hostId), sAuthenticator.getPairingSecret(hostId),
- sCapabilityManager.getLocalCapabilities(), flags);
- sConnected = true;
- }
-
/** Performs the native portion of the connection. */
- private static native void nativeConnect(String username, String authToken, String hostJid,
+ static native void nativeConnect(String username, String authToken, String hostJid,
String hostId, String hostPubkey, String pairId, String pairSecret,
String capabilities, String flags);
- /** Severs the connection and cleans up. Called on the UI thread. */
- public static void disconnectFromHost() {
- if (!sConnected) {
- return;
- }
-
- sConnectionListener.onConnectionState(
- ConnectionListener.State.CLOSED, ConnectionListener.Error.OK);
-
- disconnectFromHostWithoutNotification();
- }
-
- /** Same as disconnectFromHost() but without notifying the ConnectionListener. */
- private static void disconnectFromHostWithoutNotification() {
- if (!sConnected) {
- return;
- }
-
- nativeDisconnect();
- sConnectionListener = null;
- sConnected = false;
- sCapabilityManager.onHostDisconnect();
-
- // Drop the reference to free the Bitmap for GC.
- synchronized (sFrameLock) {
- sFrameBitmap = null;
- }
- }
-
/** Performs the native portion of the cleanup. */
- private static native void nativeDisconnect();
+ static native void nativeDisconnect();
/** Called by native code whenever the connection status changes. Called on the UI thread. */
@CalledByNative
private static void onConnectionState(int stateCode, int errorCode) {
- ConnectionListener.State state = ConnectionListener.State.fromValue(stateCode);
- ConnectionListener.Error error = ConnectionListener.Error.fromValue(errorCode);
- sConnectionListener.onConnectionState(state, error);
- if (state == ConnectionListener.State.FAILED || state == ConnectionListener.State.CLOSED) {
- // Disconnect from the host here, otherwise the next time connectToHost() is called,
- // it will try to disconnect, triggering an incorrect status notification.
- disconnectFromHostWithoutNotification();
+ if (Client.getInstance() != null) {
+ Client.getInstance().onConnectionState(stateCode, errorCode);
}
}
/** Prompts the user to enter a PIN. Called on the UI thread. */
@CalledByNative
private static void displayAuthenticationPrompt(boolean pairingSupported) {
- sAuthenticator.displayAuthenticationPrompt(pairingSupported);
- }
-
- /**
- * Performs the native response to the user's PIN.
- * @param pin The entered PIN.
- * @param createPair Whether to create a new pairing for this client.
- * @param deviceName The device name to appear in the pairing registry. Only used if createPair
- * is true.
- */
- public static void handleAuthenticationResponse(
- String pin, boolean createPair, String deviceName) {
- assert sConnected;
- nativeAuthenticationResponse(pin, createPair, deviceName);
+ if (Client.getInstance() != null) {
+ Client.getInstance().displayAuthenticationPrompt(pairingSupported);
+ }
}
- /** Native implementation of handleAuthenticationResponse(). */
- private static native void nativeAuthenticationResponse(
+ /** Native implementation of Client.handleAuthenticationResponse(). */
+ static native void nativeAuthenticationResponse(
String pin, boolean createPair, String deviceName);
/** Saves newly-received pairing credentials to permanent storage. Called on the UI thread. */
@CalledByNative
private static void commitPairingCredentials(String host, String id, String secret) {
- sAuthenticator.commitPairingCredentials(host, id, secret);
- }
-
- /**
- * Moves the mouse cursor, possibly while clicking the specified (nonnegative) button. Called
- * on the UI thread.
- */
- public static void sendMouseEvent(int x, int y, int whichButton, boolean buttonDown) {
- if (!sConnected) {
- return;
+ if (Client.getInstance() != null) {
+ Client.getInstance().commitPairingCredentials(host, id, secret);
}
-
- nativeSendMouseEvent(x, y, whichButton, buttonDown);
}
/** Passes mouse information to the native handling code. */
- private static native void nativeSendMouseEvent(
+ static native void nativeSendMouseEvent(
int x, int y, int whichButton, boolean buttonDown);
- /** Injects a mouse-wheel event with delta values. Called on the UI thread. */
- public static void sendMouseWheelEvent(int deltaX, int deltaY) {
- if (!sConnected) {
- return;
- }
-
- nativeSendMouseWheelEvent(deltaX, deltaY);
- }
-
/** Passes mouse-wheel information to the native handling code. */
- private static native void nativeSendMouseWheelEvent(int deltaX, int deltaY);
-
- /**
- * Presses or releases the specified (nonnegative) key. Called on the UI thread. If scanCode
- * is not zero then keyCode is ignored.
- */
- public static boolean sendKeyEvent(int scanCode, int keyCode, boolean keyDown) {
- if (!sConnected) {
- return false;
- }
-
- return nativeSendKeyEvent(scanCode, keyCode, keyDown);
- }
+ static native void nativeSendMouseWheelEvent(int deltaX, int deltaY);
/**
* Passes key press information to the native handling code.
*/
- private static native boolean nativeSendKeyEvent(int scanCode, int keyCode, boolean keyDown);
-
- /** Sends TextEvent to the host. Called on the UI thread. */
- public static void sendTextEvent(String text) {
- if (!sConnected) {
- return;
- }
-
- nativeSendTextEvent(text);
- }
+ static native boolean nativeSendKeyEvent(int scanCode, int keyCode, boolean keyDown);
/** Passes text event information to the native handling code. */
- private static native void nativeSendTextEvent(String text);
-
- /** Sends an array of TouchEvents to the host. Called on the UI thread. */
- public static void sendTouchEvent(TouchEventData.EventType eventType, TouchEventData[] data) {
- nativeSendTouchEvent(eventType.value(), data);
- }
+ static native void nativeSendTextEvent(String text);
/** Passes touch event information to the native handling code. */
- private static native void nativeSendTouchEvent(int eventType, TouchEventData[] data);
+ static native void nativeSendTouchEvent(int eventType, TouchEventData[] data);
- /**
- * Enables or disables the video channel. Called on the UI thread in response to Activity
- * lifecycle events.
- */
- public static void enableVideoChannel(boolean enable) {
- if (!sConnected) {
- return;
- }
-
- nativeEnableVideoChannel(enable);
- }
-
- /** Native implementation of enableVideoChannel() */
- private static native void nativeEnableVideoChannel(boolean enable);
-
- /**
- * Sets the redraw callback to the provided functor. Provide a value of null whenever the
- * window is no longer visible so that we don't continue to draw onto it. Called on the UI
- * thread.
- */
- public static void provideRedrawCallback(Runnable redrawCallback) {
- sRedrawCallback = redrawCallback;
- }
-
- /** Forces the native graphics thread to redraw to the canvas. Called on the UI thread. */
- public static boolean redrawGraphics() {
- if (!sConnected || sRedrawCallback == null) return false;
-
- nativeScheduleRedraw();
- return true;
- }
+ /** Native implementation of Client.enableVideoChannel() */
+ static native void nativeEnableVideoChannel(boolean enable);
/** Schedules a redraw on the native graphics thread. */
- private static native void nativeScheduleRedraw();
+ static native void nativeScheduleRedraw();
/**
* Performs the redrawing callback. This is a no-op if the window isn't visible. Called on the
@@ -294,23 +115,9 @@ public class JniInterface {
*/
@CalledByNative
private static void redrawGraphicsInternal() {
- Runnable callback = sRedrawCallback;
- if (callback != null) {
- callback.run();
- }
- }
-
- /**
- * Returns a bitmap of the latest video frame. Called on the native graphics thread when
- * DesktopView is repainted.
- */
- public static Bitmap getVideoFrame() {
- if (Looper.myLooper() == Looper.getMainLooper()) {
- Log.w(TAG, "Canvas being redrawn on UI thread");
- }
-
- synchronized (sFrameLock) {
- return sFrameBitmap;
+ Client client = Client.getInstance();
+ if (client != null) {
+ client.redrawGraphicsInternal();
}
}
@@ -319,12 +126,9 @@ public class JniInterface {
*/
@CalledByNative
private static void setVideoFrame(Bitmap bitmap) {
- if (Looper.myLooper() == Looper.getMainLooper()) {
- Log.w(TAG, "Video frame updated on UI thread");
- }
-
- synchronized (sFrameLock) {
- sFrameBitmap = bitmap;
+ Client client = Client.getInstance();
+ if (client != null) {
+ client.setVideoFrame(bitmap);
}
}
@@ -334,7 +138,7 @@ public class JniInterface {
*/
@CalledByNative
private static Bitmap newBitmap(int width, int height) {
- return Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ return Client.newBitmap(width, height);
}
/**
@@ -342,24 +146,12 @@ public class JniInterface {
* shape from the host.
*/
@CalledByNative
- public static void updateCursorShape(
+ private static void updateCursorShape(
int width, int height, int hotspotX, int hotspotY, ByteBuffer buffer) {
- sCursorHotspot = new Point(hotspotX, hotspotY);
-
- int[] data = new int[width * height];
- buffer.order(ByteOrder.LITTLE_ENDIAN);
- buffer.asIntBuffer().get(data, 0, data.length);
- sCursorBitmap = Bitmap.createBitmap(data, width, height, Bitmap.Config.ARGB_8888);
- }
-
- /** Position of cursor hotspot within cursor image. Called on the graphics thread. */
- public static Point getCursorHotspot() {
- return sCursorHotspot;
- }
-
- /** Returns the current cursor shape. Called on the graphics thread. */
- public static Bitmap getCursorBitmap() {
- return sCursorBitmap;
+ Client client = Client.getInstance();
+ if (client != null) {
+ client.updateCursorShape(width, height, hotspotX, hotspotY, buffer);
+ }
}
//
@@ -368,23 +160,14 @@ public class JniInterface {
/** Pops up a third party login page to fetch the token required for authentication. */
@CalledByNative
- public static void fetchThirdPartyToken(String tokenUrl, String clientId, String scope) {
- sAuthenticator.fetchThirdPartyToken(tokenUrl, clientId, scope);
- }
-
- /**
- * Notify the native code to continue authentication with the |token| and the |sharedSecret|.
- */
- public static void onThirdPartyTokenFetched(String token, String sharedSecret) {
- if (!sConnected) {
- return;
+ private static void fetchThirdPartyToken(String tokenUrl, String clientId, String scope) {
+ if (Client.getInstance() != null) {
+ Client.getInstance().fetchThirdPartyToken(tokenUrl, clientId, scope);
}
-
- nativeOnThirdPartyTokenFetched(token, sharedSecret);
}
/** Passes authentication data to the native handling code. */
- private static native void nativeOnThirdPartyTokenFetched(String token, String sharedSecret);
+ static native void nativeOnThirdPartyTokenFetched(String token, String sharedSecret);
//
// Host and Client Capabilities
@@ -392,8 +175,10 @@ public class JniInterface {
/** Set the list of negotiated capabilities between host and client. Called on the UI thread. */
@CalledByNative
- public static void setCapabilities(String capabilities) {
- sCapabilityManager.setNegotiatedCapabilities(capabilities);
+ private static void setCapabilities(String capabilities) {
+ if (Client.getInstance() != null) {
+ Client.getInstance().setCapabilities(capabilities);
+ }
}
//
@@ -402,18 +187,12 @@ public class JniInterface {
/** Passes on the deconstructed ExtensionMessage to the app. Called on the UI thread. */
@CalledByNative
- public static void handleExtensionMessage(String type, String data) {
- sCapabilityManager.onExtensionMessage(type, data);
- }
-
- /** Sends an extension message to the Chromoting host. Called on the UI thread. */
- public static void sendExtensionMessage(String type, String data) {
- if (!sConnected) {
- return;
+ private static void handleExtensionMessage(String type, String data) {
+ if (Client.getInstance() != null) {
+ Client.getInstance().handleExtensionMessage(type, data);
}
-
- nativeSendExtensionMessage(type, data);
}
- private static native void nativeSendExtensionMessage(String type, String data);
+ /** Passes extension message to the native code. */
+ static native void nativeSendExtensionMessage(String type, String data);
}
diff --git a/remoting/android/javatests/src/org/chromium/chromoting/TouchInputStrategyTest.java b/remoting/android/javatests/src/org/chromium/chromoting/TouchInputStrategyTest.java
index c7774f7..3dea369 100644
--- a/remoting/android/javatests/src/org/chromium/chromoting/TouchInputStrategyTest.java
+++ b/remoting/android/javatests/src/org/chromium/chromoting/TouchInputStrategyTest.java
@@ -197,7 +197,10 @@ public class TouchInputStrategyTest extends InstrumentationTestCase {
public void setUp() {
mRenderData = new RenderData();
mRemoteInputInjector = new MockRemoteInputInjector();
- mInputStrategy = new TouchInputStrategy(mRenderData);
+
+ // TODO(lambroslambrou): Provide a mock Client implementation that doesn't call out to JNI,
+ // and mock the Client methods instead of using MockRemoteInputInjector here.
+ mInputStrategy = new TouchInputStrategy(mRenderData, null);
mInputStrategy.setRemoteInputInjectorForTest(mRemoteInputInjector);
mEventGenerator = new TouchEventGenerator();