Caratteristiche, Architettura e Kernel del sistema operativo Android

Caratteristiche, Architettura e Kernel del sistema operativo Android

Android è un sistema operativo open source per dispositivi mobili costituito da uno stack software che include un sistema operativo, un middleware e applicazioni di base.
Nel corso degli anni, Android è arrivato ad essere la piattaforma mobile più diffusa al mondo. Infatti nel secondo trimestre del 2012, Android domina il mercato con una quota di 68,1%, rispetto all’iOS di Apple che ha solo 16,9% come mostrato nella figura seguente. Tramite il negozio online Google Play Store si ha accesso a più di un milione di applicazioni, milioni di libri e brani audio e migliaia di film.

Quote di mercato approssimate per i sistemi operativi degli smartphone
Quote di mercato approssimate per i sistemi operativi degli smartphone per il secondo trimestre del 2012

Storia di Android

Il sistema operativo ha origini nell’acquisizione di Android, Inc. da parte di Google nel 2005. L’azienda è stata fondata nel 2003 da Andy Rubin (co-fondatore di Danger) e Nick Sears (ex-vicepresidente di T-Mobile). Nel 2007, prende vita la Open Handset Alliance (OHA), un consorzio formato da 84 aziende di cui Google è capofila. I membri includono operatori mobili, costruttori di telefoni, aziende di semiconduttori, aziende software e aziende di commercializzazione. Nel 5 novembre dello stesso anno OHA presentò publicamente Android. Dopo una settimana è stato rilasciato il Software Development Kit (SDK) che include: gli strumenti di sviluppo, le librerie, un emulatore del dispositivo, la documentazione e alcuni progetti di esempio e tutorial.

Nel 2008 è uscita la versione 1.0 di Android insieme al primo dispositivo reale, ovvero HTC Dream, conosciuto anche come T-Mobile G1 in alcuni mercati (nella figura seguente). A febbraio 2009 esce la versione 1.1 che porta piccole correzioni e alcuni bugfix. Però le più importanti versioni lanciate nel 2009 sono 1.5 Cupcake e 1.6 Donut. La prima introduce una maggior integrazione con i servizi Google e aggiunge il pieno supporto ai widget, mentre la seconda integra funzionalità a voce come la ricerca e la sintesi vocale, supporto alle reti CDMA, diverse risoluzioni di schermo insieme ad altre piccole novità generali. Dalla 1.5 in poi, ogni versione avrà un nome di dolce che rispetterà un ordine alfabetico.

HTC Dream - il primo dispositivo Android
HTC Dream – il primo dispositivo Android

A poche settimane del rilascio della versione 1.6, Google ha lanciato Android 2.0 Eclair, che porta alcune novità come il supporto per il Bluetooth 2.1, supporto all’HTML 5, migliorie alla fotocamera e sincronizzazione con account multipli. Nel gennaio 2010 è stato rilasciato l’Android SDK 2.1 che corregge piccoli bug di sistema. Il 20 maggio 2010 al Google I/O conference è stato rilasciato l’Android SDK 2.2 Froyo. Questa versione contiene importanti aggiornamenti: nuovo kernel linux 2.6.32, nuovo compilatore JIT16, V8 Engine per il javascript, tethering Wi-fi nativo per utilizzare il terminale come Hotspot Wireless, nuove icone per la Home, Adobe Flash Player 10.1 e Adobe AIR integrato, possibilità di installare le apps sulla memoria SD, aggiornamento automatico Over-the-Air delle applicazioni e nuove API. Il primo smartphone al mondo a ricevere Froyo è stato Google Nexus One prodotto da HTC, il primo smartphone Android della serie Nexus by Google. per gli sviluppatori, tra cui le OpenGL ES 2.0. Questa versione è caratterizzata da una velocità maggiore, performance e fluidità rispetto alla precedente versione 2.1 Eclair.

