OLD | NEW |
---|---|
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 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.graphics.Bitmap; | |
8 import android.graphics.Point; | |
9 import android.os.Looper; | |
10 | |
11 import org.chromium.base.Log; | |
7 import org.chromium.base.annotations.JNINamespace; | 12 import org.chromium.base.annotations.JNINamespace; |
13 import org.chromium.chromoting.CapabilityManager; | |
14 import org.chromium.chromoting.SessionAuthenticator; | |
15 | |
16 import java.nio.ByteBuffer; | |
17 import java.nio.ByteOrder; | |
8 | 18 |
9 /** | 19 /** |
10 * Class to manage a client connection to the host. This class controls the life time of the | 20 * Class to manage a client connection to the host. This class controls the life time of the |
11 * corresponding C++ object which implements the connection. A new object should be created for | 21 * corresponding C++ object which implements the connection. A new object should be created for |
12 * each connection to the host, so that notifications from a connection are alwa ys sent to the | 22 * each connection to the host, so that notifications from a connection are alwa ys sent to the |
13 * right object. | 23 * right object. |
14 */ | 24 */ |
15 @JNINamespace("remoting") | 25 @JNINamespace("remoting") |
16 public class Client { | 26 public class Client { |
Sergey Ulanov
2015/12/21 17:58:15
maybe not for this CL: define abstract interface f
Lambros
2016/01/29 23:58:25
Acknowledged.
| |
27 private static final String TAG = "Chromoting"; | |
28 | |
17 // Pointer to the C++ object, cast to a |long|. | 29 // Pointer to the C++ object, cast to a |long|. |
18 private long mNativeJniClient; | 30 private long mNativeJniClient; |
19 | 31 |
20 public void init() { | 32 public Client() { |
21 mNativeJniClient = nativeInit(); | 33 mNativeJniClient = nativeInit(); |
22 } | 34 } |
23 | 35 |
24 private native long nativeInit(); | 36 private native long nativeInit(); |
25 | 37 |
26 public void destroy() { | 38 public void destroy() { |
39 disconnectFromHost(); | |
27 nativeDestroy(mNativeJniClient); | 40 nativeDestroy(mNativeJniClient); |
28 } | 41 } |
29 | 42 |
30 private native void nativeDestroy(long nativeJniClient); | 43 private native void nativeDestroy(long nativeJniClient); |
44 | |
45 /** Used for authentication-related UX during connection. Accessed on the UI thread. */ | |
46 private SessionAuthenticator mAuthenticator; | |
47 | |
48 /** Whether the native code is attempting a connection. Accessed on the UI t hread. */ | |
joedow
2016/01/19 16:35:06
Should this be 'attempting' a connection or 'estab
Lambros
2016/01/29 23:58:25
I'd prefer to clean this up in a followup CL - I m
| |
49 private boolean mConnected; | |
50 | |
51 /** Notified upon successful connection or disconnection. Accessed on the UI thread. */ | |
52 private ConnectionListener mConnectionListener; | |
53 | |
54 /** | |
55 * Callback invoked on the graphics thread to repaint the desktop. Accessed on the UI and | |
56 * graphics threads. | |
57 */ | |
58 private Runnable mRedrawCallback; | |
59 | |
60 /** Bitmap holding a copy of the latest video frame. Accessed on the UI and graphics threads. */ | |
61 private Bitmap mFrameBitmap; | |
62 | |
63 /** Protects access to {@link mFrameBitmap}. */ | |
64 private final Object mFrameLock = new Object(); | |
65 | |
66 /** Position of cursor hot-spot. Accessed on the graphics thread. */ | |
67 private Point mCursorHotspot = new Point(); | |
68 | |
69 /** Bitmap holding the cursor shape. Accessed on the graphics thread. */ | |
70 private Bitmap mCursorBitmap; | |
71 | |
72 /** Capability Manager through which capabilities and extensions are handled . */ | |
73 private CapabilityManager mCapabilityManager = new CapabilityManager(); | |
74 | |
75 public CapabilityManager getCapabilityManager() { | |
76 return mCapabilityManager; | |
77 } | |
78 | |
79 /** Returns whether the client is connected. */ | |
80 public boolean isConnected() { | |
81 return mConnected; | |
82 } | |
83 | |
84 /** Attempts to form a connection to the user-selected host. Called on the U I thread. */ | |
85 public void connectToHost(String username, String authToken, String hostJid, | |
86 String hostId, String hostPubkey, ConnectionListener listener, | |
87 SessionAuthenticator authenticator) { | |
88 disconnectFromHost(); | |
89 | |
90 mConnectionListener = listener; | |
91 mAuthenticator = authenticator; | |
92 JniInterface.nativeConnect(username, authToken, hostJid, hostId, hostPub key, | |
93 mAuthenticator.getPairingId(hostId), mAuthenticator.getPairingSe cret(hostId), | |
94 mCapabilityManager.getLocalCapabilities()); | |
95 mConnected = true; | |
96 } | |
97 | |
98 /** Severs the connection and cleans up. Called on the UI thread. */ | |
99 public void disconnectFromHost() { | |
100 if (!mConnected) { | |
101 return; | |
102 } | |
103 | |
104 mConnectionListener.onConnectionState( | |
105 ConnectionListener.State.CLOSED, ConnectionListener.Error.OK); | |
joedow
2016/01/19 16:35:06
Should this event get fired after the connection w
Lambros
2016/01/29 23:58:25
I'm not certain that changing this won't break any
| |
106 | |
107 disconnectFromHostWithoutNotification(); | |
joedow
2016/01/19 16:35:06
It looks like 'onConnectionState()' already has th
Lambros
2016/01/29 23:58:25
I don't follow?
This code looks fine to me, it's j
| |
108 } | |
109 | |
110 /** Same as disconnectFromHost() but without notifying the ConnectionListene r. */ | |
111 private void disconnectFromHostWithoutNotification() { | |
112 if (!mConnected) { | |
113 return; | |
114 } | |
115 | |
116 JniInterface.nativeDisconnect(); | |
117 mConnectionListener = null; | |
118 mConnected = false; | |
119 mCapabilityManager.onHostDisconnect(); | |
120 | |
121 // Drop the reference to free the Bitmap for GC. | |
122 synchronized (mFrameLock) { | |
123 mFrameBitmap = null; | |
124 } | |
125 } | |
126 | |
127 /** Called by native code whenever the connection status changes. Called on the UI thread. */ | |
128 void onConnectionState(int stateCode, int errorCode) { | |
129 ConnectionListener.State state = ConnectionListener.State.fromValue(stat eCode); | |
130 ConnectionListener.Error error = ConnectionListener.Error.fromValue(erro rCode); | |
131 mConnectionListener.onConnectionState(state, error); | |
132 if (state == ConnectionListener.State.FAILED || state == ConnectionListe ner.State.CLOSED) { | |
133 // Disconnect from the host here, otherwise the next time connectToH ost() is called, | |
134 // it will try to disconnect, triggering an incorrect status notific ation. | |
135 disconnectFromHostWithoutNotification(); | |
136 } | |
137 } | |
138 | |
139 /** Called from native code to prompt the user to enter a PIN. Called on the UI thread. */ | |
140 void displayAuthenticationPrompt(boolean pairingSupported) { | |
141 mAuthenticator.displayAuthenticationPrompt(pairingSupported); | |
142 } | |
143 | |
144 /** | |
145 * Performs the native response to the user's PIN. | |
Sergey Ulanov
2015/12/21 17:58:15
Reword this. E.g. "Called by mAuthenticator after
Lambros
2016/01/29 23:58:25
Done.
| |
146 * @param pin The entered PIN. | |
147 * @param createPair Whether to create a new pairing for this client. | |
148 * @param deviceName The device name to appear in the pairing registry. Only used if createPair | |
149 * is true. | |
150 */ | |
151 public void handleAuthenticationResponse( | |
152 String pin, boolean createPair, String deviceName) { | |
153 assert mConnected; | |
154 JniInterface.nativeAuthenticationResponse(pin, createPair, deviceName); | |
155 } | |
156 | |
157 /** | |
158 * Called from native code, to save newly-received pairing credentials to pe rmanent storage. | |
Sergey Ulanov
2015/12/21 17:58:15
many of these comments need to be rewritten. E.g.
Lambros
2016/01/29 23:58:25
Done.
| |
159 * Called on the UI thread. | |
160 */ | |
161 void commitPairingCredentials(String host, String id, String secret) { | |
162 mAuthenticator.commitPairingCredentials(host, id, secret); | |
163 } | |
164 | |
165 /** | |
166 * Moves the mouse cursor, possibly while clicking the specified (nonnegativ e) button. Called | |
167 * on the UI thread. | |
168 */ | |
169 public void sendMouseEvent(int x, int y, int whichButton, boolean buttonDown ) { | |
170 if (!mConnected) { | |
171 return; | |
172 } | |
173 | |
174 JniInterface.nativeSendMouseEvent(x, y, whichButton, buttonDown); | |
175 } | |
176 | |
177 /** Injects a mouse-wheel event with delta values. Called on the UI thread. */ | |
178 public void sendMouseWheelEvent(int deltaX, int deltaY) { | |
179 if (!mConnected) { | |
180 return; | |
181 } | |
182 | |
183 JniInterface.nativeSendMouseWheelEvent(deltaX, deltaY); | |
184 } | |
185 | |
186 /** | |
187 * Presses or releases the specified (nonnegative) key. Called on the UI thr ead. If scanCode | |
joedow
2016/01/19 16:35:06
Does it make sense to check the keypress value to
Lambros
2016/01/29 23:58:26
I'm not sure why "nonnegative" appears here - I'll
| |
188 * is not zero then keyCode is ignored. | |
189 */ | |
190 public boolean sendKeyEvent(int scanCode, int keyCode, boolean keyDown) { | |
191 if (!mConnected) { | |
192 return false; | |
193 } | |
194 | |
195 return JniInterface.nativeSendKeyEvent(scanCode, keyCode, keyDown); | |
196 } | |
197 | |
198 /** Sends TextEvent to the host. Called on the UI thread. */ | |
199 public void sendTextEvent(String text) { | |
200 if (!mConnected) { | |
201 return; | |
202 } | |
203 | |
204 JniInterface.nativeSendTextEvent(text); | |
205 } | |
206 | |
207 /** Sends an array of TouchEvents to the host. Called on the UI thread. */ | |
208 public void sendTouchEvent(TouchEventData.EventType eventType, TouchEventDat a[] data) { | |
209 if (!mConnected) { | |
210 return; | |
211 } | |
212 | |
213 JniInterface.nativeSendTouchEvent(eventType.value(), data); | |
214 } | |
215 | |
216 /** | |
217 * Enables or disables the video channel. Called on the UI thread in respons e to Activity | |
218 * lifecycle events. | |
219 */ | |
220 public void enableVideoChannel(boolean enable) { | |
221 if (!mConnected) { | |
222 return; | |
223 } | |
224 | |
225 JniInterface.nativeEnableVideoChannel(enable); | |
226 } | |
227 | |
228 /** | |
229 * Sets the redraw callback to the provided functor. Provide a value of null whenever the | |
230 * window is no longer visible so that we don't continue to draw onto it. Ca lled on the UI | |
231 * thread. | |
232 */ | |
233 public void provideRedrawCallback(Runnable redrawCallback) { | |
234 mRedrawCallback = redrawCallback; | |
235 } | |
236 | |
237 /** Forces the native graphics thread to redraw to the canvas. Called on the UI thread. */ | |
238 public boolean redrawGraphics() { | |
239 if (!mConnected || mRedrawCallback == null) return false; | |
240 | |
241 JniInterface.nativeScheduleRedraw(); | |
242 return true; | |
243 } | |
244 | |
245 /** | |
246 * Called by native code to perform the redrawing callback. This is a no-op if the window isn't | |
247 * visible. Called on the graphics thread. | |
248 */ | |
249 void redrawGraphicsInternal() { | |
250 Runnable callback = mRedrawCallback; | |
251 if (callback != null) { | |
252 callback.run(); | |
253 } | |
254 } | |
255 | |
256 /** | |
257 * Returns a bitmap of the latest video frame. Called on the native graphics thread when | |
258 * DesktopView is repainted. | |
259 */ | |
260 public Bitmap getVideoFrame() { | |
261 if (Looper.myLooper() == Looper.getMainLooper()) { | |
262 Log.w(TAG, "Canvas being redrawn on UI thread"); | |
263 } | |
264 | |
265 synchronized (mFrameLock) { | |
266 return mFrameBitmap; | |
267 } | |
268 } | |
269 | |
270 /** | |
271 * Called by native code to set a new video frame. Called on the native grap hics thread when a | |
272 * new frame is allocated. | |
273 */ | |
274 void setVideoFrame(Bitmap bitmap) { | |
275 if (Looper.myLooper() == Looper.getMainLooper()) { | |
276 Log.w(TAG, "Video frame updated on UI thread"); | |
277 } | |
278 | |
279 synchronized (mFrameLock) { | |
280 mFrameBitmap = bitmap; | |
281 } | |
282 } | |
283 | |
284 /** | |
285 * Creates a new Bitmap to hold video frame pixels. Called by native code wh ich stores a global | |
286 * reference to the Bitmap and writes the decoded frame pixels to it. | |
287 */ | |
288 static Bitmap newBitmap(int width, int height) { | |
289 return Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); | |
290 } | |
291 | |
292 /** | |
293 * Called by native code to update the cursor shape. This is called on the g raphics thread when | |
294 * receiving a new cursor shape from the host. | |
295 */ | |
296 void updateCursorShape( | |
297 int width, int height, int hotspotX, int hotspotY, ByteBuffer buffer ) { | |
298 mCursorHotspot = new Point(hotspotX, hotspotY); | |
299 | |
300 int[] data = new int[width * height]; | |
301 buffer.order(ByteOrder.LITTLE_ENDIAN); | |
302 buffer.asIntBuffer().get(data, 0, data.length); | |
303 mCursorBitmap = Bitmap.createBitmap(data, width, height, Bitmap.Config.A RGB_8888); | |
304 } | |
305 | |
306 /** Position of cursor hotspot within cursor image. Called on the graphics t hread. */ | |
307 public Point getCursorHotspot() { | |
308 return mCursorHotspot; | |
309 } | |
310 | |
311 /** Returns the current cursor shape. Called on the graphics thread. */ | |
312 public Bitmap getCursorBitmap() { | |
313 return mCursorBitmap; | |
314 } | |
315 | |
316 // | |
317 // Third Party Authentication | |
318 // | |
319 | |
320 /** | |
321 * Called by native code, to pop up a third party login page to fetch the to ken required for | |
322 * authentication. | |
323 */ | |
324 void fetchThirdPartyToken(String tokenUrl, String clientId, String scope) { | |
325 mAuthenticator.fetchThirdPartyToken(tokenUrl, clientId, scope); | |
326 } | |
327 | |
328 /** | |
329 * Notify the native code to continue authentication with the |token| and th e |sharedSecret|. | |
330 */ | |
331 public void onThirdPartyTokenFetched(String token, String sharedSecret) { | |
332 if (!mConnected) { | |
333 return; | |
334 } | |
335 | |
336 JniInterface.nativeOnThirdPartyTokenFetched(token, sharedSecret); | |
337 } | |
338 | |
339 // | |
340 // Host and Client Capabilities | |
341 // | |
342 | |
343 /** | |
344 * Called by native code to set the list of negotiated capabilities between host and client. | |
345 * Called on the UI thread. | |
346 */ | |
347 void setCapabilities(String capabilities) { | |
348 mCapabilityManager.setNegotiatedCapabilities(capabilities); | |
349 } | |
350 | |
351 // | |
352 // Extension Message Handling | |
353 // | |
354 | |
355 /** | |
356 * Called by native code, to pass on the deconstructed ExtensionMessage to t he app. Called on | |
357 * the UI thread. | |
358 */ | |
359 void handleExtensionMessage(String type, String data) { | |
360 mCapabilityManager.onExtensionMessage(type, data); | |
361 } | |
362 | |
363 /** Sends an extension message to the Chromoting host. Called on the UI thre ad. */ | |
364 public void sendExtensionMessage(String type, String data) { | |
365 if (!mConnected) { | |
366 return; | |
367 } | |
368 | |
369 JniInterface.nativeSendExtensionMessage(type, data); | |
370 } | |
31 } | 371 } |
OLD | NEW |