Travaux Pratiques   

tp9 Service, Receiver

Thème: Activation d'un service de réception et filtrage de SMS

  • Un service installe un receveur de SMS, ce receveur est prioritaire et pourra décider d'interrompre la délivrance du SMS aux applications candidates, interruption totale possible avant la version kitkat, après les sms sont transmis à l'application par défaut (une solution possible est en bas de page).
  • Ce service transmet le contenu du SMS à une chaîne de responsabilités qui en fonction du contenu reçu déclenche la commande associée ou poursuit la transmission au prochain candidat dans la chaîne.
  • Dès le cours sur les capteurs et la géo-localisation effectué ... L'objectif sera de se rapprocher des fonctionnalités d'un traceur du commerce. Les traceurs proposent en général un contrôle/commande par SMS, et délivrent à la demande les positions actuelles et avertissent lors d'une sortie d'un périmètre donné, d'un câble débranché, d'un soulèvement...

    Exemple de traceurs
       http://www.geotraceur.fr/content/19-traceur-gps-moto,
       http://www.mouchardgps.fr/traceur-antivol-moto,
       https://www.pearl.fr/article/PX3490/traceur-gsm-gt-60-avec-microphone-et-localisation-sms

    Ce chapitre de livre traite de la réception de SMS

    http://blogs.wrox.com/article/creating-android-apps-that-send-and-receive-sms-messages-and-send-email/

    http://blogs.wrox.com/files/2013/05/Chapter-8-9781118087299-2.pdf ou ici


    Question 1) Le service est global  (sans accès via AIDL)

          Question1.1) Proposez une activité permettant d'installer et de désinstaller un service de filtrage du contenu des SMS entrants. L'activité peut s'enquérir auprès du service, d'informations comme le nombre de SMS filtrés, les commandes effectuées, leur nombre, les dates... L'activité peut disparaître sans aucune incidence sur le service. Une instance de la classe Messenger permettra de répondre à ces demandes d'informations venant de l'activité initiatrice. Pour les commandes, vous pouvez vous inspirer de la liste de commandes de ce traceur PX3490, décrite ci-dessous

    Note: Pour cette question le service est local à l'application, et installé sur un processus distinct de l'activité demandée.

    Ci-dessous, la liste des commandes extraites de la notice d'un traceur du commerce PX3490.pdf la liste sera complétée dès que le cours sur les capteurs et la localisation sera effectué, il manque notamment en entrée : les commandes concernant le périmètre au delà duquel un envoi de SMS est effectué, la batterie en défaut, un débranchement de l'alimentation, un déplacement non prévu de l'appareil,...etc....

    Ci-dessous, une suggestion d'interface minimaliste de l'activité de gestion du service.

    Un exemple possible de fichier AndroidManifest.xml, notez la syntaxe pour le processus distinct pour le service, en cas d'arrêt du service, cette instruction killProcess( myPid() ); peut alors être installée au sein de la méthode onDestroy, sans oublier de retirer (unregisterReceiver) le receveur de SMS

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="tp_broadcast_global.smb116.cnam.myapplication">
    																	

    <application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">

    <activity android:name=".MainActivity">
    <intent-filter>
    <action android:name="android.intent.action.MAIN" />
    <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
    </activity>
    <service
    android:name=".BroadcastService"
    android:enabled="true"
    android:exported="true"
    android:process=":BroadcastService">
    <intent-filter>
    <action android:name="BroadcastService" />
    </intent-filter>
    </service>
        </application>

    </manifest>
     
    Sans oublier ces permissions

    <uses-permission android:name="android.permission.RECEIVE_SMS" />
    <uses-permission android:name="android.permission.WRITE_SMS" />
    <uses-permission android:name="android.permission.SEND_SMS" />
    <uses-permission android:name="android.permission.READ_SMS" />

    Avec éventuellement ce  receveur qui devrait permettre de déclencher le service dès que le mobile démarre

    <receiver
       android:name="xxxxx.interceptor.sms.SMSServiceAtBoot"
       android:enabled="true"
       android:exported="false" >
          <intent-filter android:priority="2147483647" >
               <action android:name="android.intent.action.BOOT_COMPLETED" />
               <category android:name="android.intent.category.DEFAULT" />
           </intent-filter>
    </receiver>

     

    public class SMSServiceAtBoot extends BroadcastReceiver {

        public void onReceive(Context context, Intent intent) {
         // démarrer le service si nécessaire
     

           A ce stade du cours, chaque élément de la chaîne de responsabilités lié à la localisation se contente d'afficher la commande reçue dans un Toast, ci-dessous un exemple d'élément de la chaîne de responsabilités qui se contente d'afficher dans la console l'heure et le message reçu (le Toast est en commentaire).

    Afficher l'image d'origine
    source : http://www.dofactory.com/images/diagrams/net/chain.gif

    public class TraceHandler extends ChainHandler<String> {
    private final static boolean I = true;

    private Context context;

    public TraceHandler(Context context, ChainHandler<String> successor){
    super(successor);
    this.context = context;
    }

    public boolean handleRequest(String value){
    Calendar c = Calendar.getInstance();
    DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT, Locale.FRANCE);
    DateFormat dt = DateFormat.getTimeInstance(DateFormat.SHORT,Locale.FRANCE);
    String date = df.format(c.getTime()) + "-" + dt.format(c.getTime());
    if(I) Log.i("ToastChainHandler", date + " handleRequest: " + value );
    //Toast.makeText(context,"value: " + value,Toast.LENGTH_LONG).show();
    //if(I) Log.i("ToastChainHandler","handleRequest: " + value );
    return super.handleRequest(value);
    }
    }
    public abstract class ChainHandler<T>{
    protected ChainHandler<T> successor = null;

    public ChainHandler(){ this.successor = null;}
    public ChainHandler(ChainHandler<T> successor){ this.successor = successor;}
    public void setSuccessor(ChainHandler<T> successor){this.successor = successor;}
    public ChainHandler<T> getSuccessor(){return this.successor;}
    public boolean handleRequest(T value){
    if ( successor == null ) return false;
    return successor.handleRequest(value);
    }
    }
     

        Ci-dessous les méthodes onCreate et onClickStartService issues de l'activité initiatrice

    public class MainActivity extends AppCompatActivity {
    private ChainHandler<String> chaine;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    this.chaine = new ToastChainHandler(this,new TraceHandler(this,null));
    ...
    public void onClickStartService(View v){
    Intent intent = new Intent(this,BroadcastService.class);
    Messenger messager = new Messenger(handler);
    intent.putExtra("messager", messager);
    startService(intent);
    }
     
    
        
     

     

    Question 2) Le service est global, accessible par n'importe quelle activité

     Le service de la question 1 est maintenant accessible depuis n'importe application installée sur le même mobile, proposez ce même service et son interface AIDL. (File->New->AIDL). La liste des méthodes ci-dessous n'est pas exhaustive...

    // SMSServiceI.aidl
    package votre paquetage; interface SMSServiceI {

    void startSMSInterceptor();
    void stopSMSInterceptor();

    // ..
      long interceptedSMS();
      String lastSMSReceived();
    String lastSMSIntercepted();
    //..
    }
     
     En exemple, au sein d'une activité, la connexion via l'appel de bindService pourrait être
    public void onClickBind(View v){
    Intent intent = new Intent();
    intent.setAction("BroadcastService"); // le nom du service, cf. <intent-filter> du service
    intent.putExtra("message","un message...");
    bindService(intent,connection,BIND_AUTO_CREATE);
    }
    private ServiceConnection connection = new ServiceConnection(){

    @Override
    public void onServiceConnected(ComponentName name, IBinder binder) {
    service = IBroadcastService.Stub.asInterface(binder);
    try {
    Log.i("MainActivity", "service.interceptedSMS()== " + service.interceptedSMS());
    }catch(RemoteException e){
    Log.i("MainActivity", "RemoteException" + e.getMessage());
    }
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
    Log.i("MainActivity", "onServiceDisconnected");
    }
    };

     

     Proposez une nouvelle activité, en modifiant l'activité issue de la question1, et utilisant la communication via le fichier AIDL (dans un autre projet AndroidStudio, un autre paquetage...)

     
     

    Question 3) Ajout/suppression d'éléments de la chaîne de responsabilités (CoR)

     Proposez une solution afin d'ajouter ou de supprimer des éléments à la chaîne de responsabilités, par exemple une instance de la classe Messenger qui depuis le service déclenche le maillon de la chaîne dans l'activité, ou bien ... ?

    Cette question a pour but de préparer les futurs ajouts en fonction des prochains cours.

     

    Optionnelle) Après Kitkat, interception totale des SMS

      Depuis kitkat, les sms ne peuvent être totalement interceptés que par l'application pas défaut, une solution serait que votre application Tracer devienne
    l'application par défaut et ainsi intercepte tous les SMS  à ce lien une description (source ici) d'une solution avec le .zip ici