La versione Android 2.3 Gingerbread ha avuto il suo lancio nel dicembre 2010 con grande successo, e oggi è la versione più diffusa e utilizzata tra tutti i terminali Android. La versione del kernel Linux su cui si basa Gingerbread è la 2.6.35. Il JIT compiler è stato ulteriormente ottimizzato, i driver OpenGL ES sono stati aggiornati ed è stato aggiunto il supporto alle API audio Khronos OpenSL ES. La novità più grande è però il supporto alla tecnologia Near Field Communication (NFC), che consentirà ai possessori di terminali con chip NFC di poter usare il terminale come strumento per effettuare pagamenti. A seguire, nei mesi del 2011, sono state rilasciate alcune nuove versioni al fine di correggere i vari bug del software: sono uscite 5 versioni correttive di Gingerbread numerate dalla 2.3.3 alla 2.3.7. Nel febbraio del 2011 viene rilasciato il sistema 3.0 (Honeycomb) dedicato ai soli tablet. Honeycomb ha messo delle basi solide per il mondo tablet Android, ma ha rappresentato anche un sistema operativo poco ottimizzato. Durante il resto del 2011 sono usciti alcuni update che però hanno introdotto poche novità. Il 19 ottobre 2011 Android 4.0 Ice Cream Sandwich fa il suo debutto contemporaneamente alla presentazione del nuovo Samsung Galaxy Nexus. Questa versione è destinata sia per smartphone, sia per tablet, e intende di risolvere i problemi della Honeycomb. Le principali novità sono l’aumento delle prestazioni, il Face Unlock, ovvero il sistema che permette di sbloccare lo smartphone tramite riconoscimento facciale, la rimozione del ritardo negli scatti della fotocamera e la nuova interfaccia utente completamente riprogettata chiamata “Holo UI”.

L’ultima versione del sistema operativo Android è la 4.1 Jelly Bean, che è stata lanciata nel giugno 2012. Tra i cambiamenti si notano: riconoscimento del tocco migliorato, ottimizzato l’utilizzo della CPU, miglioramenti notevoli nella fluidità grazie a Project Butter, importanti miglioramenti nell’applicazione fotocamera, nuove funzionalità per la condivisione di foto e video tramite NFC e Google Play aggiornato.

Statistiche della distribuzione delle varie versioni sul totale dei dispositivi Android
Statistiche della distribuzione delle varie versioni sul totale dei dispositivi Android, aggiornate al 2012

Dalvik Virtual Machine

Per lo sviluppo delle applicazioni, Google ha scelto come linguaggio quello di Java, invece di creare uno nuovo. Questa scelta tuttavia va in parte in contrasto con la filosofia open di Android. I dispositivi che vorranno adottare la Virtual Machine (VM) associata all’ambiente J2ME devono pagare una royalty, quindi in totale disaccordo con la licenza Apache adottata.

Per evitare le royalty che doveva pagare a Sun Microsystems (poi acquistata da Oracle), Google ha deciso di creare una propria macchina virtuale, progettata da Dan Bornstein. Essa prende il nome di Dalvik Virtual Machine (DVM), nome che deriva dal villaggio di cui la famiglia di Bornstein è originaria. Si tratta di un Virtual Machine ottimizzata per sfruttare la poca memoria presente nei dispositivi mobili. Prima di essere installati su un dispositivo, i file .class con bytecode Java sono convertiti in file .dex (Dalvik Executable) che sono poi eseguiti dalla DVM. Utilizzando questa macchina virtuale alternativa comporta anche una sensibile riduzione della memoria richiesta dall’esecuzione delle applicazioni. Un file .dex non compresso è tipicamente con qualche percentuale più piccolo del file .jar compresso derivato dallo stesso file .class. Dalla versione 2.2 è stato incluso un compilatore Just-In-Time per migliorare le prestazione della macchina virtuale. Si tratta di un meccanismo attraverso il quale la VM riconosce determinati pattern di codice Java traducendoli in frammenti di codice nativo (C e C++) per una loro esecuzione più efficiente.

Una differenza importante con la JVM tradizionale è il meccanismo di generazione del codice che viene detto register based (orientato all’utilizzo di registri) a differenza di quello della JVM detto stack based (orientato all’uso di stack). Attraverso questa architettura è possibile di ridurre con 30% il numero di operazioni da eseguire, a parità di codice Java. Un’altra caratteristica esenziale della DVM è quella di permettere una efficace esecuzione di multiple istanze della VM e quindi più processi contemporaneamente. Ogni applicazione è eseguita all’interno del proprio processo Linux e ciò porta dei vantaggi dal punto di vista delle performance.

