2012-04-26


Il y a deux jours, j'ai reçu un sms non sollicité, un spam.

Non mobilobox, c'est un numéro porté, je ne suis plus trait (meuh) par le cartel des 3 escrocs que sont Béton, Vivendi et Mandarine. Je suis chez FreeMobile ce qui te vaut la chance incroyable de ne pas être signalé au 33700.

Ça marche pas de chez çamarchepas.com

Ma version personnelle de SignalSpam envoie directement les SMS. Elle ne passe pas obligatoirement par “Intent.ACTION_VIEW“ comme la version grand public disponible sur le PlayStore.

Donc ... erreur sur erreur : l'application refuse obstinément d'envoyer le message au 33700. Mon code est correct, mais ça ne fonctionne pas. Par dépit je passe en mode “Intent.ACTION_VIEW“ pour laisser l'application native de ICS.

Diantre ... même l'application native se casse les dents.

Explications

Le 33700 est géré par l'Association SMS+ (association loi 1901) alias L'Association Française du Multimédia Mobile. L'AFMM précise sur une page de son site les opérateurs membres :
  •  Auchan Telecom
  • Bouygues Telecom
  • Debitel
  • NRJ Mobile
  • Omer Telecom
  • Orange France
  • Orange Réunion
  • SFR
  • SRR
  • Tele2 Mobile
  • Telegate
Il n'y a pas FreeMobile.

Steuplait Monsieur Free ...

Bon M. Free, nous savons toi et moi que ta marge de progression dans le mobile est géante. Tu es encore jeune et plein d'ambition.

Donc, si tu veux vraiment progresser dans le détail de confort inutile à 99% de la population des mobinautes, gère le 33700.

Toi et moi aimons ces détails inutiles qui ont fait ta singularité dans l'ADSL ;)

Message Perso : Hey mobilobox, fais-moi plaisir, internet c'est avec un I majuscule. Autre chose, qu'utilises-tu comme logiciel pourri en ASCII en 2012 ? Cet anachronisme technologique fait commercialement désordre.

2012-04-25

fill_parent ou match_parent

Selon votre plus ou moins grande ancienneté avec le SDK Android, il doit vous arriver de mélanger au sein d'une même description d'interface XML des “fill_parent“ ou des “match_parent“ pour vos “android:layout_width“ ou vos “android:layout_height“.

Les deux valeurs sont équivalentes, le widget doit prendre toute la place laissée vacante dans le widget parent. La différence réside dans la version de l'API. Si votre projet vise les SDK < 7, vous devez utiliser “fill_parent“. Au delà, pour le SDK 8 et supérieurs, la valeur conseillée est “match_parent“.

Si votre projet vise un numéro d'API strictement plus grand que 7 :
find . -iname "*.xml" -exec sed -i "" "s/fill_parent/match_parent/g" {} \;
Si votre projet vise un numéro d'API strictement inférieur ou égale à 7 :
find . -iname "*.xml" -exec sed -i "" "s/match_parent/fill_parent/g" {} \;


Plus de lecture : ici (Anglais).

2012-04-23

Mettre à jour un Galaxy Nexus chiffré sans perdre de données.

Dans un précédent billet, je donnais une méthode propre pour passer son Galaxy Nexus en rom aokp. Propre, ok mais au prix de vos données.
Il est possible de mettre à jour la partie ROM du terminal sans toucher à la partie RWM. Simple sans être trivial, voici comment faire.
Attention, cette méthode de mise à jour est conforme à l'esprit de la norme ISO 1664.

Sauvegardez vos données. Sauvegardez vos données. Sauvegardez vos données.

Cette méthode n'a été vérifiée QUE et UNIQUEMENT sur un terminal chiffré équipé d'une ROM AOKP.

Préparer la rom AOKP qui va bien.

Durant cette phase, les commandes seront exécutées sur mon ordinateur personnel. La préparation doit se faire sur une machine séparée pour plusieurs raisons :
  • On ne peut pas échanger de données entre une session chiffrée et une session en clair (cwm).
  • Zip et Bzip2 sont des opérations gourmandes en temps CPU et I/O.
Le billet est réalisé avec la build 32 de la rom aokp récupérable ici
Une fois téléchargé, je prépare 2 archives supplémentaires : system.zip et system.tar.bz2
$ cd /tmp/
$ unzip aokp_maguro_build-32.zip
$ cd aokp_maguro_build-32/system/
$ # Création de l'archive ZIP
$ zip -r ../../system.zip *
$ # Création de l'archive tar.bz2
$ tar cjf ../../system.tar.bz2 *
Un ordre d'idée des tailles obtenues :
$ ls -alh  /tmp/system.*
 -rw-r--r--  1 ckg  ckg   107M 17 avr 14:23 /tmp/system.tar.bz2
 -rw-r--r--  1 ckg  ckg   108M 17 avr 14:20 /tmp/system.zip

Sauvegardez apk+data (méthode intelligente)

Pour une sauvegarde en toute tranquillité, utilisez Titanium Backup ou Rerware Backup sur le play store.

Sauvegardez apk+data (méthode barbare)

