• Non ci sono risultati.

Test di attivazione della modalit`a ad hoc su disposit

3.2 Wifi ad hoc sui device Android

3.2.2 Test di attivazione della modalit`a ad hoc su disposit

spositivi Nexus

Avendo a disposizione degli smartphone Nexus 5 e tablet Nexus 7 2013, entrambi con sistema operativo Android Lollipop 5.0, ho e↵ettuato alcune sperimentazioni al fine di attivare su di essi la modalit`a Wifi ad hoc, ed e↵ettuare uno scambio di dati di prova tra di essi attraverso la rete ad hoc creata.

Per ogni device ho quindi tentato di eseguire la stessa procedura volta all’attivazione della modalit`a Wifi ad hoc, che ora descriver`o. Dopo aver eseguito la procedura di rooting del dispositivo, `e ora possibile eseguire una shell con permessi di root, per esempio con un emulatore di terminale sotto forma di applicazione Android come ConnectBot [15], oppure utilizzando il tool di debugging adb incluso nell’SDK Android ufficiale. Dopo aver aperto una shell come utente non privilegiato, `e infatti possibile eseguire una shell di root semplicemente eseguendo su.

Una volta ottenuta una shell di root, `e ora possibile tentare di utilizzare utility come iw ed ifconfig per controllare a basso livello lo stack Wifi del dispositivo Android. Queste utility per`o non sono generalmente incluse nei sistemi Android tra i file di sistema, quindi `e necessario reperire i file eseguibili delle utility iw ed ifconfig e copiarli nel device.

I file eseguibili possono essere ottenuti compilando i rispettivi sorgenti C con il toolchain fornito nell’Android NDK (Native Development Kit), per l’architettura utilizzata dal device, armeabi nel caso dei Nexus in mio pos- sesso. Per semplicit`a, `e anche possibile reperire gli eseguibili iw ed ifconfig gi`a compilati dalla comunit`a di alcuni progetti open source, come Serval Project [16] che li include nella propria applicazione Android [17]. Una vol- ta copiati nel filesystem del device Android gli eseguibili iw ed ifconfig , `e ora possibile lanciarli dalla shell di root per tentare di attivare la modalit`a Wifi ad hoc.

Prima di tutto `e per`o necessario disattivare dall’interfaccia utente An- droid le funzionalit`a Wifi del device, in modo da non far gestire lo stack

802.11 del kernel da parte dei servizi di configurazione Wifi del sistema An- droid, che potrebbero interferire con le impostazioni e↵ettuate manualmente tramite l’utility iw.

Ora `e possibile lanciare i seguenti comandi: ifconfig wlan0 up

ifconfig wlan0 192.168.0.1 netmask 255.255.255.0

in questo modo viene configurato il protocollo IP per l’interfaccia di rete

wlan0 del kernel con un IP statico.

Come spiegato in precedenza, in sistemi di comunicazioni opportunisti- che basati su reti Wifi ad hoc, non `e possibile far assegnare gli indirizzi IP dei device ad un server DHCP centrale, quindi in questa sperimentazione ho scelto di utilizzare indirizzi IP assegnati staticamente ad ogni device.

Ora `e possibile eseguire l’utility iw per impostare lo stack 802.11 dell’in- terfaccia wlan0 in modalit`a ad hoc:

iw dev wlan0 set type ibss

iw dev wlan0 ibss join <ESSID> <frequenza in Hz> <BSSID>

Il parametro ibss identifica la modalit`a IBSS (Independent Basic Ser- vice Set), un altro nome con cui viene chiamata la modalit`a Wifi ad hoc nelle specifiche dello standard 802.11.

Nel secondo comando, vanno specificati i seguenti parametri: • ESSID: il “nome” della rete Wifi ad hoc che si vuole creare

• frequenza in Hz: la frequenza centrale del canale Wifi da utilizzare. Se si vuole utilizzare per esempio il canale 6 dello standard 802.11, occorre inserire 2437.

• BSSID: parametro opzionale che forza l’uso di un BSSID manuale, utile per evitare alcuni problemi dati dal merge di reti ad hoc pre- cedentemente separate, descritti in precedenza. [3] Per i test che ho e↵ettuato, ho scelto di utilizzare questo BSSID: 02:ca:ff:ee:ba:be Utilizzando dei parametri di esempio, possiamo lanciare il secondo co- mando in questo modo:

A questo punto, se tutto ha funzionato correttamente, il device Android dovrebbe essere connesso alla rete Wifi adhoc “adhoctest”, con l’indirizzo

IP 192.168.0.1. `E ora possibile testare il corretto funzionamento della rete

