Full text
VNC++, control remoto desde Android TRABAJO FIN DE GRADO CURSO 2012-2013 ´ Oscar Crespo Salazar Gorka Jimeno Garrach´on Luis Valero Mart´ın Director Antonio Sarasa Cabezuelo Departamento de Sistemas Inform´aticos y Computaci´on Facultad de Inform´atica Universidad Complutense de Madrid Madrid, Junio de 2013
iii Licencias c 2013 All Rights Reserved. Esta obra est´a bajo una Licencia Creative Commons Atribuci´on-CompartirIgual 3.0 Unported.
v Agradecimientos Agradecemos a Johannew Shindelin por la completa librer´ıa para VNC que cre´o y adem´as distribuirla de forma libre. Y por supuesto por su inestimable y desinteresada ayuda en el Mailing-list de la propia librer´ıa, tanto de ´el como de Christian Beier. Tambi´en agradecer a la web stackoverflow http://stackoverflow.com/, concretamente la secci´on de Android, por su certera ayuda con esas inevitables dudas que surgen al desarrollar c´odigo desde cero. Tambi´en, como no, agradecer a los desarrolladores de Google, que a trav´es de su web http://developer.android.com/index.html y ciertos ejemplos clave, han permitido culminar este proyecto. Menci´on especial para Axel Amigo, buen amigo nuestro, que nos ayud´o poniendo voz al v´ıdeo explicativo del proyecto, a Ra´ul Crespo, por hacer el montaje que tanto nos ha satisfecho a todos, y por supuesto, a Sergio Barrasa por el fant´astico logo realizado para la aplicaci´on. Por ´ultimo agradecer a Antonio Sarasa Cabezuelo, director del proyecto, por la confianza depositada en nosotros y su inestimable ayuda durante todo el proceso. Tambi´en agradecemos a todos los profesores de la Facultad de Inform´atica de la Universidad Complutense de Madrid por proporcionarnos durante todos estos a˜nos los conocimientos necesarios para el desarrollo de este proyecto.
vii Abstract Nowadays there are a lot of VNC based applications for Android. Nonetheless, none of these applications are Free Software and are implemented using the NDK. The point of this project is to use the NDK potential to make a VNC based application and try to obtain a better performance than the other applications implemented using only the SDK and all the source code will be Free Software. The protocol to comunicate with a VNC server is called RFB. To implementing this protocol in this project has been used the libVNCServer library. This library facilitates the control of RFB. Without libVNCServer it would be more difficult to make our own library to manage the protocol. JNI is used as the framework to comunicate the native code with Java. The technologies, as well as the process used to built this software is going to be explained along this report. Keywords VNC, Android, C++, C, NDK, SDK, JNI, RFB.
ix Resumen Hoy en d´ıa hay una gran cantidad de aplicaciones basadas en VNC para Android. Sin embargo, ninguna de estas aplicaciones son Free Software y se implementan utilizando el NDK. El objetivo de este proyecto es utilizar el potencial NDK para hacer una aplicaci´on basada en VNC y tratar de obtener un mejor rendimiento que el resto de las aplicaciones implementadas utilizando s´olo el SDK y todo el c´odigo fuente ser´a Free Software. El protocolo para comunicarse con un servidor VNC se llama RFB. Para la implementaci´on de este protocolo en este proyecto se ha utilizado la librer´ıa libVNCServer. Esta librer´ıa facilita el control de RFB. Sin libVNCServer habr´ıa sido m´as dif´ıcil hacer nuestra propia librer´ıa para gestionar el protocolo. JNI se utiliza como framework para comunicar el c´odigo nativo con Java. Las tecnolog´ıas, as´ı como el proceso utilizado para construir este software se explicar´a a lo largo de esta memoria. Palabras clave VNC, Android, C++, C, NDK, SDK, JNI, RFB.
2
Cap´ıtulo 1 Introducci´on El desarrollo de las redes de telecomunicaciones ha permitido que progresivamente el control remoto de un ordenador pase de meras terminales de texto a los escritorios remotos de hoy en d´ıa. La tecnolog´ıa de escritorio remoto permite la centralizaci´on de aplicaciones que normalmente se ejecutar´ıan en el entorno de usuario. Gracias a esta tecnolog´ıa el entorno de usuario (cliente) se convierte en una simple terminal de entrada/salida. Todos los eventos de pulsaci´on de teclas y movimientos del rat´on se transmiten desde el cliente a un servidor donde la aplicaci´on los procesa como si se tratasen de eventos locales. La imagen de la pantalla se devuelve al cliente y de esta manera se puede controlar desde el terminal cliente la m´aquina donde se encuentra la aplicaci´on servidor. Este proyecto se centra ´unicamente en la realizaci´on de una aplicaci´on cliente para Android. De tal manera que la aplicaci´on servidor no est´a desarrollada en este proyecto. Aunque m´as adelante se profundizar´a en cuales son los objetivos concretos de este proyecto, conviene aclarar que el objetivo era desarrollar un cliente VNC para Android, que utilizar´a el NDK y que fuera de software libre, ya que en la actualidad no existe ninguno que cumpla estos dos requisitos. Existen diferentes protocolos de comunicaci´on entre la aplicaci´on cliente y la aplicaci´on servidor. Este proyecto se ha desarrollado utilizando el protocolo de comunicaci´on Remote Frame Buffer (RFB), utilizado por todos los clientes y servidores VNC. De esta manera la aplicaci´on ser´a compatible con cualquier servidor VNC instalado en cualquier terminal. Antes de profundizar m´as en c´omo se han afrontado los objetivos del proyecto conviene establecer ciertos conocimientos b´asicos relativos al dominio de estas aplicaciones. Comenzaremos explicando algunos conceptos que se han utilizado antes, tales como VNC, RFB, Android, etc. 1.1. Conceptos 1.1.1. Escritorio remoto Un escritorio remoto es una tecnolog´ıa que permite a un usuario controlar desde un terminal gr´afico (cliente) otro terminal gr´afico (servidor). X-Window fue el primer escritorio remoto desarrollado por el Massachusetts Institute of Technology (MIT) en la d´ecada de los ochenta [1]. 3
4Cap´ ıtulo 1. Introducci´ on 1.1.2. VNC VNC son las siglas en ingl´es de Virtual Network Computing [2]. Es un sistema de escritorio remoto que utiliza el protocolo Remote Frame Buffer (RFB) para controlar remotamente otra m´aquina. Est´a basado en una arquitectura cliente-servidor, donde la m´aquina con la aplicaci´on cliente es la que controla a la m´aquina con la aplicaci´on servidor. VNC transmite los eventos de rat´on y teclado desde el cliente al servidor y ´este devuelve las actualizaciones de pantalla en la otra direcci´on a trav´es de una red de comunicaciones. VNC es independiente del sistema operativo, es decir, un cliente VNC ejecutado en una m´aquina Windows podr´ıa controlar un servidor ejecutado en una m´aquina Linux. En definitiva, se podr´ıa controlar cualquier sistema operativo desde cualquier sistema operativo siempre que ambos sean compatibles con VNC. 1.1.3. RFB RFB son las siglas en ingl´es de Remote Frame Buffer. Es un protocolo de comunicaci´on para el acceso remoto a interfaces gr´aficas de usuario. Como trabaja a nivel de framebuffer es aplicable a todos los sistemas de ventanas (X11, Windows, Macintosh). RFB es el protocolo usado en VNC [3]. 1.1.4. Android Android es un sistema operativo basado en Linux, dise˜nado principalmente para Smartphones y Tablets. Inicialmente fue desarrollado por Android Inc, pero finalmente Google lo compr´o en 2005. Fue presentado en 2007 junto con la fundaci´on Open Handset Alliance y el primer m´ovil con sistema operativo Android sali´o al mercado en 2008. La mayor´ıa del c´odigo de Android est´a liberado bajo una licencia Apache, licencia libre y de c´odigo abierto. No obstante no est´a liberado completamente, por lo tanto no se puede decir que Android sea un sistema operativo libre. Uno de sus puntos fuertes es la facilidad para desarrollar aplicaciones gracias a las herramientas que proporciona Google. Estas aplicaciones normalmente est´an escritas en Java y se desarrollan utilizando el Android SDK, pero tambi´en se pueden construir aplicaciones escritas en C/C++ usando el Android NDK [4]. 1.1.5. Android SDK SDK son las siglas en ingl´es de Software Development Kit. El SDK proporcina las APIs y herramientas de desarrollo necesarias para construir, testear y depurar aplicaciones para Android [5]. 1.1.6. Android NDK NDK son las siglas en ingl´es de Native Development Kit. El NDK es un conjunto de herramientas que permiten desarrollar parte de la aplicaci´on para Android con lenguajes de c´odigo nativo como C o C++ [6].
1.1. Conceptos 5 1.1.7. JNI JNI son las siglas en ingl´es de Java Native Interface. Es un framework de programaci´on que permite a c´odigo Java llamar y ser llamado desde apliaciones nativas y librerias escritas en otro lenguajes como pueden ser C, C++ o ensamblador [7]. JNI proporciona todo lo necesario para que la comunicaci´on entre el SDK y el NDK sea posible. Gracias a ´el es posible construir aplicaciones en Android utilizando c´odigo nativo. 1.1.8. Tight encoding Tight es un tipo de codificaci´on m´as compactada, es muy eficiente y est´a optimizado para conexiones con poco ancho de banda. Tight incluye tanto la eficiencia de compresi´on sin p´erdidas, como opcionales formas de compresi´on con perdidas para JPEG [8]. 1.1.9. ZRLE encoding ZRLE, es un tipo de codificaci´on compacta y eficiente. Est´a optimizado para conexiones con poco ancho de banda y utiliza la librer´ıa ZLib para comprimir los datos. 1.1.10. Git Es un software de control de versiones dise˜nado por Linux torvals[9][10]. Se centra en la velocidad y la eficiencia. Inicialmente fue construido para el desarrollo del Kernel de Linux pero desde entonces cada vez m´as proyectos lo han ido adoptando. 1.1.11. WireShark WireShark es un software analizador de protocolo de red (sniffer) licenciado bajo GPLv2 [11]. Es uno de los sniffer m´as importantes y completos, en ´el contribuyen expertos en redes de todo el mundo. 1.1.12. Free Software Free Software del ingl´es software libre. Es el software que permite al usuario compartir, estudiar y modificar [12]. Todo sotware libre debe garantizar las libertades de usar el programa con cualquier prop´osito, estudiar y modificar el programa, distribuir copias del programa y, por ´ultimo, la libertad de hacer p´ublicas las modificaciones que se hayan realizado. 1.1.13. GPL GPL son las siglas en ingl´es de General Public License [13]. Es una licencia de c´odigo libre redactada por la Free Software Fundation. El c´odigo bajo esta licencia es c´odigo libre ya que esta licencia garantiza las cuatro libertades de ´este, que son: usar el programa con cualquier prop´osito, estudiar y modificar el programa, distribuir copias del programa y por ´ultimo la libertad de hacer p´ublicas las modificaciones que se hayan realizado.
6Cap´ ıtulo 1. Introducci´ on 1.2. Motivaciones La motivaci´on principal para la realizaci´on de este proyecto era el desarrollo de algo nuevo. As´ı, junto con nuestro inter´es por el desarrollo de aplicaciones Android usando c´odigo nativo, nos dimos cuenta de que no exist´ıa ninguna aplicaci´on de escritorio remoto que usase esta tecnolog´ıa. La realizaci´on de este proyecto supon´ıa un desaf´ıo de cara a la interconexi´on entre el c´odigo en C++ y Java ya que para ello fue necesario aprender a hacer compilaciones cruzadas, a parte de tambi´en aprender a utilizar JNI. 1.3. Objetivos Los objetivos de este proyecto son principalmente dos, el desarrollo de una aplicaci´on de escritorio remoto para Android usando el NDK y que dicha aplicaci´on en su totalidad sea software libre. El objetivo de realizar la aplicaci´on no era s´olo el hecho de construir una aplicaci´on que funcionase correctamente. La aplicaci´on ten´ıa que ser usable y eficiente. Esto ´ultimo lo hemos logrado, ya que gracias al uso de c´odigo nativo se ha conseguido que la aplicaci´on, no s´olo sea eficiente si no que tiene niveles de eficiencia comparables con los de la aplicaci´on oficial de los creadores del protocolo. En cuanto a la usabilidad, la aplicaci´on proporciona muchas opciones como, por ejemplo, establecer conexiones cifradas y enviar combinaciones de teclas. En cuanto al software libre, todo el c´odigo ha sido licenciado bajo una licencia GPL versi´on 3, por lo tanto el software de la aplicaci´on garantiza las cuatro libertades que todo software debe tener para ser considerado como tal. Consideramos importante y por eso est´a en esta secci´on de objetivos, el car´acter libre del software, ya que el software libre supone un avance muy importante con respecto al mundo del software privativo, creando una comunidad de usuarios y programadores que interact´uan entre s´ı mejorando el desarrollo del software.
Cap´ıtulo 2 Estado del arte Como referencias actuales para realizar este proyecto, y obtener ideas de aplicaciones enfocadas en el mismo campo, se han utilizado los siguientes clientes para Android: 2.1. VNC Viewer(RealVNC) VNC Viewer(RealVNC)[14] perteneciente a la compa˜n´ıa RealVNC. Inicialmente VNC surgi´o en el Olivetti & Oracle Research Lab (ORL3), siendo desarrollado por Tristan Richardson (inventor), Andy Harter (director del proyecto), Quentin Stanfford-Fraser y James Weatherall. Unos a˜nos despu´es, varios de estos desarrolladores crear´ıan RealVNC con la idea de seguir con este proyecto software. Al no ser una aplicaci´on de c´odigo abierto, es dif´ıcil saber el tipo de implementaci´on que lleva a cabo. Posee una interfaz gr´afica muy usable y un rendimiento ´optimo. Para el control del ordenador utiliza la pantalla a modo de touchpad, esto es, el movimientos en la pantalla son los movimientos que har´a el rat´on. Esto proporciona un manejo lento pero extremadamente preciso. Por otro lado RealVNC no almacena las constrase˜nas sino que pregunta al usuario por ella cuando conecta a un servidor que la requiera. Ha sido una de las aplicaciones de referencia, tanto por su interfaz como por su ´optimo rendimiento. Como ya se ha dicho con anterioridad es de c´odigo cerrado, as´ı que fue imposible trabajar sobre ella. 7
8Cap´ ıtulo 2. Estado del arte 2.2. Android-vnc-viewer(AndroidVNC) Android-vnc-viewer(AndroidVNC)[16] es un programa libre y de c´odigo abierto de control remoto para dispositivos Android. Es un proyecto desarrollado a partir de una rama inicial de otra aplicaci´on llamada tightVNC. Esta bajo licencia GPLv2 y tiene un repositorio activo en google http://code.google.com/p/android-vnc-viewer/. Posee una interfaz gr´afica pobre y una gesti´on de conexiones poco clara. Para el control remoto del ordenador ofrece una amplio abanico de posibilidades cubriendo cualquier posibilidad al respecto. En el apartado de la seguridad AndroidVNC almcena las contrase˜nas en la Base de Datos sin cifrar siendo vulnerable a ataques, Al ser una aplicaci´on de sofware libre hemos podio ver y aprender mucho de su c´odigo y ha sido una de las aplicaciones de referencia ya que ofrece un rendimiento muy bueno. Nos fue imposible trabajar sobre ´el ya que est´a construida totalmente sobre SDK y nuestro objetivo es utilizar la potencia del NDK. 2.3. MultiVNC MultiVNC[15] es un proyecto de software libre bajo licencia GPLv2, basado en androidvnc-viewer y desarrollado por Christian Beier. Incluye soporte para varias codificaciones, as´ı como el uso de Zeroconf (Zero Configuration Networking) para ayudar a crear de forma autom´atica una red IP sin necesidad de configuraciones o servidores especiales. Al igual que AndroidVNC posee una interfaz pobre y, aunque mejor, una gesti´on de conexiones no demasiado claro. Para la representaci´on utiliza aceleraci´on por hardware mediante GPU. Respecto al control utiliza un sistema similar a RealVNC. En el aspecto de las contrase˜na presenta un funcionamiento semejante a AndroidVNC. Esta aplicaci´on ha tenido una relevancia menor en el proyecto ya que no nos convenc´ıa su rendimiento en m´oviles de baja gama, se ha usado para observar como resolv´ıan ciertos aspectos y como comparativa de rendimiento.
Cap´ıtulo 3 Entorno de desarrollo y banco de pruebas 3.1. Entorno de desarrollo Todo el proyecto ha sido desarrollado bajo el Sistema operativo Linux Mint Debian Edition 64 bits, bajo las opciones de escritorio tanto Mate como Xfce. La principal herramienta de desarrollo utilizada ha sido Eclipse Juno, con los entornos de desarrollo integrados tanto para C/C++ como para Java, ya que el proyecto ha necesitado de estos lenguajes. JDK: versi´on 1.6 64 bits. SDK: versi´on android-sdk-r21.1-linux. NDK: versi´on actual android-ndk-r8d, aunque a lo largo del proyecto fue necesario actualizar a esta versi´on m´as reciente. Android: versi´on m´ınima de desarrollo Android 4.0 (API 14). Aunque en un inicio se opt´o por desarrollar para Android 2.3.3 (API 10) por tener mayor cuota de mercado, nos dimos cuenta al ir desarrollando que la 4.0 nos dar´ıa mayores beneficios futuros. Git: sistema de control de versiones para el trabajo en equipo. Egit: plugin de Eclipse usado para interactuar con el repositorio p´ublico del proyecto entre los distintos integrantes. Doxygen: plugin que genera la documentaci´on del c´odigo final, ya que consideramos m´as completo que el javadoc de serie. ADT: Android Developers Tools versi´on 22.0.1, plugin de Eclipse para el desarrollo de aplicaciones Android. LaTeX: herramienta usada para la composici´on del texto de la memoria. Gracias estas herramientas hemos podido trabajar sobre un entorno unificado, ya que eclipse daba todas las posibilidades sin salir de ´el. Por un lado permit´ıa escribir el c´odigo en Java y C/C++ desde la misma herramienta, as´ı mismo, permit´ıa el desarrollo sobre Android gracias al plugin ADT y la gesti´on del c´odigo mediante Git de una forma sencilla gracias a Egit. 9
10 Cap´ ıtulo 3. Entorno de desarrollo y banco de pruebas 3.2. Banco de pruebas Las pruebas de depuraci´on de la aplicaci´on se han realizado en dos terminales m´oviles con distintas resoluciones y especificaciones generales para observar el impacto tanto en terminales de gama media/baja, como terminales de alta gama. Samsung Galaxy S3 con Android 4.2.2 Jelly Bean. Samsung Galaxy Mini 2 con Android 4.1.2 Jelly Bean. Como servidores VNC se han usado: x11vnc, con codificaci´on SSL y sin ella. RealVNC, con clave y sin ella.
Cap´ıtulo 4 Tecnolog´ıas fallidas En este apartado se describir´an algunas de las tecnolog´ıas que se pensaron usar en un principio y que por un motivo u otro al final se desecharon. 4.1. Simple DirectMedia Layer Simple DirectMedia Layer [17][18], o m´as com´unmente conocida por sus siglas SDL, es una librer´ıa desarrollada en C y licenciada y distribuida bajo licencia LGPL, proporciona una serie de funciones para la gesti´on de dibujos e im´agenes en dos dimensiones. Escrita originalmente por Sam Latinga. Durante el proceso de desarrollo del proyecto, una de las tecnolog´ıas por las que se optaron fue SDL, dada las facilidades y potencia que ofrec´ıa para el tratamiento y renderizado de la imagen. Sin embargo, durante la inclusi´on del mismo en el proyecto surgieron dos problemas que nos decidieron a abandonarlo. El primer motivo fue que la ´unica versi´on continuada y con soporte para Android era SDL 2.0, la cual se encontraba en fase de desarrollo y las funciones principales pod´ıan cambiar en mitad del desarrollo. Exist´ıa una versi´on no oficial y descontinuada de la misma, que era SDL 1.2, pero entre los errores conocidos estaban el uso del modo v´ıdeo, que era esencial en el proyecto, y dado que estaba descontinuado, nunca se hubiera arreglado. Por otro lado, el rendimiento obtenido, una vez acoplado al proyecto, fue muy pobre, llegando al caso de que el servidor expulsaba la conexi´on. Adem´as dado el funcionamiento de SDL, se dibujaba s´olo la parte que se ve´ıa de la pantalla, la usabilidad al moverse por la misma, teniendo que redibujar continuamente la imagen, hizo inviable su uso. 11
18 Cap´ ıtulo 5. Tecnolog´ ıas usadas Recorrido del vector: Aunque la representaci´on de la informaci´on es un vector lineal, en realidad el recorrido del mismo se realiza como si de un vector bidimensional se tratase. De tal forma que para pasar de una fila a otra se har´ıa: Anchura*bpp En cualquier caso, nos encontrar´ıamos con un recorrido del orden O(n2) (coste cuadr´atico). Para recoger la informaci´on de la imagen, se recorre el rect´angulo que ha sufrido cambios y se copia directamente en el vector final. Para hacer la copia se usa la funci´on de C memcpy, dada su eficiencia copiando bloques de bytes. 5.2.3. Canvas para la representaci´on Para la representaci´on de la imagen se ha usado Canvas para Android, usando sus funciones de dibujado de Bitmap. Los dos m´etodos de Canvas usados para dibujar el bitmap son: drawBitmap ( i n t [ ] c olo rs , i nt o f f s e t , i n t s tr i de , int x , int y , int width , int height , boolean hasAlpha , Paint paint ) Mediante este m´etodo es posible dibujar un bitmap a partir de su informaci´on de p´ıxeles, almacenada en colors, se usa para representar los datos de la imagen seg´un se modifican. drawBitmap ( Bitmap bitmap , Rect src , RectF dst , Paint paint ) Mediante ´este dibujamos un bitmap, pas´andole el bitmap ya creado como par´ametro. Cada uno de los m´etodos anteriores es usado en dos situaciones y contexto distintos, es necesario usar ambos, como veremos m´as adelante, para una ´optima representaci´on. Los dos contextos distintos a los que hace frente la aplicaci´on son: Estado normal: sucede siempre que hay una actualizaci´on en la imagen y siempre y cuando el usuario no se est´e desplazando por la pantalla o haciendo zoom. En este caso la imagen se representar´a mediante su vector de p´ıxeles. El usuario se mueve por la imagen o hace zoom: cuando el usuario se desplaza a trav´es de la imagen o hace zoom, es necesario crear un bitmap con la informaci´on actual de la imagen, ya que en caso contrario, obtendr´ıamos un p´esimo rendimiento por dos motivos: •Si us´asemos la representaci´on a partir de su informaci´on de p´ıxeles, en cada progresi´on por la imagen har´ıa falta redibujar la imagen, consiguiendo un movimiento o zoom lento. •Como estar´ıamos continuamente redibujando la imagen, y adem´as, en cada representaci´on Android crear´ıa un bitmap temporal, provocar´ıamos un alto consumo de CPU.
5.2. Aplicaci´ on 19 De tal forma, al crear un bitmap, el movimiento por la imagen es fluido. El ´unico punto a considerar es la optimizaci´on de cuando crear y destruir este bitmap, ya que la creaci´on de bitmaps es un proceso pesado y que requiere de muchos recursos. As´ı pues los puntos en los que se destruye o se marca para destruir el bitmap son: •Cuando el usuario haga click sobre la imagen, en este punto se considera que el usuario intenta interactuar con el servidor, por lo tanto, es necesario destruir el bitmap y mantener actualizada la imagen en todo momento. •En el caso de que el usuario deje de moverse por la pantalla, al menos, durante medio segundo o deje de hacer zoom. En este caso es conveniente destruir la imagen en pos de conseguir una imagen actualizada del servidor. El motivo por el cual se espera medio segundo, es con el fin de no interrumpir el movimiento del usuario, es decir, si se destruyese el bitmap en el momento que levanta el dedo de la pantalla, al volver a moverse se producir´ıa una lentitud hasta que se volviese a crear el nuevo bitmap. Adem´as as´ı, optimizamos la creaci´on de bitmap y reducimos el uso de CPU. El problema que encontramos frente a otras soluciones, a la hora de actualizar la imagen, es el hecho de que la parte nativa (C++) actualiza los datos de la imagen directamente en el vector, si por el contrario, todo se hubiese desarrollado en Java, se podr´ıa haber actualizado la informaci´on sobre un bitmap ya creado, de tal forma que se ahorrar´ıa el consumo de CPU derivado de la creaci´on, aunque se perder´ıa rendimiento en la actualizaci´on.
20 Cap´ ıtulo 5. Tecnolog´ ıas usadas
Cap´ıtulo 6 Especificaciones En este cap´ıtulo se describe la construcci´on de la aplicaci´on. Primero se expondr´an las especificaciones de requisitos tanto externos, como funcionales. Despu´es en el siguiente apartado se explicar´an los diagramas UML del c´odigo, junto con explicaciones de todo el flujo del programa. 6.1. Especificaci´on de requisitos Los requisitos de la aplicaci´on son: 6.1.1. Interfaces externos En esta secci´on se indican los requisitos externos a la aplicaci´on. Interfaces de usuario: La interfaz de uso proporciona todo lo necesario para controlar la aplicaci´on en su totalidad. Interfaces de hardware: Se debe disponer de un dispositivo m´ovil y un ordenador. Interfaces de software: La versi´on de Android del dispositivo m´ovil debe ser la 4.0 o superior. En la m´aquina remota se debe disponer de una aplicaci´on VNC de tipo servidor. Interfaces de comunicaci´on: Tanto el cliente como el servidor deben disponer de una conexi´on a Internet o estar ambos en la misma red local. 21
22 Cap´ ıtulo 6. Especificaciones 6.1.2. Funciones Los siguientes requisitos funcionales definen las acciones que deben tener lugar en el software procesando las entradas y generando las salidas. Crear conexi´on: Prioridad: Alta. Estabilidad: Alta. Descripci´on: Crea una nueva conexi´on y la a˜nade a la lista de conexiones. Entrada: Nombre de la conexi´on, IP, puerto, contrase˜na y calidad de imagen. Salida: La conexi´on se a˜nade a la lista y se conecta. Origen: Usuario. Destino: Aplicaci´on. Necesita: Acceso a la Base de Datos. Acci´on: Crea una nueva conexi´on. Precondici´on: Nombre de conexi´on no repetida, IP v´alida y puerto en un rango v´alido. Postcondici´on: La conexi´on se a˜nade a la lista de conexiones. Efectos laterales: N/A. Editar conexi´on: Prioridad: Media. Estabilidad: Alta. Descripci´on: Se editan los datos de una determinada conexi´on. Entrada: Nombre de la conexi´on, IP, puerto, contrase˜na y calidad de imagen. Salida: La conexi´on con los datos modificados. Origen: Usuario. Destino: Aplicaci´on. Necesita: Acceso a la Base de Datos. Acci´on: Edita los datos de una conexi´on. Precondici´on: IP v´alida y puerto en un rango v´alido. Postcondici´on: Los datos de la conexi´on se modifican. Efectos laterales: N/A.
6.1. Especificaci´ on de requisitos 23 Borrar conexi´on: Prioridad: Media. Estabilidad: Alta. Descripci´on: Se elimina una conexi´on. Entrada: Evento usuario. Salida: La conexi´on se elimina de la aplicaci´on. Origen: Usuario. Destino: Aplicaci´on. Necesita: Acceso a la Base de Datos. Acci´on: Elimina una conexi´on. Precondici´on: N/A. Postcondici´on: La conexi´on se borra de la aplicaci´on. Efectos laterales: Si la conexi´on est´a en favoritos se borrar´a tambi´en ah´ı. Ver informaci´on de la conexi´on: Prioridad: Baja. Estabilidad: Alta. Descripci´on: Muestra la informaci´on de una conexi´on. Entrada: Evento usuario. Salida: Los datos de la conexi´on. Origen: Usuario. Destino: Usuario. Necesita: Acceso a la Base de Datos. Acci´on: Muestra los datos de una conexi´on. Precondici´on: N/A Postcondici´on: Se muestran los datos de una conexi´on. Efectos laterales: N/A.
24 Cap´ ıtulo 6. Especificaciones A˜nadir conexi´on a favoritos: Prioridad: Baja. Estabilidad: Alta. Descripci´on: A˜nade una conexi´on a la lista de favoritos del usuario. Entrada: Evento usuario. Salida: La conexi´on se a˜nade a la lista de favoritos. Origen: Usuario. Destino: Aplicaci´on. Necesita: Acceso a la base de datos. Acci´on: A˜nade la conexi´on a la lista de favoritos. Precondici´on: N/A. Postcondici´on: La conexi´on se a˜nade a favoritos. Efectos laterales: N/A. Borrar conexi´on de favoritos: Prioridad: Baja. Estabilidad: Alta. Descripci´on: Borra una conexi´on de la lista de favoritos. Entrada: Evento usuario. Salida: La conexi´on se elimina de favoritos. Origen: Usuario. Destino: Aplicaci´on. Necesita: Acceso a la base de datos. Acci´on: Elimina una conexi´on de la lista de favoritos. Precondici´on: N/A. Postcondici´on: La conexi´on se borra de favoritos. Efectos laterales: N/A.
6.1. Especificaci´ on de requisitos 25 Ver lista de conexiones: Prioridad: Alta. Estabilidad: Alta. Descripci´on: Se muestran todas las conexiones recientes. Entrada: Evento usuario. Salida: La lista de conexiones recientes. Origen: Usuario. Destino: Usuario. Necesita: Acceso a la base de datos. Acci´on: Se muestran las conexiones recientes. Precondici´on: N/A Postcondici´on: Se muestra la lista de conexiones recientes. Efectos laterales: N/A. Ver lista de conexiones favoritas: Prioridad: Baja. Estabilidad: Alta. Descripci´on: Se muestran todas las conexiones favoritas. Entrada: Evento usuario. Salida: La lista de conexiones favoritas. Origen: Usuario. Destino: Usuario. Necesita: Acceso a la base de datos. Acci´on: Se muestran las conexiones favoritas. Precondici´on: N/A Postcondici´on: Se muestra la lista de conexiones favoritas. Efectos laterales: N/A.
26 Cap´ ıtulo 6. Especificaciones Abrir men´u principal: Prioridad: Baja. Estabilidad: Alta. Descripci´on: Se abre el men´u principal. Entrada: Evento usuario. Salida: Se muestra el men´u. Origen: Usuario. Destino: Usuario. Necesita: N/A. Acci´on: Se muestra el men´u principal. Precondici´on: N/A Postcondici´on: Se muestra el men´u principal. Efectos laterales: N/A Conectarse: Prioridad: Alta. Estabilidad: Alta. Descripci´on: Se conecta al servidor especificado en los datos de la conexi´on. Entrada: Datos de la conexi´on y evento usuario. Salida: Conexi´on con el servidor. Origen: Usuario. Destino: Usuario. Necesita: N/A. Acci´on: Se conecta con el servidor remoto. Precondici´on: El servidor tiene que tener ejecut´andose una aplicaci´on VNC. Postcondici´on: El cliente se conecta con el servidor. Efectos laterales: N/A.
6.1. Especificaci´ on de requisitos 27 Desconectarse: Prioridad: Alta. Estabilidad: Alta. Descripci´on: Se desconecta del servidor al que se estaba conectado. Entrada: Evento usuario o desconexi´on del servidor Salida: Desconexi´on con el servidor. Origen: Usuario o servidor. Destino: Usuario. Necesita: Estar conectado. Acci´on: Se desconecta del servidor remoto. Precondici´on: Se debe estar conectado para poder desconectarse. Postcondici´on: El cliente se desconecta del servidor. Efectos laterales: N/A. Hacer click: Prioridad: Alta. Estabilidad: Alta. Descripci´on: Se env´ıa la orden de pulsar el bot´on izquierdo del rat´on. Entrada: Evento usuario. Salida: N/A. Origen: Usuario. Destino: Aplicaci´on. Necesita: Estar conectado. Acci´on: Se env´ıa la orden de hacer click. Precondici´on: Se debe estar conectado. Postcondici´on: Se env´ıa la orden de hacer click. Efectos laterales: N/A.
34 Cap´ ıtulo 6. Especificaciones es.farfuteam.vncpp.controller.ListFragmentTab + ListFragmentTab() + onCreateView(inflater : LayoutInflater, container : ViewGroup, savedInstanceState : Bundle) : View + onStart() + onListItemClick(listView : ListView, view : View, position : int, id : long) + deleteUser() + editingUser() + refreshFavorites(user : es.farfuteam.vncpp.model.sql.Connection) + getO() : Object + setO(o : Object) + getUserList() : ArrayList<Connection> + setUserList(userList : ArrayList<Connection>) es.farfuteam.vncpp.controller.ListFragmentTabFav + ListFragmentTabFav() + onCreateView(inflater : LayoutInflater, container : ViewGroup, savedInstanceState : Bundle) : View + onStart() + onListItemClick(listView : ListView, view : View, position : int, id : long) + deleteUser() + editingUser() + getO() : Object + setO(o : Object) + getUserList() : ArrayList<Connection> + setUserList(userList : ArrayList<Connection>) es.farfuteam.vncpp.view.Adapter + Adapter(act : Activity, u : ArrayList<Connection>) + getCount() : int + getItem(position : int) : Object + getItemId(position : int) : long + getView(position : int, convertView : View, parent : ViewGroup) : View + deleteRow(row : es.farfuteam.vncpp.model.sql.Connection) + getList() : ArrayList<Connection> + setList(list : ArrayList<Connection>) es.farfuteam.vncpp.view.DialogOptions + newInstance(listener : SuperListener) : DialogOptions + onCreateDialog(savedInstanceState : Bundle) : Dialog -adapter -adapter Diagram: listasUML Page 1 Figura 6.3: Diagrama Listas de Conexiones Las clases ListFragmentTab y ListFragmentTabFav (Ver Figura 6.3) son las encargadas de mostrar las listas de conexiones y conexiones favoritas respectivamente. La clase SlideListFragment (Ver Figura 6.4) se encarga de construir el men´u lateral y es llamada desde CanvasActivity. CanvasActivity (Ver Figura 6.4) es la encargada de capturar todos los eventos que se produzcan en el cliente, mostrar la imagen del escritorio y comunicarse con la parte nativa. Para dibujar la imagen se apoya en la clase CanvasView (Ver Figura 6.4). Para Comunicarse con la parte nativa se apoya en VncBridgeJNI (Ver imagen 6.5). La comunicaci´on entre el VncBridgeJNI y el Canvas Activity se realiza mediante el patr´on observer (Ver figura 6.4 y 6.5).
6.2. Diagramas de clases 35 es.farfuteam.vncpp.model.ObservableCanvas + ObservableCanvas() + addObserver(observer : ObserverCanvas) + notifyIniFrame(data : int[], offset : int, x : int, y : int, width : int, height : int, realWidth : int, realHeight : int) + notifyRedraw(x : int, y : int, width : int, height : int) + notifyFinish() es.farfuteam.vncpp.view.CanvasView + CanvasView(context : Context, attrs : AttributeSet) + getRealHeight() : int + getRealWidth() : int + getScale() : float + setScale(scale : float, scaleX : float, scaleY : float) + startDrag() + endDrag() + reDraw() + initCanvas(data : int[], offset : int, stride : int, x : int, y : int, width : int, height : int) es.farfuteam.vncpp.view.SlideListFragment + onCreateView(inflater : LayoutInflater, container : ViewGroup, savedInstanceState : Bundle) : View + onActivityCreated(savedInstanceState : Bundle) + onListItemClick(listView : ListView, view : View, position : int, id : long) es.farfuteam.vncpp.controller.CanvasActivity + onConfigurationChanged(newConfig : Configuration) + onMenuItemSelected(featureId : int, item : MenuItem) : boolean + onKeyDown(keyCode : int, evt : KeyEvent) : boolean + dispatchKeyEvent(event : KeyEvent) : boolean + onKeyUp(keyCode : int, evt : KeyEvent) : boolean + onTouchEvent(event : MotionEvent) : boolean + onUpMouse(e : MotionEvent) : boolean + updateRedraw(x : int, y : int, width : int, height : int) + updateIniFrame(data : int[], offset : int, x : int, y : int, width : int, height : int, realWidth : int, realHeight : int) + updateFinish() + showKeyboard() + sendTextDialog() : Dialog + passwordNeededDialog() : Dialog + createServerNotFoundDialog() : Dialog + exitDialog() : Dialog + serverInterruptConexionDialog() : Dialog + timeExceededDialog() : Dialog + comboEventsDialog() : Dialog + functionKeysDialog() : Dialog + openHelpDialog() : Dialog + centerImageCanvas() «interface» es.farfuteam.vncpp.model.ObserverCanvas + updateIniFrame(data : int[], offset : int, x : int, y : int, width : int, height : int, realWidth : int, realHeight : int) + updateRedraw(x : int, y : int, width : int, height : int) + updateFinish() -observer -canvas Diagram: Canvas Page 1 Figura 6.4: Diagrama Canvas La representaci´on de la imagen se ha realizado con canvas, en la clase CanvasView (Ver Figura 6.4). Para dar mejor sensaci´on de fluidez a la hora de moverse por la imagen se utiliza un Bitmap auxiliar en lugar de siempre el mismo. Cuando el programa entra en modo drag, se crea el Bitmap auxiliar y se inicializa con el valor actual de la imagen. Si el usuario est´a moviendose por la imagen (modo drag) el m´etodo onDraw de canvas mostrar´a el Bitmap auxiliar. i f ( ! drag ){ canvas . drawBitmap ( data , o f f s e t , s tr i de , x , y , width , height , f a l s e , n u ll ) ; } e l s e { canvas . drawBitmap ( bitmap , bitmapRect , bitmapRect , null ) ; }
36 Cap´ ıtulo 6. Especificaciones En el momento que el usuario deja de moverse salta un timer creado en un hilo aparte, de tal manera que durante 0.5 segundos la imagen no se destruye (Ver secci´on 5.2.3). La actualizaci´on de la imagen nos llega desde el c´odigo nativo (Ver secci´on 6.2.2). Cuando llega una notificaci´on desde C++ de que la imagen ha sido modificada, el canvas se invalida y se redibuja. Si CanvasActivity detecta un evento de Scroll (movimiento por la pantalla), se llama al m´etodo doScroll. Este m´etodo calcula cuantos p´ıxeles se ha movido en los ejes X e Y, comprobando que no se salga de la imagen. Una vez calculados se llama al m´etodo de canvas scrollTo para que modifique la imagen. i f (moveX != 0 | | moveY != 0){ realX = realX + ( i nt )moveX ; realY = realY + ( i nt )moveY canvas . sc roll To (( i nt )( realX ∗scale Factor ) , ( i n t ) ( realY ∗scaleFact or ) ) ; } Si CanvasActivity detecta un evento de Scale (zoom), se recalcula el factor de escala y dependiendo de si ha sido positivo o negativo se llama a, el m´etodo scrollTo de canvas directamente o a el m´etodo doScroll. i f ( isDo ){ i f ( f a c t o r >1){ int moveX = ( i nt )( x∗scal eF actor ) ; int moveY = ( i nt )( y∗scal eF actor ) ; i f (x <0){ moveX = 0 ; y = 0; } i f (y <0){ moveY = 0 ; y = 0; } realX = ( int )x ; realY = ( int )y ; canvas . sc r ol l To (moveX , moveY ) ; }e l s e i f ( factor <1 ){ int auxX = 0; int auxY = 0; i f (x ∗scaleFactor >canvas . getRealWidth ( ) ) { auxX = −100; }e l s e { auxX = −10; } i f (y ∗scaleFactor >canvas . getRealHeight ( ) ){ auxY = −100; }e l s e { auxY = −10; }
6.2. Diagramas de clases 37 doS c roll (auxX , auxY ) ; } } VncBridgeJNI es la clase que proporcina los m´etodos para comunicarse con la parte nativa. Es capaz de llamar a funciones nativas tales como iniConnect, mouseEvent, keyEvent, etc., de esta manera se establece la comunicaci´on de Java a C/C++ (Ver secci´on 5.2.2). Las notificaciones llegan desde el c´odigo nativo a trav´es de la interfaz ObserverJNI. es.farfuteam.vncpp.model.ObservableCanvas + ObservableCanvas() + addObserver(observer : ObserverCanvas) + notifyIniFrame(data : int[], offset : int, x : int, y : int, width : int, height : int, realWidth : int, realHeight : int) + notifyRedraw(x : int, y : int, width : int, height : int) + notifyFinish() «interface» es.farfuteam.vncpp.model.ObserverCanvas + updateIniFrame(data : int[], offset : int, x : int, y : int, width : int, height : int, realWidth : int, realHeight : int) + updateRedraw(x : int, y : int, width : int, height : int) + updateFinish() es.farfuteam.vncpp.model.VncBridgeJNI + VncBridgeJNI() + startConnect(host : String, port : int, pass : String, quality : String, wifi : boolean, hideMouse : boolean) : ConnectionError + finishVnc() + updateFinishConnection() + updateIniFrame(width : int, height : int, bpp : int, depth : int) + updateReDraw(x : int, y : int, width : int, height : int) + sendMouseEvent(x : int, y : int, event : MouseEvent) + sendKey(key : int, down : boolean) es.farfuteam.vncpp.model.Screen + Screen(width : int, height : int, bpp : int) + createData() + getWidth() : int + setWidth(width : int) + getHeight() : int + setHeight(height : int) + getBpp() : int + setBpp(bpp : int) + getData() : int[] + add(pos : int, value : int) «interface» es.farfuteam.vncpp.model.ObserverJNI + updateFinishConnection() + updateIniFrame(width : int, height : int, bpp : int, depth : int) + updateReDraw(x : int, y : int, width : int, height : int) -screen -observer Diagram: model Page 1 Figura 6.5: Diagrama Modelo 6.2.2. Parte escrita en C/C++ Ahora vamos a describir el c´odigo escrito en C/C++. La clase Java VncBridgeJNI realiza las llamadas a la parte nativa a trav´es de las funciones implementadas en el fichero JavaBridge.cpp. Este fichero no se muestra en el diagrama de clases puesto que no es una clase, a´un as´ı, su ´unica funci´on es hacer las llamadas propias a la clase Vnc, en otras palabras act´ua como puente de Java a C++.
38 Cap´ ıtulo 6. Especificaciones ObservableJNI + ObservableJNI() + ~ ObservableJNI() + addObserver(observer : jobject, env : JNIEnv*) + notifyIniFrame(width : int, height : int, bpp : int, depth : int) + notifyReDraw() + notifyFinishConnection() + finishByClient() + getBitmapData() + getArrayElements() : jint* + setArrayElements(info : jint*) HandlerRFB + HandlerRFB() + ~HandlerRFB() + setScreen(screen : ClientScreen*) + iniFrameBuffer(client_screen : rfbClient*) + updateScreen(client : rfbClient*, x : int, y : int, w : int, h : int) + finishConnection() + finishClient() + setPass(aux_pass : char*) + getPass(client : rfbClient*) : char* + freePass() Vnc + Vnc() + ~ Vnc() + iniConnection(host : char*, port : int, pass : char*, quality : int, compress : int, hide_mouse : bool) : ConnectionError + closeConnection() + addObserver(observer : jobject, env : JNIEnv*) + sendMouseEvent(x : int, y : int, event : MouseEvent) : bool + sendKeyEvent(key : int, down : bool) : bool ClientConnectionRFB + ClientConnectionRFB() + ~ClientConnectionRFB() + iniConnection(host : char*, port : int, pass : char*, picture_quality : int, compress : int, hide_mouse : bool) : ConnectionError + cleanRFB() + sendMouseEvent(x : int, y : int, event : MouseEvent) : bool + sendKeyEvent(key : int, down : bool) : bool + stopConnection() ClientScreen + ClientScreen() + ~ClientScreen() + doMask(client : rfbClient*) + iniScreen(width : int, height : int, bitsPerPixel : int) : int + updateScreen(frameBuffer : uint8_t*, x : int, y : int, w : int, h : int) es.farfuteam.vncpp.model.VncBridgeJNI + VncBridgeJNI() + startConnect(host : String, port : int, pass : String, quality : String, wifi : boolean, hideMouse : boolean) : ConnectionError + finishVnc() + updateFinishConnection() + updateIniFrame(width : int, height : int, bpp : int, depth : int) + updateReDraw(x : int, y : int, width : int, height : int) + sendMouseEvent(x : int, y : int, event : MouseEvent) + sendKey(key : int, down : boolean) rfb 1 0..1-rfb screen 1 1 screen 11 Diagram: connection Page 1 Figura 6.6: Diagrama Conexi´on Al inicializar el c´odigo nativo se crea la clase Vnc (Ver Figura 6.6) y se a˜nade el observer a la clase ClientScreen para que ´este pueda realizar las notificaciones a Java. En la constructora de Vnc se crean las clases ClientScreen, ClientConnectionRFB y se le pasa a la clase HandlerRFB la referencia a ClientScreen para que ´esta pueda comunicarse con ella. Vnc retrasmitir´a todas las ordenes que le lleguen de JavaBridge a ClientConnectionRFB. La clase ClientConnectionRFB (Ver Figura 6.6) es la encargada de gestionar la conexi´on entre el cliente y el servidor utilizando el protocolo RFB. Para establecer una conexi´on lo primero que se hace es inicializar la estructura rfbClient, en este punto se indica cual es el host, el puerto, la calidad de imagen, etc. Un punto muy importante de la inicializaci´on es indicar cuales son las funciones que se han de llamar cuando el server mande notificaciones. Para ello nos apoyamos en la clase HandlerRFB, de tal manera que, por ejemplo, cuando llega un update de la imagen indicamos que tiene que llamar al m´etodo updateScreen del handler. clientRFB=rfbGetClient ( bitsPerSample , samplesPerPixel , bytesPerPixel ) ; clientRFB−>serverPort = port ; clientRFB−>serverHost = host ; clientRFB−>programName = ”VNC++”;
6.2. Diagramas de clases 39 HandlerRFB : : setPass ( pass ) ; clientRFB−>GetPassword = HandlerRFB : : getPass ; clientRFB−>appData . q uali ty Leve l = p i c t u r e q u a l i t y ; clientRFB−>appData . compressLevel = compress ; clientRFB−>appData . useRemoteCursor = hide mouse ; clientRFB−>MallocFrameBuffer=HandlerRFB : : iniFrameBuffer ; clientRFB−>canHandleNewFBSize = TRUE; clientRFB−>GotFrameBufferUpdate=HandlerRFB : : updateScreen ; clientRFB−>FinishedFrameBufferUpdate= HandlerRFB : : finishUpdate ; clientRFB−>l i s t e n P o r t = LISTEN PORT OFFSET; clientRFB−>l i s t e n 6 P o r t = LISTEN PORT OFFSET; Una vez terminada la inicializaci´on de rfbClient se procede a la conexi´on con el servidor. i f ( ! r f b I n i t C l i e n t ( clientRFB ,0 ,NULL)){ err or c onn ect = NoServerFound ; i f (DEBUG) LOGE(”No se rv e r found ” ) ; clientRFB = NULL; } e l s e i f ( ! clientRFB−>frameBuffer){ i f (DEBUG) LOGE(”No Frame Found ” ) ; err or c onn ect = NoFrameFound ; cleanRfb (); } Si todo va bien se crea un hilo para manejar los eventos del protocolo RFB. Dicho hilo es un bucle infinito a la espera de mensajes que provengan del servidor. while ( ! aux this −>stop connection ) { mes=WaitForMessage ( aux this −>clientRFB , timeWait ) ; i f (mes<0){ aux this−>stop connection = true ; serverOut = true ; } i f (mes){ i f ( ! HandleRFBServerMessage ( aux this −>clientRFB )){ aux this−>stop connection = true ; serverOut = true ; } } } La variable aux this es un puntero a ClientConnectionRFB. Si no se ha parado la conexi´on se espera hasta que llega un mensaje del servidor, la funci´on encargada de ello es WaitForMessage. Despu´es la funci´on HandlerRFBServerMessage es la encargada de comprobar cual es el mensaje que ha llegado y en funci´on de ello, llamar a unas funciones u otras de HandlerRFB. Para enviar los eventos del rat´on o de pulsaci´on de teclas se utilizan las funciones SendPointerEvent ySendKeyEvent respectivamente. Cabe destacar que para enviar el
40 Cap´ ıtulo 6. Especificaciones evento de pulsaci´on de teclado hay que transformar el c´odigo de tecla que nos llega desde Java para que el server sea capaz de interpretarlo correctamente. bool ClientConnectionRFB : : sendKeyEvent ( int key , bool down){ rfbKeySym rfbKey = transformToRfbKey ( key ) ; i f ( rfbKey != 0){ SendKeyEvent ( clientRFB , rfbKey , down ) ; } } La clase HandlerRFB (Ver Figura 6.6) es una clase completamente est´atica. Esto se debe a que, dado que LibVNCServer se encuentra escrita en C y los eventos del mismo son punteros a funciones, si no fuesen est´aticos los m´etodos en C++ tienen de forma impl´ıcita el par´ametro this, que es una referencia a la propia clase. Por el contrario C no es un lenguaje orientado a objetos, por ello ese parametro this no existe en las llamadas. Al declarar est´aticos los m´etodos, el par´ametro this desaparece. Sus m´etodos b´asicamente redirigen la llama a las funciones de ClientScreen, por eso era necesario pasar la referencia al inicio. ClientScreen (Ver Figura 6.6) se encarga del manejo de las actualizaciones de la imagen, as´ı como de comunicar todos los cambios a Java. Esta comunicaci´on la hace a trav´es de ObservableJNI mediante la herencia de sus m´etodos. Cuando llega un evento de inicializar la imagen desde el handler se llama al m´etodo iniScreen. En este m´etodo se inicializan todos los valores de la imagen y se env´ıa una notificaci´on a Java. int ClientScreen : : in iScre e n ( const i nt width , const i n t height , const i n t bits PerP ixe l ) { i f (DEBUG) LOGE(” JNI iniFrameBuffer ” ) ; this −>width=width ; this −>height=height ; // se pasa de b i t s a byte this −>bytesPerPixel=bit sPer Pixe l /8; this −>depth = bitsPerPixel ; // se c a l c u l a e l e s pac io t o t a l d el b u f f e r this −>s i z e = this −>height ∗this −>width∗this −>bytesPerPixel ; i f (DEBUG) LOGE(”FIN JNI iniFrameBuffer ” ) ; notifyIniFrame(this−>width , this −>height , this −>bytesPerPixel , this −>depth ) ; return s i z e ; } Cuando se quiere invocar m´etodos de Java, para notificar cambios, es necesario colocarse en el entorno de ejecuci´on actual, esto es, en el hilo. Para ello usamos la funci´on getEnviroment. Este m´etodo comprueba si la variable env, del tipo JNIEnv*, se encuentra apuntando al hilo actual, si no fuese el caso, se coloca en ´el.
6.2. Diagramas de clases 41 void ObservableJNI : : getEnviroment (){ int getEnv = vm−>GetEnv ( ( void ∗∗)&env , JNI VERSION 1 6 ) ; i f ( getEnv == JNI EDETACHED){ vm−>AttachCurrentThread(&env ,NULL) ; } } El m´etodo notifyIniFrame obtiene el m´etodo updateIniFrame del entorno Java y lo llama pas´andole la informaci´on nueva. Despu´es se llama a GetObjectClass para obtener la clase VncBridgeJNI y as´ı, poder hacer llamadas a sus m´etodos. Para hacer una llamada a un m´etodo de Java, una vez tenemos la clase, hay que utilizar el m´etodo GetMethodID para obtener el ID del m´etodo que deseamos invocar. Una vez que dispongamos de dicha ID podemos llamarlo usando CallVoidMethod. void ObservableJNI : : notifyIniFrame ( i nt width , int height , int bpp , int depth ){ getEnviroment ( ) ; i f (DEBUG) LOGE(” Take method iniFrame ” ) ; o b s e r v e r c l a s s = env−>GetObjectClass(this−>observer object ); // cogemos e l ID jmethodID updateScreen = env−>GetMethodID ( o b s e r v e r c l a s s , ”updateIniFrame”, ”( I I I I )V” ) ; i f (DEBUG) LOGE(” Launch method iniFrame ” ) ; env−>CallVoidMethod ( observer object , updateScreen , width , height , bpp , depth ) ; i f (DEBUG) LOGE(” Finish launch method iniFrame ” ) ; env−>DeleteLocalRef ( o b s e r v e r c l a s s ) ; } Para finalizar, el m´etodo updateScreen de ClientScreen es el encargado de actualizar una porci´on de la imagen. El inicio de la porci´on a actualizar viene indicado por los par´ametros x e y, siendo w y h la anchura y la altura respectivamente de dicha porci´on. void ClientScreen : : updateScreen ( u int8 t ∗frameBuffer , const i n t x , const i n t y , const i n t w, const i n t h){ i f (DEBUG) LOGE(” JNI Update ” ) ; i nt p i x e l ;
42 Cap´ ıtulo 6. Especificaciones int pos ; int pos co l ; int pos array ; getBitmapData (); j i n t ∗i n f o = getArrayElements ( ) ; int r e a l i , r e a l j ; f o r ( i n t i =0; i <w; i++ ){ r e a l i = i + x ; pos co l = ( r e a l i ∗bytesPerPixel ); f o r ( i n t j =0; j<h ; j++){ r e a l j = j + y ; pos = (( bytesPerPixel∗width ) ∗r e a l j ) + pos col ; memcpy(&pixel ,& frameBuffer [ pos ] , bytesPerPixel ) ; pos array = ( width ∗r e a l j ) + r e a l i ; i n f o [ po s array ] = p i x e l ; } } i f (DEBUG) LOGE(” JNI no t i fy reDraw ” ) ; setArrayElements ( i n f o ) ; } Lo que hace este m´etodo es recorrer la porci´on que ha cambiado con los dos bucles for. Las variables real i y real j son las posiciones reales de la porci´on dentro de la imagen. Como la imagen se representa en un buffer unidimensional es necesario calcular el desplazamiento que se obtiene: pos = ((bytesP erP ixel)(width))(real j) + ((real i)(bytesP erP ixel)) Una vez tenemos la posici´on copiamos el p´ıxel con memcpy y lo guardamos en info. El bitmap info es una referencia al bitmap de Java, lo obtenemos a trav´es de los m´etodos getBitmapData ygetArrayElements, de tal manera que cuando se modifican elementos de dicho bitmap en C se est´an modificando en Java. Lo ´unico que falta por hacer es actualizar la informaci´on a trav´es del m´etodo setArrayElements. Mediante el que JNIEnv libera el vector del entorno de C/C++ con la funci´on ReleaseIntArrayElements y se actualiza el vector de Java.
Cap´ıtulo 7 Comparativas Se describe la comparaci´on del resulatdo final con las aplicaciones que han sido tomadas como referencia en el principio del proyecto. Como ya se indic´o en el cap´ıtulo dos, usamos como punto de partida tres aplicaciones que abarcan el campo del control remoto m´ovil. 7.1. Funcionalidad Nuestro prop´osito inicial era mejorar ciertos aspectos de estas aplicaciones ya existentes, y en este apartado observamos resultados. 7.1.1. Funcionalidades destacadas compartidas con VNC++ Hace zoom. Muestra el teclado. Escribe. Env´ıa secuencias de teclas. Recuerda conexiones previas. 7.1.2. Funcionalidades diferentes respecto VNC++ RealVNC: como ya se matiz´o, esta aplicaci´on est´a basada en software privativo, por lo que no disponemos de mucha informaci´on relativa a la implementaci´on interna de este proyecto. Pero algo que nos propusimos desde el principio era crear un software puramente libre ya que pensamos que es as´ı como se llega a un producto final con m´as opciones de desarrollo y uso futuro. Permite, a trav´es de un men´u de opciones, desactivar el modo de pantalla completa, as´ı como opci´on de Copy&paste. Tambi´en es destacable la diferencia en el manejo, en VNC Viewer al mover el dedo por la pantalla lo que moveremos es el cursor y no la imagen como en nuestro caso. MultiVNC: libre y de c´odigo abierto, usa aceleraci´on hardware OpenGL para el dibujado y el zoom, mientras que en nuestro caso se ha optado por el uso de Canvas. El manejo en la pantalla de MultiVNC es igual que el de RealVNC. 43
50 Cap´ ıtulo 7. Comparativas MultiVNC: Figura 7.8: Rendimiento 3G MultiVNC Como se puede observar los valores con una conexi´on 3G son muy parecidos, ello es debido a c´omo funciona la propia conexi´on y sus limitaciones. As´ı pues se puede concluir que el rendimiento en entornos de 3G de las aplicaciones es equivalente.
Cap´ıtulo 8 Conclusiones 8.1. Conclusiones Como conclusi´on pensamos que se han alcanzado las metas propuestas en el apartado 1.3. Todo el c´odigo ha sido licenciado bajo una licencia libre (GPL versi´on 3), por tanto el objetivo de crear una aplicaci´on de software libre se ha cumplido sin mayor problema. En cuanto al objetivo m´as importante, la construcci´on de la aplicaci´on de escritorio remoto en Android utilizando c´odigo nativo, tambi´en se ha cumplido, ya que no s´olo se ha construido una aplicaci´on con muchas funcionalidades, sino que es una aplicaci´on eficiente, capaz de competir con todas las dem´as aplicaciones de escritorio remoto para Android que existen en este momento. Cabe destacar una vez m´as que la gran diferencia y la raz´on por la que este proyecto es innovador, es en que ninguna de las dem´as aplicaciones utilizan c´odigo nativo, al menos no las de software libre. La imposibilidad de saber como est´an construidas las aplicaciones privativas nos impide afirmar con total rotundidad que no existe una aplicaci´on de escritorio remoto para Android que utilice el NDK. No obstante, al no existir una de software libre, la innovaci´on sigue estando ah´ı, ya que hemos trabajado sobre algo que no existe en ning´un lado y con unos resultados considerablemente buenos. 8.2. Trabajo Futuro Como posible trabajo futuro se podr´ıa a˜nadir soporte para todos los idiomas, en dos sentidos. Por una lado la traducci´on de la interfaz a diveros idiomas y, por otro, soporte en el teclado para m´as idiomas. Otra cosa que se podr´ıa investigar m´as es la aceleraci´on por hardware, de esta manera se podr´ıa conseguir una mayor velocidad a la hora de trabajar las im´agenes y con ello se obtendr´ıa una mejora del rendimiento global considerable. Tambi´en pensar en portar la aplicaci´on a las dem´as plataformas m´oviles como Firefox OS. 51
52 Cap´ ıtulo 8. Conclusiones
Bibliograf´ıa [1] Wikipedia Escritorio Remoto: http://es.wikipedia.org/wiki/Escritorio_ remoto [2] Wikipedia Virtual Network Computing: http://en.wikipedia.org/wiki/VNC [3] Remote Frame Buffer Protocol: http://www.realvnc.com/docs/rfbproto.pdf [4] Wikipedia Android (operating system): http://en.wikipedia.org/wiki/Android_ (operating_system) [5] Software Development Kit: http://developer.android.com/sdk/index.html [6] Native Development Kit: http://developer.android.com/tools/sdk/ndk/index. html [7] Wikipedia Java Native Interface: http://en.wikipedia.org/wiki/Java_Native_ Interface [8] TightVNC, Tight encoding: http://www.tightvnc.com/decoder.php [9] Wikipedia Git: http://en.wikipedia.org/wiki/Git_(software) [10] Git: http://git-scm.com/ [11] WireShark: http://www.wireshark.org/ [12] Free Software: http://www.fsf.org/about/what-is-free-software [13] General Public License: http://www.gnu.org/licenses/gpl.html [14] Wikipedia RealVNC: http://es.wikipedia.org/wiki/RealVNC [15] MultiVNC: https://play.google.com/store/apps/details?id=com. coboltforge.dontmind.multivnc&hl=es [16] Android-vnc-viewer: http://code.google.com/p/android-vnc-viewer/ [17] SDL: http://www.libsdl.org/ [18] Wikipedia Simple DirectMedia Layer: http://es.wikipedia.org/wiki/Simple_ DirectMedia_Layer [19] SherlockBar: http://actionbarsherlock.com/ [20] LibVNC: http://libvncserver.sourceforge.net/ [21] SlidingMenu: https://github.com/jfeinstein10/SlidingMenu 53
54 BIBLIOGRAF´ IA [22] SQLite Android: http://developer.android.com/reference/android/database/ sqlite/package-summary.html [23] Interfaz Holo: http://developer.android.com/design/style/themes.html
Ap´endices 55
Ap´endice A Aqu´ı se presenta una peque˜na descripci´on de c´omo se usa la aplicaci´on. Este texto de ayuda se puede ver tambi´en desde la propia aplicaci´on accediendo al men´u en la ventana principal. Se encuentra tanto en espa˜nol como ingl´es, al igual que todos los di´alogos y texto de los botones. Es el propio sistema Android, en base al idioma seleccionado por defecto en el sistema operativo, el que selecciona el idioma a mostrar. En caso de no disponer del idioma correspondiente al del sistema, por defecto se mostrar´an los texto en ingl´es. A.1. P´agina de ayuda de VNC++ A.1.1. Manejo principal Para crear una nueva conexi´on pulse el icono “+” en la barra superior de Acci´on (Ver Figura A.1). Una vez dentro, es necesario especificar la IP del servidor, as´ı como un nombre de identificaci´on de la conexi´on. El puerto por defecto es el 5900, pudi´endose cambiar, as´ı como la contrase˜na, pudiendose dejar en blanco si no se necesita. Figura A.1: Nueva conexi´on Figura A.2: Vista del escritorio 57
58 Cap´ ıtulo A. El desplegable de calidades sirve para indicar la calidad de la imagen. Se puede elegir entre calidad super-alta, alta, media o baja, seg´un el tipo de conexi´on del usuario ya sea m´as lenta o r´apida, o simplemente por necesidad de cierta calidad de imagen. En el bot´on desplegable de opciones en la actividad principal se puede acceder a tres submen´us: La opci´on de Configuraci´on: permite, a trav´es de dos switches de selecci´on, marcar si se recuerda o no la opci´on de confirmar salida, as´ı como ocultar el cursor o no (Ver Figura A.3). Manual de uso parecido al que se est´a narrando (Ver Figura A.4). Secci´on “Acerca de” donde se reflejan los creadores de la aplicaci´on, as´ı como el tipo de licencia y las librer´ıas usadas (Ver Figura A.8). Figura A.3: Configuraci´on Figura A.4: Manual de uso A.1.2. Control de pesta˜nas Se puede cambiar de pesta˜na pulsando sobre las etiquetas RECIENTES o FAVORITOS, y as´ı ver las distintas conexiones creadas (Ver Figura A.5). A.1.3. A˜nadir a favoritos Para a˜nadir a favoritos ´unicamente hay que clicar sobre el icono con forma de estrella (Ver Figura A.5). Para quitar esa conexi´on de favoritos, se pulsa de nuevo el icono marcado, quit´andose de la secci´on de favoritos.
A.1. P´ agina de ayuda de VNC++ 59 A.1.4. Opciones de conexi´on Al pulsar sobre una conexi´on de la lista, se presenta un men´u de opciones. Desde ah´ı se puede conectar, mostrar la informaci´on de la conexi´on, editar y eliminar dicha conexi´on. Tambi´en se puede conectar pulsando el icono de la derecha con forma de flecha. A.1.5. Opciones de edici´on Se podr´a modificar cualquier valor previo de la conexi´on, a excepci´on del identificador elegido, del mismo modo que se crea una nueva conexi´on. A.1.6. Men´u lateral Una vez cargada la imagen se puede acceder al men´u de opciones, tanto desde el bot´on f´ısico del terminal, como arrastrando desde la izquierda de la pantalla para sacar el men´u lateral. Una vez ah´ı se puede mostrar teclado, mandar teclas, centrar imagen, mostrar una ayuda reducida y desconectar (Ver Figura A.6). Figura A.5: Pesta˜nas Figura A.6: Men´u lateral A.1.7. Manejo Para mandar evento click, simplemente haga una pulsaci´on con el dedo. Para hacer doble click, mantenga presionado el dedo sobre la pantalla durante unos 3 segundos. Para hacer zoom, coloque un dedo sobre la pantalla, mientras con otro dedo alejas o acercas del primer dedo, a modo de pinza.