Architettura Android
Architettura Android

Architettura di Android

Android ha un’architettura di tipo gerarchico, strutturata a layer a complessità crescente dal basso verso l’alto, come mostrato nella figura seguente. I layer comprendono un sistema operativo, un insieme di librerie native per le funzionalità core della piattaforma, una implementazione della VM e un insieme di librerie Java.

Il kernel

Il livello più basso è rappresentato dal kernel Linux nella versione 2.6 e 3.x (da Android 4.0 in poi) che costituisce il livello di astrazione di tutto l’hardware sottostante. Si noti la presenza di driver per la gestione delle periferiche multimediali, del display, delle connessione Wi-Fi e Bluetooth, dell’alimentazione, del GPS, della fotocamera. I produttori di telefoni possono quindi intervenire già a questo livello per personalizzare i driver di comunicazione con i propri dispositivi. Grazie all’astrazione dell’hardware, infatti, i livelli soprastanti non si accorgono dei cambiamenti hardware, permettendo una programmazione ad alto livello omogenea ed una user experience indipendente dal device.

La scelta verso l’utilizzo di un kernel Linux si spiega attraverso la necessità di avere un’alta affidabilità. L’affidabilità è la più importante delle prestazioni in un dispositivo mobile che deve principalmente garantire il servizio di telefonia: gli utenti si aspettano quindi tale affidabiltà, ma allo stesso tempo hanno bisogno di un dispositivo che possa garantire servizi più evoluti: Linux permette di raggiungere entrambi gli scopi.

Android Runtime e le librerie native

Salendo nella gerarchia troviamo un insieme di librerie native realizzate in C e C++ e l’Android Runtime. L’ambiente di runtime è costituito dalle librerie core Java e dalla macchina virtuale Dalvik (DVM): insieme costituiscono la piattaforma di sviluppo per Android.

Le librerie invece fanno riferimento a un insieme di progetti Open Source e sono descritte di seguito:

  • Surface Manager (SM), componente fondamentale in quanto ha la responsabilità di gestire le View, ovvero i componenti dell’interfaccia grafica. Il suo compito è coordinare e impostare i diversi layer delle finestre, utilizzando il double buffering. Il Surface Manager permette l’accesso alle funzionalità del display e la visualizzazione contemporanea di grafica 2D e 3D delle diverse applicazioni.
  • OpenGL ES (for Embedded Systems), una versione ridotta di OpenGL per sistemi embedded. Essa comprende un insieme di API multipiattaforma che forniscono l’accesso a funzionalità 2D e 3D nei dispositivi mobili.
  • SGL (Scalable Graphics Library), libreria in C++ che costituisce il motore grafico di Android insieme alle OpenGL. Se per la grafica 3D ci si appoggia all’OpenGL, per quella 2D viene utilizzato invece un motore ottimizzato chiamato SGL. È una libreria utilizzata principalmente dal Window Manager e dal Surface Manager all’interno del processo di renderizzazione grafica.
  • Media Framework, API in grado di gestire i diversi codec per i vari formati di acquisizione e riproduzione audio e video. Si basa su una libreria open source OpenCore di PacketVideo, uno dei membri fondatori dell’OHA. I codec gestiti dal Media Framework permettono la gestione dei formati più importanti tra cui MPEG4, H.264, MP3, AAC, AMR oltre quelli per la gestione delle immagini come JPG e PNG.
  • FreeType, libreria di piccole dimensioni, molto efficiente e altamente personalizzabile per il rendering dei font. Una caratteristica importante è quella di fornire un insieme di API semplici per ciascun tipo di font in modo indipendente dal formato del corrispondente file.
  • SSL (Secure Socket Layer), libreria per la sicurezza della comunicazione su internet.
  • SQLite, libreria scritta in C che implementa un DBMS20
  • WebKit, framework per la navigazione web utilizzato già da diversi browser come Safari e Chrome. Si tratta di un browser engine open-source basato sulle tecnologie HTML, CSS, JavaScript e DOM. Un aspetto da sottolineare è che WebKit occupa un’aspetto di browser engine quindi può essere integrato in diversi tipi di applicazioni. relazionale caratterizzato dal fatto di essere compatto, diretto, di non necessitare alcuna configurazione e soprattutto essere transazionale. Permette di creare una base di dati incorporata in un unico file ed è diretto in quanto non utilizza un processo standalone, ma può essere incorporato all’interno dell’applicazione che lo usa.
  • LibC, implementazione della libreria standard C ottimizzata per dispositivi basati su Linux embedded.