~ # # Création des dossiers
~ # mkdir /mnt/sdcard/backup/apk
~ # mkdir /mnt/sdcard/backup/apk-data
~ # # Sauvegarde des apk
~ # cp /data/app/*.apk /mnt/backup/apk
~ # cd /data/data/
~ # # Purge des caches
~ # rm -r */cache/*
~ # # Dans l'absolu, lib/ est optionnel
~ # for i in *;do tar cjf /mnt/sdcard/backup/apk-data/$i.tar.bz2 $i;done;
Si vous souhaitez récupérer les sous-dossiers de /mnt/sdcard/backup via MTP il faut forcer le daemon mtp à recharger sa liste de fichier :
~ # pkill -HUP f_mtp 
il ne reste plus qu'a sauvegarder la carte SD.

Shell minimal : Clockwork Mode Recovery

La suite des opérations se fait dans le mode recovery.
$ adb reboot recovery
Une fois le recovery actif, je lance une session shell.
Le mount m'indique que seule la partition /cache est chargée :
~ # mount
rootfs on / type rootfs (rw)
tmpfs on /dev type tmpfs (rw,nosuid,relatime,mode=755)
devpts on /dev/pts type devpts (rw,relatime,mode=600)
proc on /proc type proc (rw,relatime)
sysfs on /sys type sysfs (rw,relatime)
/dev/block/platform/omap/omap_hsmmc.0/by-name/cache on /cache type ext4 (rw,nodev,noatime,nodiratime,barrier=1,data=ordered)
Visiblement, pas de /system monté. Le chemin pour trouver /system est indiqué par /cache.
Pour monter /system :
~ # mkdir /x
~ # mount /dev/block/platform/omap/omap_hsmmc.0/by-name/system /x/
On peut vérifier :
~ # ls /x/
addon.d     build.prop  framework   media       tts         xbin
app         etc         lib         su-backup   usr
bin         fonts       lost+found  system      vendor
La partition /system/ n'est pas chiffrée.
Si votre téléphone est saisi par un tiers vous pouvez le considérer comme corrompu. Je reviendrai un autre jour sur le "comment détecter la compromission d'un terminal". C'est la fin de la phase de préparation depuis le shell minimal.

Pousser la rom aokp complete

Normalement, vous devriez avoir la place de pousser la rom complete dans le /data/media de votre terminal. Depuis l'ordinateur :
$ adb push system.zip /data/media
Depuis la session shell (adb shell)
~ # unzip -o /data/media/system.zip -d /x/
Si vous obtenez "unzip: zip flags 1 and 8 are not supported", utilisez l'archive system.tar.bz2 définie plus haut alors, depuis l'ordinateur :
$ adb push system.tar.bz2 /data/media
Depuis la session shell (adb shell)
~ # cd /data/media/
~ # bunzip2 system.tar.bz2
~ # tar xf system.tar -C /x/

Fin

Avant de débrancher, il faut umount puis reboot :
~ # umount /x/
~ # reboot

2012-04-16

import org.iso.9001.*;

Semaine sans Android ... l'audit de renouvellement par BVC s'est très bien passé. Ouf. Cependant d'après l'auditeur, le système documentaire gagnerait en pertinence en étant plus informatisé.

Note pour le futur, la maîtrise de la diffusion d'un document peut reposer en partie sur l'utilisateur final. Lorsque ce dernier effectue une consultation non maitrisée (document imprimé ou PDF) il s'engage à toujours se référer au document original. Cette pirouette est permise par l'esprit général du 6.2.1 que je limitais, à tort, à l'incidence sur la conformité du produit.

J'ai commencé ma formation avec le référentiel 1994 pour lequel le report d'une telle responsabilité était inenvisageable. 12 après la publication du référentiel 2000 il me reste des réflexes à dépoussiérer.

Comment prouver la vérification / validation / approbation du document ? Dans un premier temps, plaider la faible complexité de l'organisme qui m'emploie et me contenter de la mise à disposition du PDF sur un serveur partagé en lecture seule.
Dans un second temps, il faudra mettre en place une PKI et signer ces foutus PDF.

Il va falloir jouer avec iText et BouncyCastle ... avec une application tablet Android ? :-)

2012-04-09

Free gère bien ses codes GSM en itinérance.

Dans mon article sur coucou-networks j’accusai Free d'ignorer les ordres GSM concernant la gestion des transferts d'appels.

Je déteste viscéralement les répondeurs. A chaque changement d'opérateur, j’envoie le code GSM (magique) ##002# (à ne pas faire sans précautions).

Le statut obtenu, avec l'opérateur Free donne ceci :

Dans la théorie ça devrait marcher.
Dans la réalité, chez moi ça marche pas™, et ça m'agace. Mes correspondants tombent sur le répondeur et laissent des messages ([Message de Service] : je n'écoute pas vos messages, tous les 2-3 mois je supprime sans les écouter).

Or, hier, j'ai été surpris de sentir 5 vibrations dans ma poche avant de pouvoir décrocher ... A Angers, où je n'étais pas en itinérance Orange, le répondeur ne s'est pas déclenché.