per esempio connettendosi con un altro device alla stessa rete, e provando a scambiare dati per esempio tramite il comando ping.

Dai test che ho e↵ettuato su device come il Nexus 5, ho notato che negli smartphone che possiedono connettivit`a ad Internet tramite rete cellulare, il sistema Android si connette alla rete LTE/3G dato che pensa che il Wifi sia disattivato, e di conseguenza configura lo stack IP del kernel Linux per funzionare con la connessione ad Internet tramite rete cellulare, in modo da bloccare il transito dei pacchetti con l’interfaccia di rete Wifi.

Per risolvere questo problema, `e possibile eseguire questo comando: ip rule add table main

tramite questo comando viene modificata la configurazione delle regole IP del kernel Linux in modo da aggirare alcune limitazioni configurate dal sistema Android probabilmente per motivi di sicurezza. Dopo aver eseguito questo comando, il device Android `e in grado di far coesistere la rete Wifi ad hoc e la connessione ad Internet tramite LTE/3G, permettendo quindi alle applicazioni di comunicare contemporaneamente sia tramite la rete ad hoc che tramite Internet.

Per e↵ettuare dei test sul corretto funzionamento della rete Wifi ad hoc appena configurata, `e possibile utilizzare il comando ping, gi`a incluso nel sistema Android ufficiale dei dispositivi Nexus, specificando come argomen- to l’indirizzo IP di un altro device con il quale si vuole testare la possibilit`a di comunicare:

ping 192.168.0.2

Dai risultati di alcuni test che ho e↵ettuato, due device Nexus 5 riescono a comunicare correttamente con la procedura sopra descritta, mentre invece ho riscrontrato alcuni problemi in una rete formata da un Nexus 7 ed un Nexus 5, in particolare ho riscontrato un alto tasso di perdita dei pacchetti inviati.

I problemi che ho riscontrato potrebbero essere dovuti a dei bug o limi- tazioni nei driver del chipset 802.11 utilizzati nei rispettivi device. I Nexus 5 infatti montano il chipset Wifi Broadcom BCM4339 [18] ed utilizzano un driver Broadcom, mentre i Nexus 7 2013 usano un Qualcomm Atheros WCN3660 tramite un driver Qualcomm.

3.2.3

Sviluppo di un’applicazione Android per l’atti-

vazione automatizzata di una rete ad hoc

Nel punto precedente `e stata descritta una procedura manuale per connette- re un dispositivo Android ad una rete Wifi ad hoc, lanciando manualmente dei file eseguibili da una shell di root. Se si vuole realizzare un sistema reale che fa uso delle reti Wifi ad hoc, occorre per`o automatizzare la procedura vista precedentemente, in modo da renderla semplice da usare per l’utente finale del sistema.

Analizziamo ora come sviluppare un semplice prototipo di applicazione Android che esegue l’attivazione della modalit`a Wifi ad hoc, e successi- vamente invia e riceve pacchetti UDP di broadcast tramite dei socket, in modo da testare il corretto funzionamento della rete. Essendo solamente una semplice applicazione di prova, per semplicit`a essa sar`a composta da una sola Activity con all’interno un Fragment, che contiene tutta la logica di attivazione della modalit`a ad hoc e dello scambio di pacchetti UDP.

Dato che l’applicazione deve eseguire come root le utility iw ed ifconfig, occorre:

• Includere i binari di iw ed ifconfig nel file apk finale dell’applicazio- ne.

Uno dei metodi per includere eseguibili Unix all’interno di una appli- cazione Android, consiste nell’inserirli nel progetto Gradle di Android Studio nei seguenti path:

lib/armeabi/lib_iw_.so

lib/armeabi/lib_ifconfig_.so

In questo modo vengono inclusi nell’apk dai build script, e dopo l’in- stallazione nel device Android, essi sono accessibili con permessi di esecuzione dall’applicazione al path:

/data/data/<nome-package-java>/lib/