Application Framework

Al prossimo livello si trova l’Application Framework (AF), costituito da un insieme di componenti che utilizzano le librerie native. Si tratta di un insieme di API e componenti per l’esecuzione di funzionalità di base del sistema Android.
I componenti dell’AF sono:

  • Activity Manager – lo strumento fondamentale che gestisce il ciclo di vita delle activity di un’applicazione. L’activity è una singola schermata che permette la visualizzazione, la raccolta di informazioni e l’interazione con l’utente. Questo componente ha la responsabilità di organizzare le varie schermate di un’applicazione in un unico stack a seconda dell’ordine di visualizzazione delle stesse sullo schermo.
  • Package Manager – gestisce il ciclo di vita delle applicazioni nei dispositivi analogamente a quanto avviene in J2ME da parte del Java Application Manager (JAM).
  • Window Manager – componente molto importante che permette di gestire le finestre delle diverse applicazioni, gestite da processi diversi, sullo schermo dispositivo.
  • Telephony Manager – gestore delle funzionalità caratteristiche di un telefono come la semplice possibilità di iniziare una chiamata o di verificare lo stato della chiamata stessa
  • Content Provider – componente che gestisce la condivisione di informazioni tra i vari processi.
  • Resource Manager – componente che ha la responsabilità di gestire le risorse che oltre il codice compongono una applicazione (immagini, file di configurazione, file di definizione del layout, ecc). Per le risorse, come avviene già per il codice, esiste un processo di trasformazione delle stesse in contenuti binari, ottimizzati per il loro utilizzo all’interno di un dispositivo.
  • View System – gestore del rendering dei componenti dell’interfaccia grafica nonché della gestione degli eventi associati.
  • Location Manager – componente che mette a disposizione le API necessarie per creare applicazioni che gestiscono la localizzazione del dispositivo.
  • Notification Manager – componente che fornisce diversi strumenti che un’applicazione può utilizzare per inviare notifiche al dispositivo, il quale le dovrà presentare all’utente.
  • XMPP Service (Extensible Messaging and Presence Protocol) – insieme di protocolli open di messaggistica istantanea basato su XML (Extensible Markup Language).

Application layer

Al livello più alto si trova il layer delle applicazioni native come telefono, contatti, browser e di terze parti. Le funzionalità base del sistema, come per esempio il telefono, non sono altro che applicazioni utente scritte in Java e che girano ognuna nella sua VM. A questo livello verrano installate le app create dall’utente o scaricate dal Play Store.

Componenti di un’applicazione

Ci sono cinque blocchi esenziali che costituiscono un’applicazione Android:

  1. Activity
  2. Intent e Intent Filter
  3. Service
  4. Content Provider
  5. Broadcast Receiver

Activity

Una parte fondamentale dell’applicazione è rappresentata dalla gestione dell’interfaccia grafica. Essa viene realizzata attraverso la definizione di activity descritte tramite la classe Activity del package android.app. Un’Activity è un componente dell’applicazione che fornisce una schermata con cui gli utenti possono interagire. Una normale applicazione consisterà in una sequenza di activity. Quindi, abbiamo diverse schermate che si alternano sul display comunicando eventualmente tra di loro e scambiandosi delle informazioni.