Ce matin, à la maison, en itinérance Orange : pas de répondeur.

Ce matin, pas loin de mon travail, en itinérance Orange : pas de répondeur.


Bien bien bien Free. Je suis exigeant et râleur, mais je vous ♡ !

Plus de lecture : http://geckobeach.com/cellular/secrets/gsmcodes.php

2012-04-03

Remplir une ListView avec un ArrayList<String> sans passer par une ListActivity

Mon projet du mois fait une utilisation intensive du chipset GPS et du RIL du smartphone. Globalement tout se passe comme prévu malgré la diversité des précisions des chipset et l'endroit où se trouve le terminal. Globalement, parce que parfois ça marche pas. Alors que chez moi ça marche™.

Le casse-tête de la journée est de trouver une manière aussi simple que possible d'aider les testeurs en leur proposant de visionner un journal d'activité de l'application. Comment remplir le plus simplement possible une ListView avec un ArrayList<String> ?

Layout

La partie la plus simple du casse-tête est sans aucun doute la déclaration de la ListView dans le layout qui nous interesse.
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android" >
   ...
        <ListView
            android:id="@+id/lvLog"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" >
        </ListView>
   ...
</merge>

ArrayList

La déclaration de notre ArrayList<String> de test est également assez simple :
ArrayList<String> als = new ArrayList<String>();
als.add("dim");
als.add("dam");
als.add("doum");

Déclaration du remplissage

Le morceaux le plus technique du casse-tête est encore devant mais il est possible de déclarer l'inconnu :
 private void lvRefresh() {
  ListView list1 = (ListView) findViewById(R.id.lvLog);
  LogAdapter logAdapter = new LogAdapter(als);
  list1.setAdapter(logAdapter);

 }

A ce stade le code ne se compile plus parce que logAdapter n'est pas encore déclaré. C'est que nous allons faire MAINTENANT !

ListView

Contrairement à ce que pourrait nous laisser croire la vue de la ListView à travers le Graphical Layout du plug-in Eclipse, la ListView ne contient PAS de vue de base.

ListView est à prendre au sens littéral : une liste de vues. Des vues non définies.

Il est vraiment nécessaire de comprendre cette "indéfinition" des vues pour construire notre adapter : il faudra créer une vue pour chaque ligne de journal.

logAdapter

La classe LogAdapter hérite de BaseAdapter. Eclipse se propose de déclarer les méthodes manquantes :
private class LogAdapter extends BaseAdapter {
 @Override
 public int getCount() {
  // TODO Auto-generated method stub
  return 0;
 }

 @Override
 public Object getItem(int arg0) {
  // TODO Auto-generated method stub
  return null;
 }

 @Override
 public long getItemId(int arg0) {
  // TODO Auto-generated method stub
  return 0;
 }

 @Override
 public View getView(int arg0, View arg1, ViewGroup arg2) {
  // TODO Auto-generated method stub
  return null;
 }
}
Notre code compile. Youpi !
Notre LogAdapter pourrait utiliser une variable globale, mais ce ne sera pas le cas dans cet exemple. Je vais donc déclarer une variable private stringList et un constructeur à cet endroit là :
private class LogAdapter extends BaseAdapter {
 private ArrayList stringList;

 public LogAdapter(ArrayList arraylistLog) {
  // TODO Auto-generated constructor stub
  this.stringList = arraylistLog;
 }
Les méthodes getItem et getItemId sont assez triviales. La première renvoie le Xième élément et la deuxième donne la position d'un élément dans l'ensemble. On rajoute également getCount() pour la route :
 @Override
 public int getCount() {
  // TODO Auto-generated method stub
  return stringList.size();
 }

 @Override
 public Object getItem(int arg0) {
  // TODO Auto-generated method stub
  return stringList.get(arg0);
 }

 @Override
 public long getItemId(int arg0) {
  // TODO Auto-generated method stub
  return arg0;
 }
Nous voila au sommet de la colline :
 @Override
 public View getView(int arg0, View arg1, ViewGroup arg2) {
  // TODO Auto-generated method stub
  TextView v = (TextView) new TextView(getBaseContext());
  v.setTextColor(0xff000000);
  v.setText(this.stringList.get(arg0));
  return v;
 }

Recyclage

Il est dommage recréer une View lorsque l'utilisateur monte ou descend la liste : une View créée et une vue détruite. Android permet de recycler les vues existantes avec setTag()/getTag().
 @Override
 public View getView(int arg0, View arg1, ViewGroup arg2) {
  if (arg1 == null) {
   //Log.d(TAG,"NOUVEAU getView " + arg0 + " " + arg1 );
   arg1 = (TextView) new TextView(getBaseContext());
   ((TextView) arg1).setTextColor(0xff000000);
   arg1.setTag(arg1);
  } else {
   //Log.d(TAG,"RECYCLAGE getView " + arg0 + " " + arg1 );
   arg1 = (View) arg1.getTag();
  }
  ((TextView) arg1).setText(this.stringList.get(arg0));
  return arg1;
 }
Si cet aspect recyclage vous intéresse, il y a l'excellent article de Amber Fog