• Includere tra le dipendenze Gradle una libreria per facilitare l’esecuzio- ne di comandi come utente root. Una di queste librerie `e libsuperuser, utilizzabile aggiungendo la seguente dipendenza nel file build.gradle:

compile ’eu.chainfire:libsuperuser:1.0.0.+’ `

E ora possibile realizzare un semplice metodo Java che esegue i comandi descritti precedentemente per la connessione ad una rete Wifi ad hoc: 1 p r i v a t e v o i d s t a r t W i f i A d h o c ( S t r i n g myIpAddr , S t r i n g netmask , S t r i n g e s s i d , i n t

f r e q u e n c y ){

2

3 // D i s a b l e Android W i f i management , I need t o c o n t r o l t h e Linux W i f i s t a c k by m y s e l f !

4 WifiManager w i f i M a n a g e r = ( WifiManager ) g e t A c t i v i t y ( ) . g e t S y s t e m S e r v i c e ( C o n t e x t . WIFI SERVICE ) ; 5 w i f i M a n a g e r . s e t W i f i E n a b l e d (f a l s e) ; 6 7 t r y { 8 Thread . s l e e p ( 4 0 0 0 ) ; 9 } c a t c h ( I n t e r r u p t e d E x c e p t i o n e ) {} 10 11 // I f i l e ” lib NOME . s o ” c o n t e n u t i i n q u e s t a d i r e c t o r y i n r e a l t a ’ s o n o d e g l i e s e g u i b i l i Unix (ELF) . 12 // L i ho c h i a m a t i i n q u e l modo p e r c h e ’ c o s i ’ vengono t r a t t a t i ” c o r r e t t a m e n t e ” d a l b u i l d s y s t e m d i Android 13 S t r i n g e x e D i r = ” / d a t a / d a t a / ” + g e t A c t i v i t y ( ) . getPackageName ( ) + ” / l i b / ”; 14 15 // E x e c u t e a s r o o t t h e commands t o e n a b l e adhoc w i f i 16 L i s t <S t r i n g > e x e c R e s u l t s = S h e l l . SU . run (new S t r i n g [ ]{ 17 e x e D i r + ” l i b i f c o n f i g . s o wlan0 up”,

18 e x e D i r + ” l i b i f c o n f i g . s o wlan0 ” + myIpAddr + ” netmask ” + netmask ,

19 e x e D i r + ” l i b i w . s o dev wlan0 s e t t y p e i b s s ”,

20 e x e D i r + ” l i b i w . s o dev wlan0 i b s s j o i n ” + e s s i d + ” ” + f r e q u e n c y + ” ” + BSSID ,

21 ” i p r u l e add t a b l e main ” // t h i s i s n e e d e d t o make t h e adhoc n e t w o r k w o r k i n g s i m u l t a n e o u s l y w i t h LTE/3G/2G c o n n e c t i o n . 22 // T h i s p r o b a b l y make e v e r y t h i n g b y p a s s some Android n e t w o r k a c c e s s l i m i t a t i o n s / p e r m i s s i o n s , 23 // but I t h i n k i t ’ s s u f f i c i e n t l y s e c u r e anyway f o r u s e c a s e . 24 }) ; 25 26 f o r( S t r i n g msg : e x e c R e s u l t s ){ 27 Log . i (” adhoc ”, msg ) ; 28 } 29 }

Per e↵ettuare un testing veloce del funzionamento della rete Wifi ad hoc appena creata, `e possibile includere nell’applicazione una funzionalit`a per inviare dei messaggi periodici o manuali tramite un socket UDP all’indirizzo di broadcast, e contemporanemente riceverli con un altro socket UDP e mostrarli a video.

Questa `e una semplice implementazione che fa uso di AsyncTask e Thread, da utilizzare dentro una Activity o un Fragment:

1 p r i v a t e c l a s s U d p B r o a d c a s t R e c e i v e r T a s k e x t e n d s AsyncTask<Void , S t r i n g , Void> {

2

3 @ O v e r r i d e

4 p r o t e c t e d Void d oInB ackgroun d ( Void . . . params ) {

5 6 // Non s o s e i l M u l t i c a s t L o c k r i g u a r d a a n c h e i p a c c h e t t i d i b r o a d c a s t . . . n e l d u b b i o l o a c q u i s i s c o 7 WifiManager w i f i = ( WifiManager ) g e t A c t i v i t y ( ) . g e t S y s t e m S e r v i c e ( C o n t e x t . WIFI SERVICE ) ; 8 WifiManager . M u l t i c a s t L o c k m u l t i c a s t L o c k = w i f i . c r e a t e M u l t i c a s t L o c k (” o p p o r t u n i s t i c n e t ”) ; 9 m u l t i c a s t L o c k . a c q u i r e ( ) ; 10 11 t r y {

12 p u b l i s h P r o g r e s s (” s t a r t i n g udp p a c k e t s r e c e i v e r ! ”) ;

13

14 DatagramSocket r e c v S o c k = new DatagramSocket (UDP PORT, I n e t A d d r e s s . getByName (” 0 . 0 . 0 . 0 ”) ) ;

15 r e c v S o c k . s e t B r o a d c a s t (t r u e) ;

16

17 w h i l e (t r u e) {

18 b y t e[ ] d a t a = new b y t e[ 1 5 0 0 0 ] ;

19 DatagramPacket p a c k e t = new DatagramPacket ( data , d a t a . l e n g t h ) ;

20 r e c v S o c k . r e c e i v e ( p a c k e t ) ; 21 S t r i n g msg = new S t r i n g ( p a c k e t . g e t D a t a ( ) ) ; 22 23 p u b l i s h P r o g r e s s ( msg ) ; 24 } 25 26 } c a t c h ( E x c e p t i o n e ){ 27 e . p r i n t S t a c k T r a c e ( ) ; 28 } 29 30 m u l t i c a s t L o c k . r e l e a s e ( ) ; 31 32 r e t u r n n u l l; 33 } 34 35 @ O v e r r i d e 36 p r o t e c t e d v o i d o n P r o g r e s s U p d a t e ( S t r i n g . . . v a l u e s ) { 37 s u p e r. o n P r o g r e s s U p d a t e ( v a l u e s ) ; 38 39 l o g T e x t V i e w . s e t T e x t ( v a l u e s [ 0 ] +”\n” + l o g T e x t V i e w . g e t T e x t ( ) . t o S t r i n g ( ) ) ; 40 } 41 } 42 43 44 45 // Questo t h r e a d s a r e b b e s t a t o da f a r e u n i t o a l t a s k s o t t o . . . 46 p r i v a t e c l a s s B r o a d c a s t S e n d e r T h r e a d e x t e n d s Thread { 47 48 p r i v a t e DatagramSocket s e n d S o c k ; 49 50 p u b l i c B r o a d c a s t S e n d e r T h r e a d ( ) t h r o w s S o c k e t E x c e p t i o n { 51 s e n d S o c k = new DatagramSocket ( ) ; 52 s e n d S o c k . s e t B r o a d c a s t (t r u e) ; 53 } 54 55 @ O v e r r i d e 56 p u b l i c v o i d run ( ) { 57 s u p e r. run ( ) ; 58 59 w h i l e( ! i s I n t e r r u p t e d ( ) ) { 60 t r y { 61 Thread . s l e e p ( 1 0 0 0 0 ) ; 62 63 b y t e[ ] d a t a = (” c i a o s o n o ” + myIpEditText . g e t T e x t ( ) . t o S t r i n g ( ) ) . g e t B y t e s ( ) ;

64 DatagramPacket p a c k e t = new DatagramPacket ( data , d a t a . l e n g t h , I n e t A d d r e s s . getByName (BROADCAST ADDRESS) , UDP PORT) ;

65 s e n d S o c k . s e n d ( p a c k e t ) ; 66 67 } c a t c h ( E x c e p t i o n e ){ 68 e . p r i n t S t a c k T r a c e ( ) ; 69 } 70 } 71 } 72 } 73 74 75

76 p r i v a t e c l a s s B r o a d c a s t S e n d e r T a s k e x t e n d s AsyncTask<Void , Void , Void> {

77 78 p r i v a t e S t r i n g msg ; 79 80 p u b l i c B r o a d c a s t S e n d e r T a s k ( S t r i n g msg ){ 81 t h i s. msg = msg ; 82 } 83 84 @ O v e r r i d e

86 t r y {

87 // I n v i o i n b r o a d c a s t i l m e s s a g g i o s c r i t t o d a l l ’ u t e n t e

88 DatagramSocket s e n d S o c k = new DatagramSocket ( ) ;

89 s e n d S o c k . s e t B r o a d c a s t (t r u e) ;

90 b y t e[ ] d a t a = msg . g e t B y t e s ( ) ;

91 DatagramPacket p a c k e t = new DatagramPacket ( data , d a t a . l e n g t h , I n e t A d d r e s s . getByName (BROADCAST ADDRESS) , UDP PORT) ;

92 s e n d S o c k . s e n d ( p a c k e t ) ; 93 } c a t c h( E x c e p t i o n e ){ 94 e . p r i n t S t a c k T r a c e ( ) ; 95 } 96 r e t u r n n u l l; 97 } 98 }

Le precedenti due parti di codice, possono essere inserite in una classe chiamata NetStatusFragment: 1 p a c k a g e i t . u n i b o . o p p o r t u n i s t i c n e t . a n d r o i d ; 2 3 i m p o r t a n d r o i d . c o n t e n t . C o n t e x t ; 4 i m p o r t a n d r o i d . n e t . w i f i . WifiManager ; 5 i m p o r t a n d r o i d . o s . AsyncTask ; 6 i m p o r t a n d r o i d . o s . Bundle ; 7 i m p o r t a n d r o i d . s u p p o r t . v4 . app . Fragment ; 8 i m p o r t a n d r o i d . t e x t . method . S c r o l l i n g M o v e m e n t M e t h o d ; 9 i m p o r t a n d r o i d . u t i l . Log ; 10 i m p o r t a n d r o i d . v i e w . L a y o u t I n f l a t e r ; 11 i m p o r t a n d r o i d . v i e w . View ; 12 i m p o r t a n d r o i d . v i e w . ViewGroup ; 13 i m p o r t a n d r o i d . w i d g e t . Button ; 14 i m p o r t a n d r o i d . w i d g e t . E d i t T e x t ; 15 i m p o r t a n d r o i d . w i d g e t . TextView ; 16 i m p o r t eu . c h a i n f i r e . l i b s u p e r u s e r . S h e l l ; 17 18 i m p o r t j a v a . n e t .⇤ ; 19 i m p o r t j a v a . u t i l . L i s t ; 20 21 22 p u b l i c c l a s s N e t S t a t u s F r a g m e n t e x t e n d s Fragment { 23 24 p r i v a t e f i n a l s t a t i c i n t UDP PORT = 1 0 0 0 0 ; 25 p r i v a t e f i n a l s t a t i c S t r i n g BROADCAST ADDRESS = ” 1 9 2 . 1 6 8 . 0 . 2 5 5 ”; 26 27 p r i v a t e f i n a l s t a t i c S t r i n g BSSID =” 0 2 : c a : f f : e e : ba : be ”; // un q u a l s i a s i MAC a d d r e s s c h e i n i z i con 0 2 : 28 29 p r i v a t e TextView l o g T e x t V i e w ; 30 p r i v a t e E d i t T e x t myIpEditText ; 31 p r i v a t e E d i t T e x t msgEditText ; 32 33 p u b l i c N e t S t a t u s F r a g m e n t ( ){ 34 } 35 36 @ O v e r r i d e 37 p u b l i c View o n C r e a t e V i e w ( L a y o u t I n f l a t e r i n f l a t e r , ViewGroup c o n t a i n e r , 38 Bundle s a v e d I n s t a n c e S t a t e ) { 39 r e t u r n i n f l a t e r . i n f l a t e (R . l a y o u t . f r a g m e n t n e t s t a t u s , c o n t a i n e r , f a l s e) ; 40 } 41 42 43 @ O v e r r i d e

44 p u b l i c v o i d onViewCreated ( View view , Bundle s a v e d I n s t a n c e S t a t e ) {

45 s u p e r. onViewCreated ( view , s a v e d I n s t a n c e S t a t e ) ; 46 47 myIpEditText = ( E d i t T e x t ) g e t A c t i v i t y ( ) . f i n d V i e w B y I d (R . i d . myIpEditText ) ; 48 49 Button a d h o c S t a r t B u t t o n = ( Button ) g e t A c t i v i t y ( ) . f i n d V i e w B y I d (R . i d . a d h o c S t a r t B u t t o n ) ; 50 a d h o c S t a r t B u t t o n . s e t O n C l i c k L i s t e n e r (new View . O n C l i c k L i s t e n e r ( ) { 51 @ O v e r r i d e 52 p u b l i c v o i d o n C l i c k ( View v ) { 53 54 s t a r t W i f i A d h o c ( myIpEditText . g e t T e x t ( ) . t o S t r i n g ( ) , ” 2 5 5 . 2 5 5 . 2 5 5 . 0 ”, ” mesh ”, 2 4 3 7 ) ; // c a n a l e 6 = 2 4 3 7 , c a n a l e 40 = 5200

55 56 // F a c c i o p a r t i r e l a demo d i s c a m b i o d e i m e s s a g g i 57 t r y { 58 new B r o a d c a s t S e n d e r T h r e a d ( ) . s t a r t ( ) ; 59 } c a t c h ( E x c e p t i o n e ){ 60 e . p r i n t S t a c k T r a c e ( ) ; 61 } 62

63 new U d p B r o a d c a s t R e c e i v e r T a s k ( ) . e x e c u t e O n E x e c u t o r ( AsyncTask .THREAD POOL EXECUTOR ) ; 64 } 65 }) ; 66 67 l o g T e x t V i e w = ( TextView ) g e t A c t i v i t y ( ) . f i n d V i e w B y I d (R . i d . l o g T e x t V i e w ) ; 68 l o g T e x t V i e w . setMovementMethod (new S c r o l l i n g M o v e m e n t M e t h o d ( ) ) ; 69 70 msgEditText = ( E d i t T e x t ) g e t A c t i v i t y ( ) . f i n d V i e w B y I d (R . i d . msgEditText ) ; 71

72 Button sendMsgButton = ( Button ) g e t A c t i v i t y ( ) . f i n d V i e w B y I d (R . i d . sendMsgButton ) ;

73 sendMsgButton . s e t O n C l i c k L i s t e n e r (new View . O n C l i c k L i s t e n e r ( ) {

74 @ O v e r r i d e

75 p u b l i c v o i d o n C l i c k ( View v ) {

76 new B r o a d c a s t S e n d e r T a s k ( msgEditText . g e t T e x t ( ) . t o S t r i n g ( ) ) . e x e c u t e O n E x e c u t o r ( AsyncTask . THREAD POOL EXECUTOR) ;

77 } 78 }) ; 79 } 80 81 p r i v a t e v o i d s t a r t W i f i A d h o c ( S t r i n g myIpAddr , S t r i n g netmask , S t r i n g e s s i d , i n t f r e q u e n c y ){ 82 . . . 83 } 84 85 p r i v a t e c l a s s U d p B r o a d c a s t R e c e i v e r T a s k e x t e n d s AsyncTask<Void , S t r i n g , Void> { 86 . . . 87 } 88 89 p r i v a t e c l a s s B r o a d c a s t S e n d e r T h r e a d e x t e n d s Thread { 90 . . . 91 } 92

93 p r i v a t e c l a s s B r o a d c a s t S e n d e r T a s k e x t e n d s AsyncTask<Void , Void , Void> {

94 . . .

95 }

96 }

Di seguito anche la risorsa XML dell’interfaccia utente del NetStatu- sFragment sopra descritto:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin" android:orientation="vertical"> <LinearLayout

android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceMedium" android:text="My IP addr:" android:id="@+id/textView"/> <EditText android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/myIpEditText" android:text="192.168.0.1" android:layout_weight="1"/> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Start Wifi Ad-Hoc" android:id="@+id/adhocStartButton" /> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <EditText android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/msgEditText"

android:hint="messaggio da inviare" android:text="prova" android:layout_weight="1"/> <Button android:id="@+id/sendMsgButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Send msg"/> </LinearLayout> <TextView android:layout_width="match_parent" android:layout_height="match_parent" android:textAppearance="?android:attr/textAppearanceSmall" android:text="Log:" android:id="@+id/logTextView" android:layout_marginTop="20dp" android:scrollbars="vertical" /> </LinearLayout>

Tramite l’Activity dell’applicazione che contiene il NetStatusFragment appena descritto, `e possibile inserire nella myIpEditText l’indirizzo IP che verr`a assegnato al device nel quale `e eseguita l’applicazione, e successiva- mente premere il bottone “Start Wifi Ad-Hoc”.

A questo punto l’applicazione, dopo aver ottenuto l’accesso root, si oc- cuper`a di attivare la modalit`a Wifi ad hoc, eventualmente in parallelo alla connessione ad Internet tramite la rete cellulare, se abilitata.

Inoltre inizier`a ad inviare alla rete periodicamente dei messaggi di debug tramite pacchetti UDP in broadcast, che possono essere ricevuti e visualizza-

ti nella logTextView. `E anche possibile inviare dei messaggi manualmente

3.3

Costruzione di campi computazionali tra-

mite Akka Stream

Nel capitolo precedente della tesi, `e stato spiegato come realizzare una appli- cazione Android che riesca ad attivare la modalit`a Wifi ad hoc sui dispositivi che la supportano.

In questo capitolo vedremo invece come sfruttare i risultati appena de- scritti per sviluppare un sistema che riesca a costruire campi computazionali tramite comunicazioni opportunistiche tra dispositivi Android.

Una volta ottenuta l’attivazione della rete Wifi ad hoc, l’applicazione che si dovr`a costruire consister`a in un semplice software basato su tradizionali comunicazioni di rete. Esso sar`a infatti basato sull’invio e la ricezione di dati attraverso pacchetti UDP di broadcast, attraverso i quali l’applicazione dovr`a essere in grado di costruire dei campi computazionali.

Pur utilizzando a basso livello dei semplici socket UDP, la logica sovra- stante dell’applicazione pu`o risultare piuttosto complessa, in quanto deve occuparsi di gestire la ricezione e l’invio possibilmente concorrente di pac- chetti da diversi device, tenere aggiornata una struttura dati in memoria dello stato della logica di costruzione dei campi computazionali, aggiorna- mento dell’interfaccia grafica, e gestione di varie situazioni di errore come la perdita di pacchetti.

Nello sviluppo di applicazioni di questa tipologia basate sostanzialmente sull’elaborazione di flussi di eventi, pu`o risultare molto utile l’impiego di framework ad eventi, per rendere lo sviluppo di software intrinsecamente

concorrente pi`u semplice, efficiente e privo di errori.

Nella prossima sezione della tesi, verranno analizzati i framework Rx ed Akka Stream, e successivamente verr`a descritto lo sviluppo di un applicazio- ne Android basata su Akka Stream, scritta in linguaggio Scala, in grado di costruire campi computazionali sulla base di comunicazioni di rete tramite pacchetti UDP.

3.3.1

Introduzione ai framework di reactive program-

ming

Negli ultimi anni si sta assistendo ad un trend di sempre maggiore di↵u- sione dei cosiddetti framework di reactive programming, o programmazio- ne orientata agli eventi, utilizzati sopratutto negli ambiti dell’industria del

software legati al paradigma della programmazione funzionale. Questi fra- mework semplificano lo sviluppo di software basato su computazioni svolte come reazione a degli eventi, generati generalmente da operazioni di IO. Gli eventi a cui una applicazione deve poter reagire possono essere per esempio l’evento di click di un bottone della GUI, oppure l’arrivo di un pacchetto dalla rete.

Le operazioni di IO all’interno di un software, sono state tradizional- mente gestite in due modi:

• IO sincrono - l’operazione di IO viene e↵ettuata invocando per esempio dei metodi come send() e receive(), che bloccano il flusso di esecuzione finch´e l’operazione non `e terminata.

Questa soluzione ha il vantaggio di essere molto familiare per chi `e abituato al paradigma di programmazione imperativo tradizionale, ma causa problemi dati dalla necessit`a di usare molti thread per gestire tutte le computazioni necessarie. Questo oltre a causare potenziali problemi di performance, rende necessario l’uso di tecniche di locking

nel caso in cui pi`u di un thread debba operare concorrentemente su

uno stato condiviso.

Nello sviluppo di software complesso che fa uso di lock espliciti, `e estremamente facile commettere errori che possono causare bug difficili da essere scoperti, a causa del non determinismo dell’esecuzione di software concorrenti.

• IO asincrono tramite callback - nel caso delle operazioni di IO asincrone, metodi come per esempio receive() o send() terminano im- mediatamente, restituendo il flusso di controllo al chiamante, e l’ope- razione di IO vera e propria avviene in background. Quando l’ope- razione termina, l’applicazione viene notificata del risultato con l’e- secuzione di un callback, del codice che svolge la funzione di handler dell’evento.

Questa `e la soluzione tradizionalmente usata nelle GUI. Pur permet- tendo di sviluppare software e↵ettivamente asincrono, ha la grossa limitazione di rendere il codice difficile da comprendere e scarsamen- te manutenibile, appena si cerca di sviluppare una applicazione non triviale.

L’utilizzo di operazioni asincrone tramite callback non permette infatti di fare a meno di dover accedere concorrentemente ad uno stato in memoria condivisa. Si ricade quindi negli stessi problemi di gestione della shared memory tramite tecniche di locking, gi`a visti nel caso dell’IO sincrono.

I framework di reactive programming, cercano di risolvere i problemi e le limitazioni delle tecniche di programmazione asincrona basata sui callback, utilizzando concetti ed astrazioni provenienti dal mondo della programma- zione funzionale. I framework che analizzer`o sono Rx (Reactive Extensions) ed Akka, che sono infatti nati in ambiti di ricerca legata a linguaggi di programmazione funzionale, come Scala ed Haskell.

Il concetto derivante dalla programmazione funzionale di cui questi fra- mework si avvalgono maggiormente, `e sicuramente quello degli oggetti im- mutabili, cio`e dell’utilizzo di strutture dati in memoria che non possono essere modificate, tranne che creandone una copia. Le strutture dati immu- tabili permettono di far mantenere ad un flusso di esecuzione uno snapshot sempre consistente di una certa struttura dati presente in memoria. Le strutture dati immutabili possono quindi essere accedute tranquillamente

in modo concorrente da pi`u thread, senza dover utilizzare alcuna tecnica

di locking. In molti framework basati sul message-passing, le strutture dati immutabili vengono utilizzate per i messaggi scambiati tra le varie entit`a del sistema, sfruttando la loro immutabilit`a per evitare side-e↵ect. Le strutture

dati immutabili pi`u comuni, come liste, set e map, sono fornite nativamente

da linguaggi funzionali come Scala.

I framework di reactive programming principalmente utilizzati, possono essere divisi in due categorie, a seconda dell’astrazione di base che usano:

• Attori: il framework pi`u rappresentativo di questa categoria `e sicura- mente la versione tradizionale di Akka, che permette di rappresentare le entit`a che compongono un certo sistema software come degli attori, una sorta di oggetti che mantengono uno stato e che reagiscono a dei messaggi che possono essergli inviati da altri attori.

Il modello ad attori permette di risolvere alcune problematiche del- la programmazione basata su callback: invece di notificare un evento tramite un callback che potrebbe incorrere in corse critiche accedendo a della memoria condivisa utilizzata concorrentemente da altri thread,

in un sistema basato sul modello ad attori si pu`o semplicemente noti- ficare un evento inviando un messaggio ad un certo attore. Dato che i messaggi scambiati tra gli attori sono immutabili, e le computazioni all’interno dei singoli attori avvengono in modo non concorrente, il modello ad attori permette di sviluppare software asincrono e concor-

rente senza dover utilizzare direttamente memoria condivisa da pi`u

thread, con tutta la complessit`a ed i rischi che ne derivano.

Uno dei pi`u grossi problemi dei framework basati sul modello ad attori

`e dato dal fatto che un attore pu`o inviare continuamente messaggi ad un altro attore, anche se l’attore ricevente non `e in grado di proces- sarli ad una velocit`a maggiore o uguale a quella con cui gli vengono inviati. I messaggi inviati ad un attore ed in attesa di essere processati da esso, rimangono nella sua mailbox, che pu`o quindi crescere in mo- do illimitato, occupando tutta la memoria del computer su cui viene eseguito il software.

Questa problematica pu`o essere in teoria risolta elaborando dei proto- colli di comunicazione tra gli attori che notificano all’attore sorgente quando deve smettere di inviare messaggi all’attore destinazione. Tut- tavia questi protocolli sono notevolmente complessi, e non `e efficiente doverli reimplementare in tutte le situazioni in cui sono necessari. Per

evitare questo, nelle versioni pi`u recenti di Akka, `e stata introdotta

l’astrazione di stream, come descritto nel punto seguente.

• Stream: tramite questa astrazione, le computazioni in un sistema software vengono modellate come delle trasformazioni applicate in modo asincrono ad un flusso (stream) di messaggi immutabili.

Il primo framework che ha reso popolare l’astrazione di stream `e si-

Documenti correlati