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.
  • 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 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, ils 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é...

    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 ) 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 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, optionnelle) 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 ... ?