Analisi dei meccanismi di basso livello per l`IPC su piattaforma
Transcript
Analisi dei meccanismi di basso livello per l`IPC su piattaforma
Facoltà di Ingegneria Corso di Studi in Ingegneria Informatica Elaborato finale in Sistemi Operativi Analisi dei meccanismi di basso livello per l’IPC su piattaforma Android Anno Accademico 2011/2012 Candidato: Alessandro Terracciano matr. N46000716 Indice ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ Introduzione 4 Capitolo 1. Android Architecture 5 1.1 1.2 1.3 1.4 1.5 Linux Kernel Libraries Android Runtime Application Framework Applications 6 6 7 8 8 Capitolo 2. Application Fundamentals 9 2.1 2.1.1 2.1.2 2.1.3 2.1.4 2.2 2.3 2.4 2.4.1 2.4.2 2.4.3 Application Component Activities Services Content Provider Broadcast Receives Intents Manifest File Processes and Threads Processes Process Lifecycle Threads 10 10 11 13 13 14 16 16 17 17 19 Capitolo 3. Interprocess Comunication 20 3.1 3.1.2 3.1.3 3.2 3.3 Binder Communication Model IPC Mechanims Application Launch Considerations 21 21 22 28 30 Conclusioni e Sviluppi Futuri Bibliografia 32 33 III Introduzione ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ Negli ultimi anni si sta assistendo ad una progressiva diffusione dei dispositivi mobili. Tale progresso è dovuto sicuramente ad una significativa evoluzione di tipo hardware, ma soprattutto a un sistema software in grado di adattarsi alle esigenze dell'utente. Parliamo quindi di Android. La piattaforma include un sistema operativo basato su Linux, un insieme di supporti multimediali e offre un ambiente di sviluppo per le applicazioni. Ha come principale scopo quello di fornire tutto ciò che un utente ha bisogno per raggiungere i propri obiettivi. Android è una piattaforma completamente open-source, e la sua altissima diffusione è dovuta al fatto che abbia costi ridotti di accesso e di sviluppo. Inoltre consente una gestione automatica delle applicazioni e la sua estrema portabilità ne garantisce l'implementazione di un gran numero di dispositivi mobili (smartphone, tablet, netbook, ebook reader, ecc.). Fu sviluppato nel 2003 da una startup californiana di nome Android Inc., acquistato poi nel 2005 da Google. Nello stesso anno poi fu fondata l'Open Handset Alliance, un team formato da: operatori telefonici (Vodafone, T-Mobile, Telecom Italia, ecc), produttori di dispositivi mobili (HTC, Motorola, Samsung, ecc.), altri tipi di produttori (Intel, Nvidia, Texas Instruments) e compagnie di sviluppo software e di commercializzazione. Tutto finalizzato a creare standard aperti per dispositivi mobili. 4 Analisi dei meccanismi di basso livello per l’IPC su piattaforma Android Capitolo 1 ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ Android Architecture Android è una collezione di software ben gestito ed organizzato, con la particolarità di potersi adattare a qualsiasi dispositivo mobile. Per tale motivo questa piattaforma riesce a coprire la compatibilità con numerosi apparecchi, garantendone anche l'integrità. Possiede un'architettura abbastanza complessa, ed è costituita da un insieme di moduli, organizzata a livelli, che a loro volta sono formati da un insieme di componenti. Figura 1.1 – Layered Architecture L’intera struttura comprende il sistema operativo, middleware e altre applicazioni importanti. 5 Analisi dei meccanismi di basso livello per l’IPC su piattaforma Android L’architettura[1] è ben rappresentata nella Figura 1.1. Il sistema è composto da cinque sezioni in quattro livelli principali, dove ogni livello fornisce servizi a quello superiore. L'architettura a livelli, infatti, è spesso utilizzata per la sua versatilità e robustezza. Grazie a ciò, si è ridotto il numero di interconnessioni tra i moduli, proprio perchè ogni strato comunica soltanto con quello adiacente. Analizziamo nel dettaglio ogni singolo livello. 1.1 Linux Kernel Il cuore del sistema è basato sul kernel Linux, versione 2.6. In questo livello sono implementati direttamente tutti i driver di controllo hardware del dispositivo, quindi fornisce un'ottima astrazione tra le applicazioni dei livelli superiori con lo strato inferiore al kernel stesso: l'hardware. Inoltre possiede una serie di meccanismi e strategie per la gestione dei processi che saranno chiarite nel capitolo 3. 1.2 Libraries Il livello successivo è costituito dalle librerie native. Si tratta di un insieme di file utilizzati dal dispositivo per poter gestire dati di diversa tipologia. Sono scritte nel linguaggio C o C++, specifiche ad un particolare hardware, e sono accessibili da parte dello sviluppatore attraverso il livello superiore Framework Application. In Figura 1.1 sono presenti alcune librerie di notevole importanza: - Surface Manager: gestisce l'accesso al sottosistema di visualizzazione e compone i livelli grafici 2D e 3D. Si basa sull'off-screen buffering, cioè tutte le operazioni grafiche dei processi vengono prima elaborate su un buffer, e poi vengono composte da un insieme di applicazioni per rappresentarle sul display. - OpenGL: consiste in un supporto per la grafica ad alte prestazioni 2D e 3D. Ha avuto varie versioni, infatti le OpenGL|ES 1.0 e 1.1 sono stati supportati da Android 1.0 e a partire da Android 2.2, è arrivata alla versione 2.2. - SGL: è il motore grafico 2D sottostante, a cui si appoggiano OpenGL e Surface Manager. - Media Framework: fornisce diversi codec multimediali che consentono la 6 Analisi dei meccanismi di basso livello per l’IPC su piattaforma Android registrazione e la riproduzione di diversi formati multimediali. Basato su OpenCORE PacketVideo e supporta diversi formati quali: MPEG4, H.264, MP3, AAC, AMR, JPG e PNG. - FreeType: libreria che si occupa di altri tipi di formati multimediali. - SSL: garantisce la sicurezza delle applicazioni, oramai sostituito da TLS. - SQLite: si tratta di un database engine transazionale molto potente e leggero. A differenza dei comuni database SQL, SQLite effettua operazioni transazionali direttamente su file e non ha un processo server separato. Infatti è possibile modificarne la posizione in memoria senza comprometterne il funzionamento. - WebKit: un moderno motore del browser web che migliora l'utilizzo delle risorse remote. - System C Library: implementazione BSD-derived della libreria standard C System(libc), pensata per dispositivi basati su Linux. 1.3 Android Runtime Da come si intuisce dal diagramma in Figura 1.1, questa parte dell'architettura in realtà non è un vero e proprio livello, perchè fa ancora parte della categoria Libraries. Il fatto che sia definita come sezione a parte, implica che queste librerie siano di notevole importanza ai fini delle elaborazioni dei processi. Parliamo della macchina virtuale che ha il compito di fornire un'ulteriore astrazione alle applicazioni di alto livello. Complessivamente è composto da due componenti: - Core Libraries: comprende un set di librerie di base che fornisce la maggior parte delle funzionalità offerte dal linguaggio di programmazione Java. - Dalvik VM: è una macchina virtuale ottimizzata per l'occupazione di memoria minima. Ogni applicazione Android gira in un proprio processo, con la propria istanza nella macchina virtuale. Dalvik è stato ideato con lo scopo di far girare diverse istanze della macchina virtuale contemporaneamente in modo efficiente. Si basa sul kernel Linux e nasconde al sistema operativo sottostante la gestione della memoria e quella dei thread. 7 Analisi dei meccanismi di basso livello per l’IPC su piattaforma Android 1.4 Application Framework In questo livello Android mette a disposizione un ambiente di sviluppo, offrendo quindi la possibilità agli sviluppatori di poter creare applicazioni nel modo più ricco e innovativo possibile. Questo è possibile perchè essi hanno pieno accesso alle stesse API utilizzate dalle applicazioni di base. L'architettura è stata progettata con lo scopo di poter sfruttare il concetto del riuso delle componenti; chiunque può creare e pubblicare le proprie applicazioni, dando la possibilità ad alti utenti di utilizzarle. Questo stesso meccanismo consente all'utente di sostituire i componenti standard con versioni personalizzate. Alcuni componenti che questo livello comprende sono: - View System: un insieme di risorse estendibile di “Viste” che consentono la creazione di applicazioni offrendo una vasta gamma di strumenti utili. - Content Provider: consente la condivisione di dati o l’accesso di dati condivisi da altre applicazioni. - Activity Manager: gestisce il ciclo di vita delle applicazioni del livello successivo. - Resource Manager: gestisce tutte le risorse usate dalle applicazioni del livello successivo. 1.5 Applications L’ultimo livello dell’architettura consiste in una serie di applicazioni utilizzati dall’utente. Quest’ultime sono scritte in linguaggio Java e apre infinite opportunità per lo sviluppatore. Ci sono diverse applicazioni standard, pre-installate sul dispositivo mobile, per esempio: SMS Client app, Dialer, Web Browser, Contact Manager, e altre applicazioni preferite dall’utente. 8 Analisi dei meccanismi di basso livello per l’IPC su piattaforma Android Capitolo 2 ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ Application Fundamentals Si vuole adesso cercare di capire la logica usata dal sistema per far sì che le applicazioni possano coesistere in modo efficiente. Android, è un sistema operativo multi-utente Linux, dove ogni applicazione è un User diverso. Per impostazione predefinita, questo User viene associato ad un User ID, usato solo dal sistema, e quest’ultimo imposta le autorizzazioni per tutti i file di una applicazione in modo tale che solo l'User ID assegnato a tale applicazione può accedervi. Ogni processo ha una propria macchina virtuale (attraverso la Dalvik VM), quindi il codice dell’applicazione è eseguito in modo isolato. Per default, ogni applicazione gira su un processo Linux, e tale processo può essere avviato, nel caso uno dei componenti dell’applicazione deve essere necessariamente eseguito; fermato, nel caso in cui nessun componente viene eseguito (operazione effettuata per guadagnare spazio in memoria per l’elaborazione di altri processi). In questo modo, Android implementa il principio di privilegio minimo, cioè che ogni applicazione ha accesso solo alle componenti che deve utilizzare. Si crea quindi un ambiente sicuro che evita accessi a parti del sistema non consentite, per esempio ad una applicazione non è consentita l’accesso ai dati di un’altra. Tuttavia, ci sono diversi modi che consentono la condivisione dei dati. E 'possibile, per esempio, che due applicazioni possano condividere lo stesso User ID, in questo caso essi sono in grado di accedere ad altri file. Per risparmiare risorse del sistema, le applicazioni con lo stesso User ID possono anche organizzare l'esecuzione nello stesso processo e condividere la stessa VM. 9 Analisi dei meccanismi di basso livello per l’IPC su piattaforma Android 2.1 Application Component Le applicazioni Android sono formate da un insieme di componenti[2]. La maggior parte di questi, vengono utilizzati dal sistema per accedere all’applicazione stessa. Alcuni dipendono l’una dall’altra, e ognuno svolge un ruolo specifico che aiuta a definire il comportamento generale dell’applicazione. Ci sono quattro diversi tipi di componenti. Ogni tipologia ha uno scopo distinto e specifico, con un ciclo di vita diverso che caratterizza come il componente viene creato e distrutto. 2.1.1 Activities Un’attività[3] è un tipo di componente che fornisce una schermata agli utenti con cui possono interagire al fine di fare operazioni; per esempio comporre il telefono cellulare, scattare una foto, invia una e-mail o visualizzare una mappa. Quando viene eseguita l’applicazione per la prima volta, viene presentata all’utente un’attività “principale”, e può iniziarne altre, al fine di eseguire diverse azioni. Ogni volta che inizia una nuova attività, quella precedente viene arrestata, restando conservata dal sistema in una pila chiamata "Stack Back". E’ uno stack del tipo “last in – first out”, cioè quando l’utente ha terminato l’attività corrente, viene effettuata un’operazione di “pop” sullo stack, dando quindi la possibilità di eseguire attività sospese in precedenza. Nella Figura 2.1 viene illustrato questo meccanismo. Figura 2.1 – Activity con Stack Back 10 Analisi dei meccanismi di basso livello per l’IPC su piattaforma Android Quando un'attività viene fermata da una nuova, viene informata di questo cambiamento di stato attraverso i metodi di callback del ciclo di vita dell'attività. Ci sono metodi di callback diversi che un'attività possa ricevere, e ciascuno offre l'opportunità di svolgere un lavoro specifico. Per esempio, all’atto di un blocco, l’attività dovrebbe rilasciare tutti gli oggetti di grandi dimensioni, come ad esempio le connessioni di rete o un database. Quando invece viene sbloccata, ha la possibilità di riacquistare le risorse possedute in precedenza. Un'attività può avere tre stati essenzialmente: - Resumed: indicato anche come running, è l’attività in primo piano, ovvero quella usata dall’utente al momento. - Paused: attività in primo piano che non è immediatamente utilizzata. Questo capita spesso quando si ha l’esecuzione di più applicazioni contemporaneamente da parte dell’utente. All’alternare dell’utilizzo di ogni applicazione, quest’ultima effettua transizioni dallo stato Resumed a Paused. - Stopped: non è più visibile da parte dell’utente, e si tratta di applicazioni bloccate o sospese. I dati associati vengono conservati in memoria, fino a quando l’applicazione cambia stato o viene rimosso. La rimozione avviene dal sistema se c’è la necessità di ulteriore spazio in memoria da parte di altre applicazioni attive, o dall’utente qualora decide di non utilizzarla più. 2.1.2 Services Un servizio[4] è un componente di un'applicazione in grado di eseguire operazioni di lunga durata in background, e non fornisce un'interfaccia utente. Un componente di una applicazione può avviare un servizio e continuerà a funzionare in background anche se l'utente passa ad un'altra applicazione. Questo può relazionarsi ad un altro servizio, può interagire con esso e può anche eseguire la comunicazione interprocesso (IPC). Ad esempio, un servizio potrebbe gestire le transazioni in rete, ascoltare musica, eseguire file I/O, ecc. Può essere di due tipi: - Started: quando una attività invoca la funzione StartService(), avvia un servizio che viene eseguito in background indefinitamente, anche se l’attività che l’ha 11 Analisi dei meccanismi di basso livello per l’IPC su piattaforma Android creato viene distrutta. In genere esegue un singolo compito, tutto nascosto agli occhi dell’utente. - Bound: un servizio è bound quando è definito da un componente che invoca la chiamata bindService(). Offre una interfaccia client-server che consente ai componenti di interagire con il servizio, inviare richieste, ottenere risultati, e anche farlo attraverso i processi con la comunicazione interprocesso (IPC). Spesso i servizi bound impongono dei vincoli sul suo utilizzo, questo perché solo alcune applicazioni hanno il permesso di utilizzarli. Quando si crea un servizio del genere, ogni applicazione che deve usarla, deve fornire un IBinder, elemento utile per l’interazione con il servizio. Ci sono tre modi per definire l’interfaccia: - Estensione della classe Binder: questa tecnica è preferita quando il servizio è semplicemente un lavoro di background. Se è privato per la propria applicazione e viene eseguito nello stesso processo client, allora è necessario creare l'interfaccia estendendo la classe Binder e restituendo un'istanza (usando la funzione onBind() ). Dopo di che, una applicazione riceve l’oggetto e, tramite i metodi pubblici della classe, riesce ad utilizzare il servizio. - Utilizzo di un Messenger: in questo caso l’interfaccia è munita di un Messaggero, un particolare gestore in grado di rispondere a diversi tipi di oggetti Message. Quindi la condivisione dell’IBinder avviene attraverso uno scambio messaggi con il processo client. Questo è il modo più semplice per eseguire la comunicazione interprocesso (IPC), in quanto le code Messenger possono essere gestite da un singolo thread. - Utilizzo AIDL: in realtà la tecnica precedente si basa su questa, la differenza sostanziale è che mentre il Messaggero gestisce tutti i messaggi con un solo thread (e ciò implica che un solo messaggio alla volta può essere gestito), AIDL effettua l’operazione contemporaneamente, definendo una struttura basata sulla programmazione multi-threading. 12 Analisi dei meccanismi di basso livello per l’IPC su piattaforma Android 2.1.3 Content Provider I Content Provider gestiscono l’accesso ad un insieme strutturato di dati, garantendone la sicurezza e l’integrità. Sono l’interfaccia standard che collega i dati di un processo con l’esecuzione di uno o più processi. Infatti se uno di questi vuole accedere ai dati del Content Provider, deve utilizzare un oggetto ContentResolver per comunicare con il provider. Quest’ultimo riceverà le richieste, eseguirà le rispettive operazioni e restituirà i risultati. Un Content Provider è implementato come una sottoclasse di ContentProvider e deve definire un insieme standard di API che consente ad altre applicazioni per eseguire le transazioni sui dati o file. 2.1.4 Broadcast Receives I Broadcast Receives sono dei componenti utili alle applicazioni per ricevere e rispondere ad annunci broadcast provenienti dal sistema. Per esempio, potrebbero esserci annunci riguardo lo stato dello schermo, della batteria, e altri tipi di stati di altri elementi del dispositivo. Sono privi di interfaccia, nel senso che non sono visibili all’utente, ma utilizzano una barra di stato per le notifiche, che consentono all’applicazione di ricevere gli eventi broadcast dal sistema. Quindi funge da "gateway" per altri componenti, inoltre i compiti che può effettuare sono ridotti, ad esempio, potrebbe avviare un servizio per l’esecuzione di operazioni in base ad un evento. Un aspetto unico di Android è che qualsiasi applicazione può aprire il componente di un'altra applicazione. Ad esempio, se l’utente desidera catturare una foto con la fotocamera del dispositivo, non c’è bisogno di integrare o collegare il codice della applicazione della fotocamera, ma si avvia semplicemente l'attività nella applicazione che cattura una foto. Quando si avvia un componente, inizia il processo per l'applicazione (se non è già in esecuzione) e istanzia le classi necessarie per il componente. A differenza della maggior parte delle applicazioni su altri sistemi, le applicazioni Android non hanno un unico punto di ingresso (non c'è una funzione main(), per esempio). Poiché il sistema esegue ogni applicazione in un processo separato, questa non può direttamente attivare un componente da un'altra applicazione, dato che i permessi dei file limitano l'accesso. 13 Analisi dei meccanismi di basso livello per l’IPC su piattaforma Android Il sistema Android, tuttavia, può. Quindi, per avviare un componente in un'altra applicazione, è necessario inviare un messaggio al sistema per indicarne l'intenzione di attivazione. 2.2 Intents Quando il sistema desidera attivare un componente di una applicazione, lo può fare attraverso un messaggio asincrono: l’intento[5]. Si tratta di un oggetto Intent, una struttura dati che, nel caso di attività e servizi, fornisce una descrizione astratta di una operazione da effettuare; mentre per le notifiche broadcast, definisce una descrizione di qualcosa che è accaduto e che deve essere annunciato. Figura 2.2 – Intent graph Esistono meccanismi separati per fornire intenti ad ogni tipo di componente: - startActivity() per avviare una attività. - startService() per avviare un servizio. - bindService() per stabilire una connessione tra il componente chiamante e un servizio destinazione (se non è già in esecuzione, può avviarlo). - sendBroadcast() per effettuare annunci dal sistema. Un oggetto Intent è un insieme di informazioni, utili sia al componente che riceve l'intento (come l'azione da intraprendere, o i dati da elaborare), sia per il sistema Android (come la categoria del componente che deve gestire l'intento e le istruzioni su come avviare un'attività di destinazione). Principalmente, esso può contenere diversi elementi: - Component Name : Il nome del componente che deve gestire l'intento. Questo campo è un oggetto ComponentName, e viene impostato attraverso le funzioni setComponent(), setClass(), o setClassName(). 14 Analisi dei meccanismi di basso livello per l’IPC su piattaforma Android - Action : una stringa che definisce l'azione da compiere, o, nel caso di intenti broadcast, l'azione che ha avuto luogo e che deve essere segnalata. La classe Intent definisce un certo numero di costanti azioni ( per esempio: ACTION_CALL per avviare una telefonata, ACTION_EDIT per i dati dell’attività di visualizzazione che l’utente può modificare, ACTION_SYNC per sincronizzare i dati su un server con i dati del dispositivo mobile, ecc). - Data : indica l’URI dei dati che devono essere attuati, e il tipo MIME di tali dati. Per esempio se l’azione è ACTION_EDIT, il campo dati conterrà l'URI del documento da visualizzare per l'editing; se l'azione è ACTION_CALL, il campo di dati sarebbe un URI con il numero da chiamare. - Category : è una stringa contenente informazioni aggiuntive sul tipo di componente che dovrebbe gestire l'intento. Come si fa per le azioni, la classe Intent definisce le costanti di categoria (CATEGORY_BROWSABLE per invocare il browser per visualizzare i dati, CATEGORY_HOME per la visualizzazione sulla schermata principale, ecc). - Extra : sono essenzialmente coppie chiave-valore per le informazioni aggiuntive che devono essere consegnate al componente per gestire l’intento. - Flags : possono essere di vario tipo: alcune indicano al sistema Android il modo di avvio di una attività, e come trattarla dopo che è stata lanciata. Gli intenti possono essere suddivisi in due gruppi: Intenti espliciti: si considera il componente di destinazione attraverso il suo nome (il campo nome, citato in precedenza, ha un valore impostato). Poiché i nomi dei componenti generalmente non sono noti agli sviluppatori, gli intenti espliciti sono tipicamente utilizzati per applicazioni interne. Per esempio per avviare una attività subordinata o parallela. Intenti impliciti: sono spesso utilizzati per attivare componenti in altre applicazioni. In questo caso si definisce l’azione da seguire anziché il nome del destinatario. Il problema però, è che in assenza di destinazione, il sistema Android deve trovare il migliore componente che gestisca l'intento. Lo fa confrontando il contenuto dell'oggetto Intent con i filtri intenti associati ai componenti. Ogni filtro descrive una funzionalità del componente, 15 Analisi dei meccanismi di basso livello per l’IPC su piattaforma Android cioè un insieme di intenti che è disposto a ricevere. In questo modo se l’intento è uno esplicito, è sempre consegnato al suo obiettivo (per via del nome), e quindi non importa ciò che contiene, il filtro non viene consultato; se invece è un intento implicito, quest’ultimo può passare solo attraverso i filtri intenti forniti dal componente. 2.3 Manifest File Prima di avviare un componente dell'applicazione, il sistema deve sapere se esiste, e lo fa leggendo il file AndroidManifest.xml dell'applicazione, cioè il file manifesto[6]. L'applicazione deve dichiarare tutti i suoi componenti in questo file, che deve essere alla base della directory di una applicazione. Il manifesto oltre a specificare i componenti dell'applicazione, ha altri compiti quali: - Identificare tutte le autorizzazioni richieste dalle applicazioni (per esempio l'accesso a Internet). - Dichiarare il livello minimo di API richiesto dall'applicazione, per consentire a quest’ultima di utilizzarle. - Definire le caratteristiche hardware e software utilizzate o richieste dall'applicazione, ad esempio una fotocamera, servizi bluetooth, ecc. Come visto nel precedente paragrafo, è possibile avviare e gestire attività, servizi e notifiche broadcast attraverso gli intenti. Quando si dichiara un componente nel manifesto dell'applicazione, è possibile includere i filtri intenti, in modo che possa rispondere ad intenti provenienti da altre applicazioni. 2.4 Processes and Threads Quando un componente di una applicazione viene avviata e l'applicazione non dispone di altri componenti in esecuzione, il sistema Android avvia un nuovo processo Linux, con un singolo thread di esecuzione[7]. Per impostazione predefinita, tutti i componenti della stessa applicazione sono eseguiti nello stesso processo e anche nello stesso thread (definito come thread "main"). Se un componente di una applicazione inizia, ed esiste già un processo per la stessa l'applicazione (perché un altro componente della medesima è già presente), allora 16 Analisi dei meccanismi di basso livello per l’IPC su piattaforma Android viene avviato in quel processo e utilizza lo stesso thread. Tuttavia, è possibile organizzare i componenti della stessa applicazione in processi separati, o creare thread aggiuntivi per qualsiasi processo. 2.4.1 Processes Tutti i componenti della stessa applicazione sono eseguiti nello stesso processo, e la maggior parte delle applicazioni non dovrebbero cambiare questo tipo di gestione. Tuttavia, alcune non rispettano questa condizione, ed è necessario scoprire a quale processo un certo componente appartiene. Questo è possibile farlo considerando il file manifesto, dove ogni tipo di componente supporta un particolare attributo, che specifica il processo in cui tale elemento deve essere eseguito. È possibile impostare questo attributo in modo che ogni componente viene eseguito in un proprio processo, o che alcuni lo condividano, mentre altri non lo fanno, dando quindi la possibilità a più applicazioni di ottenere lo stesso User ID. A volte capita che Android si trova nella condizione di decidere di arrestare un processo, specialmente quando si riduce lo spazio in memoria, che è richiesta da altri processi, ovvero quelli più immediatamente al servizio dell'utente. I componenti delle applicazioni in esecuzione nel processo arrestato, sono di conseguenza distrutti. Al momento di decidere quale deve essere arrestato, il sistema Android pesa la loro importanza in base all’utilizzo dell’applicazione da parte dell'utente. Per esempio, è più probabile che il sistema arresti delle attività non visibili anziché quelle visibili. La decisione di terminare un processo, pertanto, dipende dallo stato dei sui componenti in esecuzione. 2.4.2 Process Lifecycle Il sistema Android tenta di mantenere un processo di applicazione il più a lungo possibile, ma deve prima rimuovere quelli vecchi per recuperare memoria per far spazio ad quelli nuovi o più importanti. Per determinare quali devono essere mantenuti e quali rimossi, il sistema li classifica in una "gerarchia di importanza" in base ai componenti in esecuzione nel processo e allo stato di tali componenti. Quelli meno importanti verranno eliminati 17 Analisi dei meccanismi di basso livello per l’IPC su piattaforma Android prima, poi toccherà a quelli con l'importanza successiva più bassa, e così via, in modo tale da recuperare le risorse del sistema. Ci sono cinque livelli nella gerarchia (definiti in ordine di importanza): 1) Foreground Process: è il tipo di processo in primo piano, che è necessario per le operazioni che l'utente sta facendo. In generale, sono pochi i processi di primo piano che esistono in un dato momento. Essi vengono arrestati per ultimo, tipo se la memoria è così bassa che impedisce al processo stesso di continuare la sua esecuzione. In genere però è difficile arrivare a questo tipo di situazione. 2) Visible Process: non ha componenti in primo piano, ma ancora influenza quello che l'utente vede sullo schermo. Un processo visibile è considerato estremamente importante e non deve essere arrestato. L’arresto avviene solo se è necessario per mantenere tutti i processi in primo piano in esecuzione. 3) Service Process: sono processi di servizio, che l'utente non vede, ma che utilizza comunque (come ad esempio la riproduzione di musica in sottofondo o il download di dati in rete). Il sistema cerca di tenerli sempre in esecuzione, a meno che non c'è abbastanza memoria che consente la coesistenza insieme ai processi di primo piano e visibili. 4) Background Process: anche questi processi non sono visibili dall’utente, ma a differenza dei Service Process, questi non hanno alcuna relazione con le operazioni dell’utente, e il sistema può ucciderli in qualsiasi momento per recuperare la memoria per i processi sopra citati. 5) Empty Process: si tratta di un processo che non possiede componenti delle applicazioni attive. L'unica ragione che giustifica questo tipo di processo in vita, è per scopi di caching, ovvero per migliorare i tempi di avvio di un componente di una applicazione. Il sistema uccide spesso questi processi, al fine di bilanciare le risorse di sistema complessive tra le cache dei processo e le cache del kernel sottostante. 18 Analisi dei meccanismi di basso livello per l’IPC su piattaforma Android 2.4.3 Threads All’avvio di una applicazione, il sistema crea un thread di esecuzione, che prende il nome di "main". Questo thread è molto importante perché ha il compito di fornire tutti gli elementi necessari per definire un’intefaccia utente. All’atto della creazione di un componente, il sistema non crea un thread separato per ogni istanza. Tutti i componenti vengono eseguiti nello stesso processo, e sono istanziati nel thread main. Il modello a unico thread però, appesantisce il carico di lavoro di quest’ultimo. Infatti se il thread esegue operazioni molto lunghe, queste causerebbero il blocco dell’interfaccia utente. Quindi, per risolvere il problema, si può adottare la soluzione di far compiere operazioni che richiedono un tempo non trascurabile in thread separati. In questo caso si ottiene una implementazione dei threads definita come Thread-safe. Tuttavia, poiché con questi meccanismi la complessità delle operazioni crescono, questo tipo di codice può risultare complicato e difficile da mantenere. Una soluzione, è quella di gestire le interazioni più complesse con un thread di lavoro, considerando l'utilizzo di un gestore che elabora i messaggi consegnati dal thread main a quelli separati. 19 Analisi dei meccanismi di basso livello per l’IPC su piattaforma Android Capitolo 3 ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ Interprocess Comunication Nei capitoli precedenti, è stata fatta una panoramica generale di come è strutturato Android, privilegiando quelle che sono le relazioni tra i componenti di questo sistema operativo. In questo capitolo si vuole affrontare il discorso dell’IPC di questa piattaforma. Prima di ciò, però, conviene dare un’occhiata ancora una volta all’architettura di Android, entrando nel dettaglio. Figura 3.1 – Overall Architecture 20 Analisi dei meccanismi di basso livello per l’IPC su piattaforma Android Anche se non sembra, questa rappresenta la stessa architettura raffigurata nella Figura 1.1 del primo capitolo, con la differenza che questa[8] è strutturata in base ai componenti. Infatti sono presenti alcuni elementi che già sono stati accennati in precedenza. Oltre a questi ce ne sono altri in Figura 3.1. Per esempio nella sezione dedicata alle macchine virtuali è presente un elemento di nome Zygote, che si occupa di clonare una Dalvik VM per ogni processo. I blocchetti in figura hanno colori diversi (che li diversifica in base alla tipologia), e sono separati da particolari elementi che ne consentono la relazione tra essi. In particolare tra le applicazioni utente e i componenti del sistema ci sono le API, e tra il kernel Linux e le macchine virtuali c’è una particolare interfaccia definita come JNI. Quest’ultima è di notevole importanza, perché, siccome il kernel è basato sul linguaggio C/C++, e le macchine virtuali e le applicazioni lavorano in Java, la JNI ha il compito di “convetire” il linguaggio da C/C++ in Java e viceversa. Un ultimo elemento non ancora visto nell’architettura è un’elemento di nome Binder, tema principale della tesi, che si occupa della comunicazione interprocesso. 3.1 Binder Come definito nel paragrafo 1.1, il cuore di Android è basato sul kernel Linux 2.6. Pertanto ci si aspetta che i meccanismi per la comunicazione interprocesso, siano quelli forniti da Linux per questo tipo di kernel, come ad esempio elementi del tipo: Signals, Piper, Sockets, Message Queues, Semaphores, Shared Memory, ecc. In realtà Android non fa uso di alcun meccanismo citato, ma si basa su un particolare costrutto chiamato Binder. 3.1.1 Communication Model Il modello[9] di comunicazione interprocesso di Binder è di tipo client-server. Da un lato, quindi, c’è il client che effettua le richieste (munito di interfaccia, chiaramente), dall’altro, invece, è presente il server, che risponde alle richieste del client. Siccome ci sono più client di server, quest’ultimo utilizza un insieme di thread per gestire tutte le richieste concorrenti. 21 Analisi dei meccanismi di basso livello per l’IPC su piattaforma Android Figura 3.2 – Binder Comunication Considerata la Figura 3.2, si nota subito al centro il Binder Driver che funge da ottimo gestore tra i due processi. Il processo A è il client e, tramite l'oggetto proxy, instaura una comunicazione con il Binder. Il processo B, invece, è il processo server, munito di più thread. Il Binder, quindi, verifica la disponibilità del processo B, controllando la possibilità d’utilizzo di ogni thread in esecuzione nel processo stesso. Nel caso in cui ci sia la disponibilità da parte di un thread ad elaborare le richieste, il Binder inoltra il messaggio dal proxy al destinatario. 3.1.2 IPC Mechanism Il Binder utilizza un piccolo modulo kernel personalizzato. Tale modulo, è definito in modo tale da eseguire efficientemente le comunicazioni tra i processi come "migrazione dei thread". Questo effetto, si presenta quando diversi processi utilizzano meccanismi IPC[10], dando l’illusione di avere dei thread che saltano da un processo all’altro. In realtà questo tipo di meccanismo non è implementato, ma è emulato dal kernel. Android utilizza un IPC Binder che mette a disposizione un pool di thread ad ogni processo, usati per gestire gli 22 Analisi dei meccanismi di basso livello per l’IPC su piattaforma Android eventi locali (quindi le comunicazioni) del processo stesso. Oltre all’IPC, il Binder ha anche il dovere di monitorare i riferimenti agli oggetti attraverso i processi. Ciò comporta la mappatura dei riferimenti degli oggetti remoti in un unico processo, mentre l'oggetto reale è presente in quello di accoglienza. In questo modo gli oggetti non vengono distrutti fino a quando altri processi hanno riferimenti su di loro. Quando un thread vuole partecipare ad un IPC (o cominciare un IPC con un altro processo, o di ricevere una chiamata in entrata IPC), la prima cosa che deve fare è aprire il driver fornito dal kernel Binder. Quest’ultimo associa un descrittore di file a quel thread, che il modulo del kernel utilizza per identificare gli iniziatori e i beneficiari dell’IPC Binder. Con questo descrittore di file, tutte le interazioni con il meccanismo IPC avverranno attraverso un piccolo insieme comandi ioctl. I comandi sono: - BINDER_WRITE_READ : invia prima un parametro per definire una particolare operazione (0 nel caso di nessuna operazione), poi attende a sua volta un parametro e restituisce il risultato. - BINDER_SET_WAKEUP_TIME : imposta il tempo previsto dell’evento all’atto di un processo chiamante. - BINDER_SET_IDLE_TIMEOUT : imposta il tempo di inattività del thread (in attesa di una nuova transazione in entrata) prima del time out. - BINDER_SET_REPLY_TIMEOUT : definisce il tempo di attesa dei thread fino al time out. - BINDER_SET_MAX_THREADS : imposta il numero massimo di thread che un driver può creare, per definire il pool di thread di un processo. Il comando più importante è BINDER_WRITE_READ, che è alla base di tutte le operazioni IPC. È necessario garantire che ci sia sempre un thread a disposizione (fino al numero massimo di thread consentito) in modo che l’IPC possa essere elaborato. Il driver si occupa anche del risveglio dei thread nel pool, quando è il momento di elaborare gli eventi asincroni nuovi nel processo locale. 23 Analisi dei meccanismi di basso livello per l’IPC su piattaforma Android Come detto in precedenza, la funzionalità di base del driver, è incapsulato nel funzionamento BINDER_WRITE_READ, ben definita dalla struttura in Figura 3.3: Figura 3.3 – Binder_Write_Read Al richiamo del driver, write_buffer contiene un insieme di comandi di esecuzione, e read_buffer è riempito con una serie di risposte per il thread che deve essere eseguito. In generale, il buffer di scrittura contiene zero o alcuni parametri che definiscono i comandi (solitamente di incremento/decremento dei riferimenti agli oggetti) e termina con un comando che richiede una risposta (come l'invio di una transazione o di un tentativo di acquisire un riferimento di un oggetto). Il buffer di ricezione viene riempito in modo analogo a quello di scrittura, e termina con il risultato dato dall'ultimo comando scritto o un comando che consente l’esecuzione di una nuova operazione nidificata. In Figura 3.4 è presente un elenco di comandi che possono essere inviati da un processo al driver, opportunamente commentato. 24 Analisi dei meccanismi di basso livello per l’IPC su piattaforma Android Figura 3.4 – Binder Driver Command 25 Analisi dei meccanismi di basso livello per l’IPC su piattaforma Android Il comando più interessante è bcTRANSACTION, che avvia una transazione IPC, e restituire una risposta, rispettivamente. La struttura dei dati di questo comando è definita in Figura 3.5. Figura 3.5 – Binder bcTRANSACTION Così, per avviare una transazione IPC, viene eseguita una ioctl BINDER_READ_WRITE con il buffer di scrittura bcTRANSACTION definita da un binder_transaction_data. La struttura è l'handle dell'oggetto che deve ricevere la transazione, formato da codice che indica all'oggetto che cosa fare quando riceve l'operazione, priorità del thread per eseguire l'IPC e un buffer di dati contenente quelli di transazione (ci sarebbe anche un ulteriore buffer (opzionale) per i meta-dati). 26 Analisi dei meccanismi di basso livello per l’IPC su piattaforma Android Quindi, dato l’handle, il driver determina in quale processo questi oggetti vivono, e invia la transazione a uno dei thread in attesa nel suo pool (se è necessario viene creato). Questo thread rimarrà in attesa in una ioctl BINDER_WRITE_READ aspettando l’evento dal driver, per ottenere i comandi necessari per l'esecuzione. Una volta eseguite tutte le operazioni, il thread ritornerà con un comando brTRANSACTION, lo stesso usato per inviare i dati, che si ritrovano disponibili nello spazio locale del processo. Il destinatario, avrà quindi nel suo spazio utente la transazione verso l’oggetto, in modo da effettuare elaborazioni, e restituire i risultati. A questo punto, si definisce un buffer di scrittura con il comando bcREPLY, una struttura binder_transaction_data contenente i dati risultanti. Questo buffer viene restituito con una ioctl BINDER_WRITE_READ sul driver, inviando la risposta al processo originale, e lasciando il thread in attesa dell'operazione successiva da eseguire. Il thread main torna finalmente dalla sua BINDER_WRITE_READ, proprio con il comando brREPLY contenente i dati di risposta. Inoltre il thread originale può anche ricevere comandi brTRANSACTION mentre è in attesa di una risposta, attraverso il meccanismo basato sulla ricorsione. Comunque sarà responsabilità del driver tenere traccia di tutte le transazioni attive, in modo che possa inviarle al thread corretto quando si verifica una ricorsione. Una delle più importanti responsabilità del driver, è quella di eseguire il mapping di oggetti da un processo ad un altro. Ci sono due forme distinte di riferimento ad un oggetto: - come un indirizzo nello spazio di memoria di un processo. - come un handle di 32 bit. Queste rappresentazioni sono mutuamente esclusive, infatti tutti i riferimenti in un processo per oggetti locali sono sottoforma di indirizzo, mentre tutti quelli definiti in un altro processo sono definiti tramite handle, per esempio il campo principale di binder_transaction_data, quando si invia una transazione, contiene un handle per l'oggetto di destinazione. Il destinatario, tuttavia, vede questo come un puntatore all'oggetto nel suo spazio di indirizzi locale. Quindi il driver conserva le mappature di puntatori degli handle tra i processi, in modo tale da effettuare sempre questa traduzione. 27 Analisi dei meccanismi di basso livello per l’IPC su piattaforma Android Il driver consente anche l’invio dei riferimenti agli oggetti attraverso operazioni. Questo viene fatto mettendo il riferimento all'oggetto per il buffer di transazione, lasciando al driver il compito di tradurre questo riferimento a quello corrispondente nel processo ricevente. Per fare la traduzione dei riferimenti, bisogna sapere dove sono situati nei dati di transazione. In questo caso, si considera il buffer aggiuntivo definito come offset, che contiene una serie di indici, che descrivono il luogo in cui sono presenti gli oggetti di transazione. Il driver può quindi riscrivere i dati del buffer, traducendo ciascuno di questi oggetti dal riferimento del processo inviante, al riferimento corretto del processo ricevente. Si noti che il driver non sa nulla di un oggetto Binder particolare, fino a quando non lo invia ad un particolare processo. A quel punto il driver aggiunge l'indirizzo dell'oggetto nella sua tabella di mappatura e chiede al processo proprietario il permesso di mantenerne un riferimento. Quando nessun processo possiede il riferimento dell'oggetto, quest’ultimo viene rimosso dalla tabella e il suo processo proprietario viene avvisato dell’evento. Questo meccanismo evita il mantenimento da parte del driver di un oggetto, finché è utilizzato solo dal suo processo locale. 3.2 Application Launch Dato le conoscenze acquisite finora, in questo paragrafo si cercherà di capire quali sono le operazioni e le fasi dietro all’inizio di una applicazione Android[11]. Come tutti i sistemi Linux, in fase di avvio, il bootloader carica il kernel ed avvia il processo init. Prima di tutto l’init avvia i processi Linux di basso livello, per la gestione delle interfacce hardware. Dopo di che, viene avviato Zygote, processo che inizializza l'istanza della prima macchina virtuale Dalvik e pre-carica tutti i framework più comuni utilizzate dalle varie applicazioni. Zygote si mette poi in ascolto su un’interfaccia, per le future richieste di creazione di macchine virtuali per i processi di nuove applicazioni utili al sistema, infine avvia un processo ben gestito chiamato server di sistema. Tutto inizia da una piattaforma di base, e hanno la priorità d’avvio tutti i servizi manager e hardware di questo contesto. A questo punto il sistema è pronto a lanciare tutte le applicazioni home. 28 Analisi dei meccanismi di basso livello per l’IPC su piattaforma Android Figura 3.6 – Application Launch Quando l’utente clicca su una applicazione, l’evento (in Figura 3.6) si traduce in una chiamata all’intento startActivity() che, come descritto nel paragrafo 2.2 effettua una richiesta per la creazione di una attività. Tale oggetto, attraverso il Binder IPC, viene indirizzato all’Activity Manager Service. Quest’ultimo, ha il compito di fare alcune azioni: 1) Il primo passo è quello di raccogliere informazioni sull'oggetto intento. Questo viene fatto utilizzando il metodo resolveIntent(), e le informazioni di destinazione vengono salvate nell’intento stesso (per evitare di rifare questo passo). 2) Il passo successivo è quello di verificare se l'utente dispone di privilegi sufficienti per richiamare il componente di destinazione dell'intento. Questa operazione viene eseguita chiamando il metodo grantUriPermissionLocked(). 29 Analisi dei meccanismi di basso livello per l’IPC su piattaforma Android 3) Superate le fasi precedenti, si passa a verificare l’esistenza del processo di quella tipologia, controllando il ProcessRecord. Se questo è nullo allora l’Activity Manager Service deve provvedere alla creazione di un nuovo processo. Ci sono tre fasi distinte per il lancio del processo: 1) Process Creation: viene creato un nuovo processo richiamando il metodo startProcessLocked(), che invia argomenti dal processo Zygote tramite la sua interfaccia. Quest’ultimo crea un'istanza di un thread (ActivityThread) munito di macchina virtuale, restituendo il pid del processo appena creato. 2) Application Binding: in questa fase si collega il processo alla specifica applicazione, preparando gli strumenti necessari in memoria, utili per l’elaborazione. L’Activity Manager chiama quindi bindApplication sull'oggetto thread, che invia un messaggio BIND_APPLICATION alla coda di messaggi, e viene recuperato dall'oggetto handler che, tramite la funzione handleMessage(), attiva l'azione specifica dei messaggi con handleBindApplication(). L’Activity Manager, a sua volta, richiama makeApplication(), che carica specifiche classi per le applicazioni in memoria. 3) Activity Launch: dopo la fase precedente, il sistema contiene il processo associato all'applicazione, con le classi private caricate in memoria. Ora avviene l’effettiva creazione dell’attività con il metodo realStartActivity() che chiama sheduleLaunchActivity() sull'oggetto thread dell'applicazione, inviando il messaggio LAUNCH_ACTIVITY alla coda messaggi. L'attività inizia il suo ciclo di vita gestito con onCreate(), viene in primo piano con onRestart() e inizia a interagire con l'utente con il metodo onStart(). 3.3 Considerations Infine, si vuole spiegare il motivo per cui Android utilizza il tipo di meccanismi precedentemente descritti, in alternativa a quelli offerti dal kernel Linux (Signals, Piper, Sockets, Message Queues, Semaphores, Shared Memory). Il problema principale sta nel codice. In genere, per consentire un’ottima comunicazione tra i processi, bisogna avere un 30 Analisi dei meccanismi di basso livello per l’IPC su piattaforma Android insieme di implementazioni che garantiscano un IPC efficiente. Nel campo informatico, l’efficienza di un codice si basa anche sulla complessità e considerando l’importanza di questi elementi, si lavora quindi con un codice abbastanza corposo. Per Android è un enorme problema, perchè questo sistema operativo ha il compito di privilegiare l’interattività con l’utente. Infatti, con il modello di processo descritto precedentemente, gli sviluppatori non hanno la necessità di conoscere di quale processo un oggetto Binder fa parte, ma possono trattare semplicemente qualsiasi istanza come se fosse elemento appartenente al processo locale. Si evidenzia subito uno dei vantaggi di questa flessibilità, ovvero che il Binder è in grado di funzionare anche in situazioni estreme. Infatti se il driver del kernel, che attua il meccanismo IPC Binder, non è disponibile, sarà possibile eseguire tutti gli elementi in un unico processo. In questo modo se ne garantisce anche l’affidabilità dell’intero sistema. Insomma, la logica utilizzata che ha favorito la scelta di tali meccanismi, è finalizzata alla definizione di un sistema operativo in grado di mantenere la dinamicità e l’efficienza allo stesso tempo, infatti questo spiega anche il motivo per cui Android può essere installato su diverse macchine, evitando particolari modifiche hardware, ma soprattutto consentendo la personalizzazione dei tool, utili sia al sistema che all’utente. 31 Conclusioni e Sviluppi Futuri ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ Android è destinato a diventare il sistema più diffuso e conosciuto rispetto ad altri, data la sua crescita in questo ultimo periodo. Infatti Andy Rubin, capo di uno dei più importanti settori di Google (cioè quello dedicato allo sviluppo di Android), afferma che "L'obiettivo del progetto Open Source Android è quello di creare un vero e proprio successo, che migliora l'esperienza mobile degli utenti". E non solo. Questo sistema tende anche ad estendere i propri domini in applicazioni quotidianamente usate (per esempio avremo l’estensione delle funzionalità di Google Chrome e di Abode). Il punto di forza deriva sicuramente dal fatto che il sistema operativo è opensource, pertanto se si considera il numero di sviluppatori che dedicano il loro lavoro ad applicazioni Android, si intuisce subito che il suo progresso avviene in maniera esponenziale. Nonostante sia una piattaforma giovane, Android è stato ideato con aspetti estremamente potenti, avendo in futuro la possibilità di diventare una risorsa indispensabile ai fini di soddisfare una utenza sempre più esigente. 32 Bibliografia ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ [1] Google, Android Developers, August 2011 http://developer.android.com/about/versions/index.html [2] Google, Android Developers, Android API Guides, “Application Fundamentals”, August 2011 http://developer.android.com/guide/components/fundamentals.html [3] Google, Android Developers, Android API Guides, “Activities”, August 2011 http://developer.android.com/guide/components/activities.html [4] Google, Android Developers, Android API Guides, “Services”, August 2011 http://developer.android.com/guide/components/services.html [5] Google, Android Developers, Android API Guides, “Intents and Intent Filter”, August 2011 http://developer.android.com/guide/components/intents-filters.html [6] Google, Android Developers, Android API Guides, “Android Manifest”, August 2011 http://developer.android.com/guide/topics/manifest/manifest-intro.html 33 [7] Google, Android Developers, Android API Guides, “Processes and Threads”, August 2011 http://developer.android.com/guide/components/processes-and-threads.html [8] “Android Internals – Android Builders Summit”, Karim Yaghmour, April 2011 http://events.linuxfoundation.org/slides/2011/abs/abs2011_yaghmour_internals.pdf [9] “Binder Android – Android Interprocess Comunication”, RUB University, Thorsten Schreiber, October 2011 http://www.nds.rub.de/media/attachments/files/2012/03/binder.pdf [10] “Binder IPC Mechanism”, PalmSource Unc., August 2005 http://www.angryredplanet.com/~hackbod/openbinder/docs/html/BinderIPCMechan ism.html [11] “Android Application Launch”, Radhika Karandikar, April 2010 http://multi-core-dump.blogspot.it/2010/04/android-application-launch.html 34