|
1 | 1 | package com.iiordanov.bVNC;
|
2 | 2 |
|
| 3 | +import java.util.Collection; |
| 4 | +import java.util.HashMap; |
| 5 | +import java.util.Iterator; |
| 6 | + |
| 7 | +import android.app.PendingIntent; |
| 8 | +import android.content.BroadcastReceiver; |
3 | 9 | import android.content.Context;
|
| 10 | +import android.content.Intent; |
| 11 | +import android.content.IntentFilter; |
4 | 12 | import android.graphics.Bitmap;
|
| 13 | +import android.hardware.usb.UsbDevice; |
| 14 | +import android.hardware.usb.UsbDeviceConnection; |
| 15 | +import android.hardware.usb.UsbManager; |
5 | 16 | import android.os.Handler;
|
| 17 | +import android.os.SystemClock; |
| 18 | +import android.util.Log; |
6 | 19 |
|
7 | 20 | import com.freerdp.freerdpcore.services.LibFreeRDP.UIEventListener;
|
8 | 21 | import com.iiordanov.bVNC.input.RdpKeyboardMapper;
|
9 | 22 | import com.iiordanov.bVNC.input.RemoteKeyboard;
|
10 | 23 | import com.iiordanov.bVNC.input.RemoteSpicePointer;
|
| 24 | +import com.iiordanov.bVNC.Constants; |
11 | 25 | import com.gstreamer.*;
|
12 | 26 |
|
13 | 27 | public class SpiceCommunicator implements RfbConnectable, RdpKeyboardMapper.KeyProcessingListener {
|
14 | 28 | private final static String TAG = "SpiceCommunicator";
|
15 |
| - |
| 29 | + |
| 30 | + private HashMap<String, Integer> deviceToFdMap = new HashMap<String, Integer>(); |
| 31 | + UsbManager mUsbManager = null; |
| 32 | + private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() { |
| 33 | + public void onReceive(Context context, Intent intent) { |
| 34 | + String action = intent.getAction(); |
| 35 | + if (Constants.ACTION_USB_PERMISSION.equals(action)) { |
| 36 | + UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); |
| 37 | + if (device != null) { |
| 38 | + int vid = device.getVendorId(); |
| 39 | + int pid = device.getProductId(); |
| 40 | + String mapKey = Integer.toString(vid)+":"+Integer.toString(pid); |
| 41 | + synchronized (deviceToFdMap.get(mapKey)) { |
| 42 | + deviceToFdMap.get(mapKey).notify(); |
| 43 | + } |
| 44 | + } |
| 45 | + } |
| 46 | + } |
| 47 | + }; |
| 48 | + |
16 | 49 | public native int SpiceClientConnect (String ip, String port, String tport, String password, String ca_file, String cert_subj, boolean sound);
|
17 | 50 | public native void SpiceClientDisconnect ();
|
18 | 51 | public native void SpiceButtonEvent (int x, int y, int metaState, int pointerMask);
|
@@ -41,9 +74,16 @@ public class SpiceCommunicator implements RfbConnectable, RdpKeyboardMapper.KeyP
|
41 | 74 |
|
42 | 75 | boolean isInNormalProtocol = false;
|
43 | 76 |
|
44 |
| - private SpiceThread spicehread = null; |
| 77 | + private SpiceThread spicethread = null; |
| 78 | + private static SpiceCommunicator myself = null; |
| 79 | + private Context context; |
| 80 | + private boolean usbEnabled = true; |
45 | 81 |
|
46 | 82 | public SpiceCommunicator (Context context, RemoteCanvas canvas, ConnectionBean connection) {
|
| 83 | + myself = this; |
| 84 | + this.context = context; |
| 85 | + mUsbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE); |
| 86 | + |
47 | 87 | if (connection.getEnableSound()) {
|
48 | 88 | try {
|
49 | 89 | GStreamer.init(context);
|
@@ -71,13 +111,13 @@ public Handler getHandler() {
|
71 | 111 |
|
72 | 112 | public void connect(String ip, String port, String tport, String password, String cf, String cs, boolean sound) {
|
73 | 113 | //android.util.Log.e(TAG, ip + ", " + port + ", " + tport + ", " + password + ", " + cf + ", " + cs);
|
74 |
| - spicehread = new SpiceThread(ip, port, tport, password, cf, cs, sound); |
75 |
| - spicehread.start(); |
| 114 | + spicethread = new SpiceThread(ip, port, tport, password, cf, cs, sound); |
| 115 | + spicethread.start(); |
76 | 116 | }
|
77 | 117 |
|
78 | 118 | public void disconnect() {
|
79 | 119 | SpiceClientDisconnect();
|
80 |
| - try {spicehread.join(3000);} catch (InterruptedException e) {} |
| 120 | + try {spicethread.join(3000);} catch (InterruptedException e) {} |
81 | 121 | }
|
82 | 122 |
|
83 | 123 | class SpiceThread extends Thread {
|
@@ -116,6 +156,70 @@ public void sendKeyEvent (boolean keyDown, int virtualKeyCode) {
|
116 | 156 |
|
117 | 157 |
|
118 | 158 | /* Callbacks from jni */
|
| 159 | + |
| 160 | + public static int openUsbDevice(int vid, int pid) throws InterruptedException { |
| 161 | + Log.i(TAG, "Attempting to open a USB device and return a file descriptor."); |
| 162 | + |
| 163 | + if (Utils.isFree(myself.context) || !myself.usbEnabled || android.os.Build.VERSION.SDK_INT < 12) { |
| 164 | + return -1; |
| 165 | + } |
| 166 | + |
| 167 | + String mapKey = Integer.toString(vid)+":"+Integer.toString(pid); |
| 168 | + myself.deviceToFdMap.put(mapKey, 0); |
| 169 | + |
| 170 | + boolean deviceFound = false; |
| 171 | + UsbDevice device = null; |
| 172 | + HashMap<String, UsbDevice> stringDeviceMap = null; |
| 173 | + int timeout = Constants.usbDeviceTimeout; |
| 174 | + while (!deviceFound && timeout > 0) { |
| 175 | + stringDeviceMap = myself.mUsbManager.getDeviceList(); |
| 176 | + Collection<UsbDevice> usbDevices = stringDeviceMap.values(); |
| 177 | + |
| 178 | + Iterator<UsbDevice> usbDeviceIter = usbDevices.iterator(); |
| 179 | + while (usbDeviceIter.hasNext()) { |
| 180 | + UsbDevice ud = usbDeviceIter.next(); |
| 181 | + Log.i(TAG, "DEVICE: " + ud.toString()); |
| 182 | + if (ud.getVendorId() == vid && ud.getProductId() == pid) { |
| 183 | + Log.i(TAG, "USB device successfully matched."); |
| 184 | + deviceFound = true; |
| 185 | + device = ud; |
| 186 | + break; |
| 187 | + } |
| 188 | + } |
| 189 | + timeout -= 100; |
| 190 | + SystemClock.sleep(100); |
| 191 | + } |
| 192 | + |
| 193 | + int fd = -1; |
| 194 | + // If the device was located in the Java layer, we try to open it, and failing that |
| 195 | + // we request permission and wait for it to be granted or denied, or for a timeout to occur. |
| 196 | + if (device != null) { |
| 197 | + UsbDeviceConnection deviceConnection = myself.mUsbManager.openDevice(device); |
| 198 | + if (deviceConnection != null) { |
| 199 | + fd = deviceConnection.getFileDescriptor(); |
| 200 | + } else { |
| 201 | + // Request permission to access the device. |
| 202 | + synchronized (myself.deviceToFdMap.get(mapKey)) { |
| 203 | + PendingIntent mPermissionIntent = PendingIntent.getBroadcast(myself.context, 0, new Intent(Constants.ACTION_USB_PERMISSION), 0); |
| 204 | + |
| 205 | + // TODO: Try putting this intent filter into the activity in the manifest file. |
| 206 | + IntentFilter filter = new IntentFilter(Constants.ACTION_USB_PERMISSION); |
| 207 | + myself.context.registerReceiver(myself.mUsbReceiver, filter); |
| 208 | + |
| 209 | + myself.mUsbManager.requestPermission(device, mPermissionIntent); |
| 210 | + // Wait for permission with a timeout. |
| 211 | + myself.deviceToFdMap.get(mapKey).wait(Constants.usbDevicePermissionTimeout); |
| 212 | + |
| 213 | + deviceConnection = myself.mUsbManager.openDevice(device); |
| 214 | + if (deviceConnection != null) { |
| 215 | + fd = deviceConnection.getFileDescriptor(); |
| 216 | + } |
| 217 | + } |
| 218 | + } |
| 219 | + } |
| 220 | + return fd; |
| 221 | + } |
| 222 | + |
119 | 223 | private static void OnSettingsChanged(int inst, int width, int height, int bpp) {
|
120 | 224 | if (uiEventListener != null)
|
121 | 225 | uiEventListener.OnSettingsChanged(width, height, bpp);
|
|
0 commit comments