OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 package org.chromium.chromoting.jni; | 5 package org.chromium.chromoting.jni; |
6 | 6 |
7 import android.content.Context; | 7 import android.content.Context; |
8 import android.graphics.Bitmap; | 8 import android.graphics.Bitmap; |
| 9 import android.graphics.Point; |
| 10 import android.os.Looper; |
9 | 11 |
10 import org.chromium.base.ContextUtils; | 12 import org.chromium.base.ContextUtils; |
| 13 import org.chromium.base.Log; |
11 import org.chromium.base.annotations.CalledByNative; | 14 import org.chromium.base.annotations.CalledByNative; |
12 import org.chromium.base.annotations.JNINamespace; | 15 import org.chromium.base.annotations.JNINamespace; |
| 16 import org.chromium.chromoting.CapabilityManager; |
| 17 import org.chromium.chromoting.SessionAuthenticator; |
13 | 18 |
14 import java.nio.ByteBuffer; | 19 import java.nio.ByteBuffer; |
| 20 import java.nio.ByteOrder; |
15 | 21 |
16 /** | 22 /** |
17 * Initializes the Chromium remoting library, and provides JNI calls into it. | 23 * Initializes the Chromium remoting library, and provides JNI calls into it. |
18 * All interaction with the native code is centralized in this class. | 24 * All interaction with the native code is centralized in this class. |
19 */ | 25 */ |
20 @JNINamespace("remoting") | 26 @JNINamespace("remoting") |
21 public class JniInterface { | 27 public class JniInterface { |
| 28 private static final String TAG = "Chromoting"; |
| 29 |
22 /* | 30 /* |
23 * Library-loading state machine. | 31 * Library-loading state machine. |
24 */ | 32 */ |
25 /** Whether the library has been loaded. Accessed on the UI thread. */ | 33 /** Whether the library has been loaded. Accessed on the UI thread. */ |
26 private static boolean sLoaded = false; | 34 private static boolean sLoaded = false; |
27 | 35 |
| 36 /** Used for authentication-related UX during connection. Accessed on the UI
thread. */ |
| 37 private static SessionAuthenticator sAuthenticator; |
| 38 |
| 39 /* |
| 40 * Connection-initiating state machine. |
| 41 */ |
| 42 /** Whether the native code is attempting a connection. Accessed on the UI t
hread. */ |
| 43 private static boolean sConnected = false; |
| 44 |
| 45 /** Notified upon successful connection or disconnection. Accessed on the UI
thread. */ |
| 46 private static ConnectionListener sConnectionListener = null; |
| 47 |
| 48 /** |
| 49 * Callback invoked on the graphics thread to repaint the desktop. Accessed
on the UI and |
| 50 * graphics threads. |
| 51 */ |
| 52 private static Runnable sRedrawCallback = null; |
| 53 |
| 54 /** Bitmap holding a copy of the latest video frame. Accessed on the UI and
graphics threads. */ |
| 55 private static Bitmap sFrameBitmap = null; |
| 56 |
| 57 /** Protects access to sFrameBitmap. */ |
| 58 private static final Object sFrameLock = new Object(); |
| 59 |
| 60 /** Position of cursor hot-spot. Accessed on the graphics thread. */ |
| 61 private static Point sCursorHotspot = new Point(); |
| 62 |
| 63 /** Bitmap holding the cursor shape. Accessed on the graphics thread. */ |
| 64 private static Bitmap sCursorBitmap = null; |
| 65 |
| 66 /** Capability Manager through which capabilities and extensions are handled
. */ |
| 67 private static CapabilityManager sCapabilityManager = CapabilityManager.getI
nstance(); |
| 68 |
28 /** | 69 /** |
29 * To be called once from the main Activity. Loads and initializes the nativ
e code. | 70 * To be called once from the main Activity. Loads and initializes the nativ
e code. |
30 * Called on the UI thread. | 71 * Called on the UI thread. |
31 */ | 72 */ |
32 public static void loadLibrary(Context context) { | 73 public static void loadLibrary(Context context) { |
33 if (sLoaded) return; | 74 if (sLoaded) return; |
34 | 75 |
35 System.loadLibrary("remoting_client_jni"); | 76 System.loadLibrary("remoting_client_jni"); |
36 | 77 |
37 ContextUtils.initApplicationContext(context.getApplicationContext()); | 78 ContextUtils.initApplicationContext(context.getApplicationContext()); |
38 nativeLoadNative(); | 79 nativeLoadNative(); |
39 sLoaded = true; | 80 sLoaded = true; |
40 } | 81 } |
41 | 82 |
42 /** Performs the native portion of the initialization. */ | 83 /** Performs the native portion of the initialization. */ |
43 private static native void nativeLoadNative(); | 84 private static native void nativeLoadNative(); |
44 | 85 |
45 /* | 86 /* |
46 * API/OAuth2 keys access. | 87 * API/OAuth2 keys access. |
47 */ | 88 */ |
48 public static native String nativeGetApiKey(); | 89 public static native String nativeGetApiKey(); |
49 public static native String nativeGetClientId(); | 90 public static native String nativeGetClientId(); |
50 public static native String nativeGetClientSecret(); | 91 public static native String nativeGetClientSecret(); |
51 | 92 |
| 93 /** Returns whether the client is connected. */ |
| 94 public static boolean isConnected() { |
| 95 return sConnected; |
| 96 } |
| 97 |
| 98 /** Attempts to form a connection to the user-selected host. Called on the U
I thread. */ |
| 99 public static void connectToHost(String username, String authToken, String h
ostJid, |
| 100 String hostId, String hostPubkey, SessionAuthenticator authenticator
, String flags, |
| 101 ConnectionListener listener) { |
| 102 disconnectFromHost(); |
| 103 |
| 104 sConnectionListener = listener; |
| 105 sAuthenticator = authenticator; |
| 106 nativeConnect(username, authToken, hostJid, hostId, hostPubkey, |
| 107 sAuthenticator.getPairingId(hostId), sAuthenticator.getPairingSe
cret(hostId), |
| 108 sCapabilityManager.getLocalCapabilities(), flags); |
| 109 sConnected = true; |
| 110 } |
| 111 |
52 /** Performs the native portion of the connection. */ | 112 /** Performs the native portion of the connection. */ |
53 static native void nativeConnect(String username, String authToken, String h
ostJid, | 113 private static native void nativeConnect(String username, String authToken,
String hostJid, |
54 String hostId, String hostPubkey, String pairId, String pairSecret, | 114 String hostId, String hostPubkey, String pairId, String pairSecret, |
55 String capabilities, String flags); | 115 String capabilities, String flags); |
56 | 116 |
| 117 /** Severs the connection and cleans up. Called on the UI thread. */ |
| 118 public static void disconnectFromHost() { |
| 119 if (!sConnected) { |
| 120 return; |
| 121 } |
| 122 |
| 123 sConnectionListener.onConnectionState( |
| 124 ConnectionListener.State.CLOSED, ConnectionListener.Error.OK); |
| 125 |
| 126 disconnectFromHostWithoutNotification(); |
| 127 } |
| 128 |
| 129 /** Same as disconnectFromHost() but without notifying the ConnectionListene
r. */ |
| 130 private static void disconnectFromHostWithoutNotification() { |
| 131 if (!sConnected) { |
| 132 return; |
| 133 } |
| 134 |
| 135 nativeDisconnect(); |
| 136 sConnectionListener = null; |
| 137 sConnected = false; |
| 138 sCapabilityManager.onHostDisconnect(); |
| 139 |
| 140 // Drop the reference to free the Bitmap for GC. |
| 141 synchronized (sFrameLock) { |
| 142 sFrameBitmap = null; |
| 143 } |
| 144 } |
| 145 |
57 /** Performs the native portion of the cleanup. */ | 146 /** Performs the native portion of the cleanup. */ |
58 static native void nativeDisconnect(); | 147 private static native void nativeDisconnect(); |
59 | 148 |
60 /** Called by native code whenever the connection status changes. Called on
the UI thread. */ | 149 /** Called by native code whenever the connection status changes. Called on
the UI thread. */ |
61 @CalledByNative | 150 @CalledByNative |
62 private static void onConnectionState(int stateCode, int errorCode) { | 151 private static void onConnectionState(int stateCode, int errorCode) { |
63 if (Client.getInstance() != null) { | 152 ConnectionListener.State state = ConnectionListener.State.fromValue(stat
eCode); |
64 Client.getInstance().onConnectionState(stateCode, errorCode); | 153 ConnectionListener.Error error = ConnectionListener.Error.fromValue(erro
rCode); |
| 154 sConnectionListener.onConnectionState(state, error); |
| 155 if (state == ConnectionListener.State.FAILED || state == ConnectionListe
ner.State.CLOSED) { |
| 156 // Disconnect from the host here, otherwise the next time connectToH
ost() is called, |
| 157 // it will try to disconnect, triggering an incorrect status notific
ation. |
| 158 disconnectFromHostWithoutNotification(); |
65 } | 159 } |
66 } | 160 } |
67 | 161 |
68 /** Prompts the user to enter a PIN. Called on the UI thread. */ | 162 /** Prompts the user to enter a PIN. Called on the UI thread. */ |
69 @CalledByNative | 163 @CalledByNative |
70 private static void displayAuthenticationPrompt(boolean pairingSupported) { | 164 private static void displayAuthenticationPrompt(boolean pairingSupported) { |
71 if (Client.getInstance() != null) { | 165 sAuthenticator.displayAuthenticationPrompt(pairingSupported); |
72 Client.getInstance().displayAuthenticationPrompt(pairingSupported); | 166 } |
73 } | 167 |
74 } | 168 /** |
75 | 169 * Performs the native response to the user's PIN. |
76 /** Native implementation of Client.handleAuthenticationResponse(). */ | 170 * @param pin The entered PIN. |
77 static native void nativeAuthenticationResponse( | 171 * @param createPair Whether to create a new pairing for this client. |
| 172 * @param deviceName The device name to appear in the pairing registry. Only
used if createPair |
| 173 * is true. |
| 174 */ |
| 175 public static void handleAuthenticationResponse( |
| 176 String pin, boolean createPair, String deviceName) { |
| 177 assert sConnected; |
| 178 nativeAuthenticationResponse(pin, createPair, deviceName); |
| 179 } |
| 180 |
| 181 /** Native implementation of handleAuthenticationResponse(). */ |
| 182 private static native void nativeAuthenticationResponse( |
78 String pin, boolean createPair, String deviceName); | 183 String pin, boolean createPair, String deviceName); |
79 | 184 |
80 /** Saves newly-received pairing credentials to permanent storage. Called on
the UI thread. */ | 185 /** Saves newly-received pairing credentials to permanent storage. Called on
the UI thread. */ |
81 @CalledByNative | 186 @CalledByNative |
82 private static void commitPairingCredentials(String host, String id, String
secret) { | 187 private static void commitPairingCredentials(String host, String id, String
secret) { |
83 if (Client.getInstance() != null) { | 188 sAuthenticator.commitPairingCredentials(host, id, secret); |
84 Client.getInstance().commitPairingCredentials(host, id, secret); | 189 } |
85 } | 190 |
| 191 /** |
| 192 * Moves the mouse cursor, possibly while clicking the specified (nonnegativ
e) button. Called |
| 193 * on the UI thread. |
| 194 */ |
| 195 public static void sendMouseEvent(int x, int y, int whichButton, boolean but
tonDown) { |
| 196 if (!sConnected) { |
| 197 return; |
| 198 } |
| 199 |
| 200 nativeSendMouseEvent(x, y, whichButton, buttonDown); |
86 } | 201 } |
87 | 202 |
88 /** Passes mouse information to the native handling code. */ | 203 /** Passes mouse information to the native handling code. */ |
89 static native void nativeSendMouseEvent( | 204 private static native void nativeSendMouseEvent( |
90 int x, int y, int whichButton, boolean buttonDown); | 205 int x, int y, int whichButton, boolean buttonDown); |
91 | 206 |
| 207 /** Injects a mouse-wheel event with delta values. Called on the UI thread.
*/ |
| 208 public static void sendMouseWheelEvent(int deltaX, int deltaY) { |
| 209 if (!sConnected) { |
| 210 return; |
| 211 } |
| 212 |
| 213 nativeSendMouseWheelEvent(deltaX, deltaY); |
| 214 } |
| 215 |
92 /** Passes mouse-wheel information to the native handling code. */ | 216 /** Passes mouse-wheel information to the native handling code. */ |
93 static native void nativeSendMouseWheelEvent(int deltaX, int deltaY); | 217 private static native void nativeSendMouseWheelEvent(int deltaX, int deltaY)
; |
| 218 |
| 219 /** |
| 220 * Presses or releases the specified (nonnegative) key. Called on the UI thr
ead. If scanCode |
| 221 * is not zero then keyCode is ignored. |
| 222 */ |
| 223 public static boolean sendKeyEvent(int scanCode, int keyCode, boolean keyDow
n) { |
| 224 if (!sConnected) { |
| 225 return false; |
| 226 } |
| 227 |
| 228 return nativeSendKeyEvent(scanCode, keyCode, keyDown); |
| 229 } |
94 | 230 |
95 /** | 231 /** |
96 * Passes key press information to the native handling code. | 232 * Passes key press information to the native handling code. |
97 */ | 233 */ |
98 static native boolean nativeSendKeyEvent(int scanCode, int keyCode, boolean
keyDown); | 234 private static native boolean nativeSendKeyEvent(int scanCode, int keyCode,
boolean keyDown); |
| 235 |
| 236 /** Sends TextEvent to the host. Called on the UI thread. */ |
| 237 public static void sendTextEvent(String text) { |
| 238 if (!sConnected) { |
| 239 return; |
| 240 } |
| 241 |
| 242 nativeSendTextEvent(text); |
| 243 } |
99 | 244 |
100 /** Passes text event information to the native handling code. */ | 245 /** Passes text event information to the native handling code. */ |
101 static native void nativeSendTextEvent(String text); | 246 private static native void nativeSendTextEvent(String text); |
| 247 |
| 248 /** Sends an array of TouchEvents to the host. Called on the UI thread. */ |
| 249 public static void sendTouchEvent(TouchEventData.EventType eventType, TouchE
ventData[] data) { |
| 250 nativeSendTouchEvent(eventType.value(), data); |
| 251 } |
102 | 252 |
103 /** Passes touch event information to the native handling code. */ | 253 /** Passes touch event information to the native handling code. */ |
104 static native void nativeSendTouchEvent(int eventType, TouchEventData[] data
); | 254 private static native void nativeSendTouchEvent(int eventType, TouchEventDat
a[] data); |
105 | 255 |
106 /** Native implementation of Client.enableVideoChannel() */ | 256 /** |
107 static native void nativeEnableVideoChannel(boolean enable); | 257 * Enables or disables the video channel. Called on the UI thread in respons
e to Activity |
| 258 * lifecycle events. |
| 259 */ |
| 260 public static void enableVideoChannel(boolean enable) { |
| 261 if (!sConnected) { |
| 262 return; |
| 263 } |
| 264 |
| 265 nativeEnableVideoChannel(enable); |
| 266 } |
| 267 |
| 268 /** Native implementation of enableVideoChannel() */ |
| 269 private static native void nativeEnableVideoChannel(boolean enable); |
| 270 |
| 271 /** |
| 272 * Sets the redraw callback to the provided functor. Provide a value of null
whenever the |
| 273 * window is no longer visible so that we don't continue to draw onto it. Ca
lled on the UI |
| 274 * thread. |
| 275 */ |
| 276 public static void provideRedrawCallback(Runnable redrawCallback) { |
| 277 sRedrawCallback = redrawCallback; |
| 278 } |
| 279 |
| 280 /** Forces the native graphics thread to redraw to the canvas. Called on the
UI thread. */ |
| 281 public static boolean redrawGraphics() { |
| 282 if (!sConnected || sRedrawCallback == null) return false; |
| 283 |
| 284 nativeScheduleRedraw(); |
| 285 return true; |
| 286 } |
108 | 287 |
109 /** Schedules a redraw on the native graphics thread. */ | 288 /** Schedules a redraw on the native graphics thread. */ |
110 static native void nativeScheduleRedraw(); | 289 private static native void nativeScheduleRedraw(); |
111 | 290 |
112 /** | 291 /** |
113 * Performs the redrawing callback. This is a no-op if the window isn't visi
ble. Called on the | 292 * Performs the redrawing callback. This is a no-op if the window isn't visi
ble. Called on the |
114 * graphics thread. | 293 * graphics thread. |
115 */ | 294 */ |
116 @CalledByNative | 295 @CalledByNative |
117 private static void redrawGraphicsInternal() { | 296 private static void redrawGraphicsInternal() { |
118 Client client = Client.getInstance(); | 297 Runnable callback = sRedrawCallback; |
119 if (client != null) { | 298 if (callback != null) { |
120 client.redrawGraphicsInternal(); | 299 callback.run(); |
121 } | 300 } |
122 } | 301 } |
123 | 302 |
| 303 /** |
| 304 * Returns a bitmap of the latest video frame. Called on the native graphics
thread when |
| 305 * DesktopView is repainted. |
| 306 */ |
| 307 public static Bitmap getVideoFrame() { |
| 308 if (Looper.myLooper() == Looper.getMainLooper()) { |
| 309 Log.w(TAG, "Canvas being redrawn on UI thread"); |
| 310 } |
| 311 |
| 312 synchronized (sFrameLock) { |
| 313 return sFrameBitmap; |
| 314 } |
| 315 } |
| 316 |
124 /** | 317 /** |
125 * Sets a new video frame. Called on the native graphics thread when a new f
rame is allocated. | 318 * Sets a new video frame. Called on the native graphics thread when a new f
rame is allocated. |
126 */ | 319 */ |
127 @CalledByNative | 320 @CalledByNative |
128 private static void setVideoFrame(Bitmap bitmap) { | 321 private static void setVideoFrame(Bitmap bitmap) { |
129 Client client = Client.getInstance(); | 322 if (Looper.myLooper() == Looper.getMainLooper()) { |
130 if (client != null) { | 323 Log.w(TAG, "Video frame updated on UI thread"); |
131 client.setVideoFrame(bitmap); | 324 } |
| 325 |
| 326 synchronized (sFrameLock) { |
| 327 sFrameBitmap = bitmap; |
132 } | 328 } |
133 } | 329 } |
134 | 330 |
135 /** | 331 /** |
136 * Creates a new Bitmap to hold video frame pixels. Called by native code wh
ich stores a global | 332 * Creates a new Bitmap to hold video frame pixels. Called by native code wh
ich stores a global |
137 * reference to the Bitmap and writes the decoded frame pixels to it. | 333 * reference to the Bitmap and writes the decoded frame pixels to it. |
138 */ | 334 */ |
139 @CalledByNative | 335 @CalledByNative |
140 private static Bitmap newBitmap(int width, int height) { | 336 private static Bitmap newBitmap(int width, int height) { |
141 return Client.newBitmap(width, height); | 337 return Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); |
142 } | 338 } |
143 | 339 |
144 /** | 340 /** |
145 * Updates the cursor shape. This is called on the graphics thread when rece
iving a new cursor | 341 * Updates the cursor shape. This is called on the graphics thread when rece
iving a new cursor |
146 * shape from the host. | 342 * shape from the host. |
147 */ | 343 */ |
148 @CalledByNative | 344 @CalledByNative |
149 private static void updateCursorShape( | 345 public static void updateCursorShape( |
150 int width, int height, int hotspotX, int hotspotY, ByteBuffer buffer
) { | 346 int width, int height, int hotspotX, int hotspotY, ByteBuffer buffer
) { |
151 Client client = Client.getInstance(); | 347 sCursorHotspot = new Point(hotspotX, hotspotY); |
152 if (client != null) { | 348 |
153 client.updateCursorShape(width, height, hotspotX, hotspotY, buffer); | 349 int[] data = new int[width * height]; |
154 } | 350 buffer.order(ByteOrder.LITTLE_ENDIAN); |
| 351 buffer.asIntBuffer().get(data, 0, data.length); |
| 352 sCursorBitmap = Bitmap.createBitmap(data, width, height, Bitmap.Config.A
RGB_8888); |
| 353 } |
| 354 |
| 355 /** Position of cursor hotspot within cursor image. Called on the graphics t
hread. */ |
| 356 public static Point getCursorHotspot() { |
| 357 return sCursorHotspot; |
| 358 } |
| 359 |
| 360 /** Returns the current cursor shape. Called on the graphics thread. */ |
| 361 public static Bitmap getCursorBitmap() { |
| 362 return sCursorBitmap; |
155 } | 363 } |
156 | 364 |
157 // | 365 // |
158 // Third Party Authentication | 366 // Third Party Authentication |
159 // | 367 // |
160 | 368 |
161 /** Pops up a third party login page to fetch the token required for authent
ication. */ | 369 /** Pops up a third party login page to fetch the token required for authent
ication. */ |
162 @CalledByNative | 370 @CalledByNative |
163 private static void fetchThirdPartyToken(String tokenUrl, String clientId, S
tring scope) { | 371 public static void fetchThirdPartyToken(String tokenUrl, String clientId, St
ring scope) { |
164 if (Client.getInstance() != null) { | 372 sAuthenticator.fetchThirdPartyToken(tokenUrl, clientId, scope); |
165 Client.getInstance().fetchThirdPartyToken(tokenUrl, clientId, scope)
; | 373 } |
| 374 |
| 375 /** |
| 376 * Notify the native code to continue authentication with the |token| and th
e |sharedSecret|. |
| 377 */ |
| 378 public static void onThirdPartyTokenFetched(String token, String sharedSecre
t) { |
| 379 if (!sConnected) { |
| 380 return; |
166 } | 381 } |
| 382 |
| 383 nativeOnThirdPartyTokenFetched(token, sharedSecret); |
167 } | 384 } |
168 | 385 |
169 /** Passes authentication data to the native handling code. */ | 386 /** Passes authentication data to the native handling code. */ |
170 static native void nativeOnThirdPartyTokenFetched(String token, String share
dSecret); | 387 private static native void nativeOnThirdPartyTokenFetched(String token, Stri
ng sharedSecret); |
171 | 388 |
172 // | 389 // |
173 // Host and Client Capabilities | 390 // Host and Client Capabilities |
174 // | 391 // |
175 | 392 |
176 /** Set the list of negotiated capabilities between host and client. Called
on the UI thread. */ | 393 /** Set the list of negotiated capabilities between host and client. Called
on the UI thread. */ |
177 @CalledByNative | 394 @CalledByNative |
178 private static void setCapabilities(String capabilities) { | 395 public static void setCapabilities(String capabilities) { |
179 if (Client.getInstance() != null) { | 396 sCapabilityManager.setNegotiatedCapabilities(capabilities); |
180 Client.getInstance().setCapabilities(capabilities); | |
181 } | |
182 } | 397 } |
183 | 398 |
184 // | 399 // |
185 // Extension Message Handling | 400 // Extension Message Handling |
186 // | 401 // |
187 | 402 |
188 /** Passes on the deconstructed ExtensionMessage to the app. Called on the U
I thread. */ | 403 /** Passes on the deconstructed ExtensionMessage to the app. Called on the U
I thread. */ |
189 @CalledByNative | 404 @CalledByNative |
190 private static void handleExtensionMessage(String type, String data) { | 405 public static void handleExtensionMessage(String type, String data) { |
191 if (Client.getInstance() != null) { | 406 sCapabilityManager.onExtensionMessage(type, data); |
192 Client.getInstance().handleExtensionMessage(type, data); | |
193 } | |
194 } | 407 } |
195 | 408 |
196 /** Passes extension message to the native code. */ | 409 /** Sends an extension message to the Chromoting host. Called on the UI thre
ad. */ |
197 static native void nativeSendExtensionMessage(String type, String data); | 410 public static void sendExtensionMessage(String type, String data) { |
| 411 if (!sConnected) { |
| 412 return; |
| 413 } |
| 414 |
| 415 nativeSendExtensionMessage(type, data); |
| 416 } |
| 417 |
| 418 private static native void nativeSendExtensionMessage(String type, String da
ta); |
198 } | 419 } |
OLD | NEW |