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; | |
11 | 9 |
12 import org.chromium.base.ContextUtils; | 10 import org.chromium.base.ContextUtils; |
13 import org.chromium.base.Log; | |
14 import org.chromium.base.annotations.CalledByNative; | 11 import org.chromium.base.annotations.CalledByNative; |
15 import org.chromium.base.annotations.JNINamespace; | 12 import org.chromium.base.annotations.JNINamespace; |
16 import org.chromium.chromoting.CapabilityManager; | |
17 import org.chromium.chromoting.SessionAuthenticator; | |
18 | 13 |
19 import java.nio.ByteBuffer; | 14 import java.nio.ByteBuffer; |
20 import java.nio.ByteOrder; | |
21 | 15 |
22 /** | 16 /** |
23 * Initializes the Chromium remoting library, and provides JNI calls into it. | 17 * Initializes the Chromium remoting library, and provides JNI calls into it. |
24 * All interaction with the native code is centralized in this class. | 18 * All interaction with the native code is centralized in this class. |
25 */ | 19 */ |
26 @JNINamespace("remoting") | 20 @JNINamespace("remoting") |
27 public class JniInterface { | 21 public class JniInterface { |
28 private static final String TAG = "Chromoting"; | |
29 | |
30 /* | 22 /* |
31 * Library-loading state machine. | 23 * Library-loading state machine. |
32 */ | 24 */ |
33 /** Whether the library has been loaded. Accessed on the UI thread. */ | 25 /** Whether the library has been loaded. Accessed on the UI thread. */ |
34 private static boolean sLoaded = false; | 26 private static boolean sLoaded = false; |
35 | 27 |
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 | |
69 /** | 28 /** |
70 * To be called once from the main Activity. Loads and initializes the nativ
e code. | 29 * To be called once from the main Activity. Loads and initializes the nativ
e code. |
71 * Called on the UI thread. | 30 * Called on the UI thread. |
72 */ | 31 */ |
73 public static void loadLibrary(Context context) { | 32 public static void loadLibrary(Context context) { |
74 if (sLoaded) return; | 33 if (sLoaded) return; |
75 | 34 |
76 System.loadLibrary("remoting_client_jni"); | 35 System.loadLibrary("remoting_client_jni"); |
77 | 36 |
78 ContextUtils.initApplicationContext(context.getApplicationContext()); | 37 ContextUtils.initApplicationContext(context.getApplicationContext()); |
79 nativeLoadNative(); | 38 nativeLoadNative(); |
80 sLoaded = true; | 39 sLoaded = true; |
81 } | 40 } |
82 | 41 |
83 /** Performs the native portion of the initialization. */ | 42 /** Performs the native portion of the initialization. */ |
84 private static native void nativeLoadNative(); | 43 private static native void nativeLoadNative(); |
85 | 44 |
86 /* | 45 /* |
87 * API/OAuth2 keys access. | 46 * API/OAuth2 keys access. |
88 */ | 47 */ |
89 public static native String nativeGetApiKey(); | 48 public static native String nativeGetApiKey(); |
90 public static native String nativeGetClientId(); | 49 public static native String nativeGetClientId(); |
91 public static native String nativeGetClientSecret(); | 50 public static native String nativeGetClientSecret(); |
92 | 51 |
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 | |
112 /** Performs the native portion of the connection. */ | 52 /** Performs the native portion of the connection. */ |
113 private static native void nativeConnect(String username, String authToken,
String hostJid, | 53 static native void nativeConnect(String username, String authToken, String h
ostJid, |
114 String hostId, String hostPubkey, String pairId, String pairSecret, | 54 String hostId, String hostPubkey, String pairId, String pairSecret, |
115 String capabilities, String flags); | 55 String capabilities, String flags); |
116 | 56 |
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 | |
146 /** Performs the native portion of the cleanup. */ | 57 /** Performs the native portion of the cleanup. */ |
147 private static native void nativeDisconnect(); | 58 static native void nativeDisconnect(); |
148 | 59 |
149 /** Called by native code whenever the connection status changes. Called on
the UI thread. */ | 60 /** Called by native code whenever the connection status changes. Called on
the UI thread. */ |
150 @CalledByNative | 61 @CalledByNative |
151 private static void onConnectionState(int stateCode, int errorCode) { | 62 private static void onConnectionState(int stateCode, int errorCode) { |
152 ConnectionListener.State state = ConnectionListener.State.fromValue(stat
eCode); | 63 if (Client.getInstance() != null) { |
153 ConnectionListener.Error error = ConnectionListener.Error.fromValue(erro
rCode); | 64 Client.getInstance().onConnectionState(stateCode, errorCode); |
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(); | |
159 } | 65 } |
160 } | 66 } |
161 | 67 |
162 /** Prompts the user to enter a PIN. Called on the UI thread. */ | 68 /** Prompts the user to enter a PIN. Called on the UI thread. */ |
163 @CalledByNative | 69 @CalledByNative |
164 private static void displayAuthenticationPrompt(boolean pairingSupported) { | 70 private static void displayAuthenticationPrompt(boolean pairingSupported) { |
165 sAuthenticator.displayAuthenticationPrompt(pairingSupported); | 71 if (Client.getInstance() != null) { |
| 72 Client.getInstance().displayAuthenticationPrompt(pairingSupported); |
| 73 } |
166 } | 74 } |
167 | 75 |
168 /** | 76 /** Native implementation of Client.handleAuthenticationResponse(). */ |
169 * Performs the native response to the user's PIN. | 77 static native void nativeAuthenticationResponse( |
170 * @param pin The entered PIN. | |
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( | |
183 String pin, boolean createPair, String deviceName); | 78 String pin, boolean createPair, String deviceName); |
184 | 79 |
185 /** Saves newly-received pairing credentials to permanent storage. Called on
the UI thread. */ | 80 /** Saves newly-received pairing credentials to permanent storage. Called on
the UI thread. */ |
186 @CalledByNative | 81 @CalledByNative |
187 private static void commitPairingCredentials(String host, String id, String
secret) { | 82 private static void commitPairingCredentials(String host, String id, String
secret) { |
188 sAuthenticator.commitPairingCredentials(host, id, secret); | 83 if (Client.getInstance() != null) { |
189 } | 84 Client.getInstance().commitPairingCredentials(host, id, secret); |
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 } | 85 } |
199 | |
200 nativeSendMouseEvent(x, y, whichButton, buttonDown); | |
201 } | 86 } |
202 | 87 |
203 /** Passes mouse information to the native handling code. */ | 88 /** Passes mouse information to the native handling code. */ |
204 private static native void nativeSendMouseEvent( | 89 static native void nativeSendMouseEvent( |
205 int x, int y, int whichButton, boolean buttonDown); | 90 int x, int y, int whichButton, boolean buttonDown); |
206 | 91 |
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 | |
216 /** Passes mouse-wheel information to the native handling code. */ | 92 /** Passes mouse-wheel information to the native handling code. */ |
217 private static native void nativeSendMouseWheelEvent(int deltaX, int deltaY)
; | 93 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 } | |
230 | 94 |
231 /** | 95 /** |
232 * Passes key press information to the native handling code. | 96 * Passes key press information to the native handling code. |
233 */ | 97 */ |
234 private static native boolean nativeSendKeyEvent(int scanCode, int keyCode,
boolean keyDown); | 98 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 } | |
244 | 99 |
245 /** Passes text event information to the native handling code. */ | 100 /** Passes text event information to the native handling code. */ |
246 private static native void nativeSendTextEvent(String text); | 101 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 } | |
252 | 102 |
253 /** Passes touch event information to the native handling code. */ | 103 /** Passes touch event information to the native handling code. */ |
254 private static native void nativeSendTouchEvent(int eventType, TouchEventDat
a[] data); | 104 static native void nativeSendTouchEvent(int eventType, TouchEventData[] data
); |
255 | 105 |
256 /** | 106 /** Native implementation of Client.enableVideoChannel() */ |
257 * Enables or disables the video channel. Called on the UI thread in respons
e to Activity | 107 static native void nativeEnableVideoChannel(boolean enable); |
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 } | |
287 | 108 |
288 /** Schedules a redraw on the native graphics thread. */ | 109 /** Schedules a redraw on the native graphics thread. */ |
289 private static native void nativeScheduleRedraw(); | 110 static native void nativeScheduleRedraw(); |
290 | 111 |
291 /** | 112 /** |
292 * Performs the redrawing callback. This is a no-op if the window isn't visi
ble. Called on the | 113 * Performs the redrawing callback. This is a no-op if the window isn't visi
ble. Called on the |
293 * graphics thread. | 114 * graphics thread. |
294 */ | 115 */ |
295 @CalledByNative | 116 @CalledByNative |
296 private static void redrawGraphicsInternal() { | 117 private static void redrawGraphicsInternal() { |
297 Runnable callback = sRedrawCallback; | 118 Client client = Client.getInstance(); |
298 if (callback != null) { | 119 if (client != null) { |
299 callback.run(); | 120 client.redrawGraphicsInternal(); |
300 } | 121 } |
301 } | 122 } |
302 | 123 |
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 | |
317 /** | 124 /** |
318 * Sets a new video frame. Called on the native graphics thread when a new f
rame is allocated. | 125 * Sets a new video frame. Called on the native graphics thread when a new f
rame is allocated. |
319 */ | 126 */ |
320 @CalledByNative | 127 @CalledByNative |
321 private static void setVideoFrame(Bitmap bitmap) { | 128 private static void setVideoFrame(Bitmap bitmap) { |
322 if (Looper.myLooper() == Looper.getMainLooper()) { | 129 Client client = Client.getInstance(); |
323 Log.w(TAG, "Video frame updated on UI thread"); | 130 if (client != null) { |
324 } | 131 client.setVideoFrame(bitmap); |
325 | |
326 synchronized (sFrameLock) { | |
327 sFrameBitmap = bitmap; | |
328 } | 132 } |
329 } | 133 } |
330 | 134 |
331 /** | 135 /** |
332 * Creates a new Bitmap to hold video frame pixels. Called by native code wh
ich stores a global | 136 * Creates a new Bitmap to hold video frame pixels. Called by native code wh
ich stores a global |
333 * reference to the Bitmap and writes the decoded frame pixels to it. | 137 * reference to the Bitmap and writes the decoded frame pixels to it. |
334 */ | 138 */ |
335 @CalledByNative | 139 @CalledByNative |
336 private static Bitmap newBitmap(int width, int height) { | 140 private static Bitmap newBitmap(int width, int height) { |
337 return Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); | 141 return Client.newBitmap(width, height); |
338 } | 142 } |
339 | 143 |
340 /** | 144 /** |
341 * Updates the cursor shape. This is called on the graphics thread when rece
iving a new cursor | 145 * Updates the cursor shape. This is called on the graphics thread when rece
iving a new cursor |
342 * shape from the host. | 146 * shape from the host. |
343 */ | 147 */ |
344 @CalledByNative | 148 @CalledByNative |
345 public static void updateCursorShape( | 149 private static void updateCursorShape( |
346 int width, int height, int hotspotX, int hotspotY, ByteBuffer buffer
) { | 150 int width, int height, int hotspotX, int hotspotY, ByteBuffer buffer
) { |
347 sCursorHotspot = new Point(hotspotX, hotspotY); | 151 Client client = Client.getInstance(); |
348 | 152 if (client != null) { |
349 int[] data = new int[width * height]; | 153 client.updateCursorShape(width, height, hotspotX, hotspotY, buffer); |
350 buffer.order(ByteOrder.LITTLE_ENDIAN); | 154 } |
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; | |
363 } | 155 } |
364 | 156 |
365 // | 157 // |
366 // Third Party Authentication | 158 // Third Party Authentication |
367 // | 159 // |
368 | 160 |
369 /** Pops up a third party login page to fetch the token required for authent
ication. */ | 161 /** Pops up a third party login page to fetch the token required for authent
ication. */ |
370 @CalledByNative | 162 @CalledByNative |
371 public static void fetchThirdPartyToken(String tokenUrl, String clientId, St
ring scope) { | 163 private static void fetchThirdPartyToken(String tokenUrl, String clientId, S
tring scope) { |
372 sAuthenticator.fetchThirdPartyToken(tokenUrl, clientId, scope); | 164 if (Client.getInstance() != null) { |
373 } | 165 Client.getInstance().fetchThirdPartyToken(tokenUrl, clientId, scope)
; |
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; | |
381 } | 166 } |
382 | |
383 nativeOnThirdPartyTokenFetched(token, sharedSecret); | |
384 } | 167 } |
385 | 168 |
386 /** Passes authentication data to the native handling code. */ | 169 /** Passes authentication data to the native handling code. */ |
387 private static native void nativeOnThirdPartyTokenFetched(String token, Stri
ng sharedSecret); | 170 static native void nativeOnThirdPartyTokenFetched(String token, String share
dSecret); |
388 | 171 |
389 // | 172 // |
390 // Host and Client Capabilities | 173 // Host and Client Capabilities |
391 // | 174 // |
392 | 175 |
393 /** Set the list of negotiated capabilities between host and client. Called
on the UI thread. */ | 176 /** Set the list of negotiated capabilities between host and client. Called
on the UI thread. */ |
394 @CalledByNative | 177 @CalledByNative |
395 public static void setCapabilities(String capabilities) { | 178 private static void setCapabilities(String capabilities) { |
396 sCapabilityManager.setNegotiatedCapabilities(capabilities); | 179 if (Client.getInstance() != null) { |
| 180 Client.getInstance().setCapabilities(capabilities); |
| 181 } |
397 } | 182 } |
398 | 183 |
399 // | 184 // |
400 // Extension Message Handling | 185 // Extension Message Handling |
401 // | 186 // |
402 | 187 |
403 /** Passes on the deconstructed ExtensionMessage to the app. Called on the U
I thread. */ | 188 /** Passes on the deconstructed ExtensionMessage to the app. Called on the U
I thread. */ |
404 @CalledByNative | 189 @CalledByNative |
405 public static void handleExtensionMessage(String type, String data) { | 190 private static void handleExtensionMessage(String type, String data) { |
406 sCapabilityManager.onExtensionMessage(type, data); | 191 if (Client.getInstance() != null) { |
| 192 Client.getInstance().handleExtensionMessage(type, data); |
| 193 } |
407 } | 194 } |
408 | 195 |
409 /** Sends an extension message to the Chromoting host. Called on the UI thre
ad. */ | 196 /** Passes extension message to the native code. */ |
410 public static void sendExtensionMessage(String type, String data) { | 197 static native void nativeSendExtensionMessage(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); | |
419 } | 198 } |
OLD | NEW |