Tipicamente, una tra le activity di un’applicazione è un’activity “main”, cioè sarà visualizzata per prima all’avvio dell’applicazione. Ogni activity può iniziare una nuova activity per eseguire diverse azioni. A tale scopo la piattaforma organizza le attività secondo una struttura a pila (il “back stack”) dove l’attività in cima è sempre quella attiva. La visualizzazione di una nuova schermata, che corrisponde all’avvio di una nuova Activity, la porterà in cima allo stack mettendo in pausa quelle precedenti. Lo stack funziona secondo il principio “Last In, First Out” (LIFO) e quindi quando l’utente preme il bottone Back, l’attività corrente è rimossa dallo stack e eliminata e l’activity precedente viene ripristinata.

Nel momento in cui un’attività è chiusa, il cambio di stato è notificato tramite le callback methods (metodi di richiamata) del ciclo di vita dell’activity (mostrato nella figura seguente). Ci sono alcuni metodi callback che un’attività può ricevere al cambiamento del suo stato e ognuna di loro permette di implementare dati opportuni per quel cambio di stato. L’implementazione di questi metodi rimane il compito dello sviluppatore. Per esempio, si possono inizializzare degli oggetti alla creazione dell’attività oppure di rilasciare degli oggetti alla chiusura della stessa.

Diagramma con i cicli di vita di un’Activity
Diagramma con i cicli di vita di un’Activity

Questa figura illustra la sequenza di chiamate ai metodi di Activity eseguite durante i passaggi di stato dell’attività:

protected void onCreate(Bundle savedIstanceState)
Metodo che deve essere implementato obbligatoriamente. È chiamato non appena l’attività viene creata. L’argomento savedInstanceState serve per riportare un eventuale stato dell’attività salvato prima di essere eliminata dal sistema. L’argomento è null nel caso in cui l’attività non abbia uno stato salvato.

protected void onStart()
Chiamato per segnalare che l’attività sta per arrivare in cima allo stack per potere essere visualizzata sul display.

protected void onResume()
Chiamato per segnalare che l’attività è in primo piano e può interagire con l’utente. Sempre seguita da onPause().

protected void onPause()
Chiamato quando il sistema è sul punto di riprendere un’altra attività e l’attività corrente non sta più interagendo con l’utente. Seguito da onResume()se l’attività ritorna in primo piano, oppure da onStop() se diventa invisibile per l’utente.

protected void onStop()
Chiamato per segnalare che l’attività non è più visibile sullo schermo. Seguita da onRestart() se l’attività arriva di nuovo in primo piano, oppure da onDestroy() se l’activity viene distrutta.

protected void onRestart()
Chiamato prima di essere riavviata l’attività, dopo un precedente arresto. Sempre seguita da onStart().

protected void onDestroy()
Metodo chiamato prima che l’activity viene distrutta.

La procedura standard richiede che, come prima riga di codice di ciascuno di questi metodi, dev’essere richiamata l’implementazione di base del metodo che si sta ridefinendo:

@Override
protected void onStart() {
super.onStart();
// qui viene inserito il proprio codice
}

Intent e Intent Filter

L’Intent è un meccanismo di messagi che può attivare tre dei componenti di base di un’applicazione: activity, service e broadcast receiver. L’oggetto Intent è una struttura dati passiva che contiene una descrizione astratta dell’operazione da eseguire. L’intent contiene informazioni per il componente che riceve l’intent (come l’azione da eseguire e i dati su cui agire) e per il sistema Android (come la categoria dei componenti che dovrà maneggiare l’intent e instruzioni riguardo il lancio dell’attività prevista).
Gli intents sono di due tipi:

  • Explicit (espliciti): indicano il componente obiettivo con il suo nome. I nomi dei componenti di altre applicazioni non sono conosciuti e per questo motivo gli intents espliciti sono usati tipicamente per messagi interni.
  • Implicit (impliciti): non indicano nessun componente. Sono di solito usati per attivare componenti in altre applicazioni.

L’IntentFilter invece è una descrizione dell’insieme di intent impliciti che un componente dell’applicazione può gestire. Esso, in prattica, filtra gli intent di un certo tipo.

Service

Un Service (servizio) è un componente dell’applicazione atto ad eseguire operazioni a lungo termine in background, senza fornire alcuna interfaccia utente. Se un componente applicativo attiva un Service, quest’ultimo inizia la sua esecuzione in background, continuandola anche se l’utente passa ad un’altra applicazione. In aggiunta, un componente puo collegarsi ad un servizio con cui vuole interagire e quindi attuare una comunicazione inter-processo (IPC): per esempio, un servizio può fornire funzionalità di gestione di connessioni di rete, riprodurre musica, fare I/O di file su memoria di massa o interagire con un Content Provider, tutto questo dal background.
Un Service può essere di due tipi:

  • Started: un servizio è “started” quando un componente dell’applicazione (come un’Activity) lo fa partire chiamando il metodo startService(). Una volta partito, un Service può girare in background per un tempo indefinito e può continuare anche se il componente che l’ha messo in esecuzione e stato distrutto. Di solito un servizio lanciato esegue una singola operazione e non ritorna alcun valore al chiamante. Per esempio, può scaricare o caricare un file su internet: quando l’operazione e conclusa, il servizio si ferma in modo autonomo.
  • Bound: un servizio si dice “bound” quando un componente dell’applicazione si connette ad esso chiamando il metodo bindService(). Un Service di tipo “bound” offre un’interfaccia client-server che permette ai componenti di interagire con esso, inviare richieste, ottenere risultati, e addirittura fare tutto questo tra i processi attraverso l’utilizzo della comunicazione inter-processo (IPC). Un servizio “bound” esegue fino a quando un componente rimane legato ad esso. È possibile legare nello stesso tempo più componenti ad un solo servizio e solo quando tutti si disconnettono, il Service viene distrutto.

Le due tipologie di servizi possono entrare in esecuzione in entrambe le modalità: possono essere “started” (per eseguire in un tempo indefinito) e nello stesso tempo permettere anche il binding da parte di altri componenti. Il tutto dipende da quale metodo di callback viene implementato all’interno del componente: onStartCommand() permette al componente di avviare il servizio, mentre onBind() permette il binding. Senza considerare il modo con cui il Service e stato avviato (“started”, “bound” o entrambi), qualsiasi componente dell’applicazione può utilizzarlo (anche da un’applicazione separata) nella stessa maniera in cui ogni componente può usare un’activity: avviarlo con un Intent. Tuttavia, per ragioni di sicurezza, è possibile dichiarare il Service come privato (nel file Manifest) e bloccare le richieste di accesso da parte di altre applicazioni.

Content Provider

I Content Provider gestiscono l’accesso ad un set di dati strutturati. Android viene rilasciato con una varietà di Content Provider per tipi di dati comuni (audio, video, immagini, informazioni di contatto personali, ecc.). Per rendere pubblici i propri dati ci sono due possibilità: creare un Content Provider personalizzato (sottoclasse di ContentProvider) oppure aggiungere i dati ad un Content Provider esistente (se ne esiste uno che riesce a controllare gli stessi dati di riferimento e se si possiedono i permessi di scrittura). Ogni applicazione può definire una o più tipologie di dati e rendere poi disponibili tali informazioni esponendo uno o più Content Provider. Nell’ordine inverso, invece, qualunque applicazione può richiedere l’accesso ad un particolare tipo di dato: il sistema la metterà in contatto con il corrispondente Content Provider precedentemente installato nel sistema.

Broadcast Receiver

Il Broadcast Receiver è un componente che gestisce eventi di broadcast lanciati dal sistema. Il sistema genera molti messaggi di broadcast (per esempio un messaggio che annuncia che lo schermo è stato spento, che la carica della batteria è bassa o che è stata appena scattata una foto) e tale funzionalità è svolta anche dalle applicazioni (per esempio per notificare al resto del sistema che dei dati sono stati scaricati e sono disponibili per l’uso). I Broadcast Receiver (come i Service) non mostrano un’interfaccia utente, ma possono creare una notificazione sulla status bar per avvertire l’utente che un determinato evento è avvenuto. Più comunemente può essere assimilabile ad un “gateway” per gli altri componenti, utilizzato per eseguire una quantità minima di lavoro.

Pubblicato da Vito Lavecchia

Lavecchia Vito Ingegnere Informatico (Politecnico di Bari) Email: [email protected] Sito Web: https://vitolavecchia.altervista.org

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *