David Madore's WebLog: Comment se perdre dans les sources d'Android et désactiver le senseur de proximité

Index of all entries / Index de toutes les entréesXML (RSS 1.0) • Recent comments / Commentaires récents

Entry #2282 [older|newer] / Entrée #2282 [précédente|suivante]:

(lundi)

Comment se perdre dans les sources d'Android et désactiver le senseur de proximité

Pour illustrer ce que je racontais hier, je voudrais détailler un peu, sur un cas concret, la manière dont les sources d'Android sont une moussaka incompréhensible et impossible à naviguer, parce que je trouve ça assez spectaculaire.

Je suis parti du but suivant : explorer la difficulté qu'il y aurait à ajouter un réglage dans l'application « téléphone » d'Android pour ignorer les données du capteur de proximité (potentiellement cassé) et ne pas éteindre l'écran pendant le passage d'un appel.

Et là, je suis tombé sur la constatation stupéfiante suivante : tel que je lis le code, cette préférence existe déjà, mais pour une raison qui m'échappe totalement, elle est « désactivée » des menus sur mon téléphone (à la fois l'ancien et le nouveau, d'ailleurs). Ou peut-être qu'elle n'est pas activée, nuance.

Une partie de la difficulté, bien sûr, consiste à savoir quel code, au juste (parmi les millions d'arbres de compilations, de branches, de versions et de sous-versions) se trouve au juste compilé sur mon téléphone. J'ai fini par trouver la réponse à ça : il faut aller voir dans le fichier /system/etc/build-manifest.xml sur le téléphone — celui-ci donne, pour chaque dépôt Git des sources d'Android, le commit exact qui a servi à faire la compilation. Beaucoup d'espions Bothans sont morts pour me fournir cette information, alors faites-en bon usage.

Après, même si je trouve la bonne version du code qui tourne sur mon téléphone, encore faut-il trouver où, dans la moussaka géante, se trouve le code de l'application « téléphone ». Il y a des répertoires des sources qui s'appellent (1) frameworks/base/telephony, (2) frameworks/opt/telephony, (3) packages/services/Telephony, et (4) packages/providers/TelephonyProvider : quel est le rapport exact entre tout ça ? je n'en ai fichtrement aucune idée, et je ne sais même pas où chercher une explication à ce sujet (le mieux que j'aie trouvé est ce petit guide, mais il ne va vraiment pas très loin). Le fait de savoir que l'application Android s'appelle, à un certain niveau, com.android.phone, n'aide pas tant que ça.

J'ai cependant fini par trouver qu'il y a un menu de préférences dans packages/services/Telephony/src/com/android/phone/CallFeaturesSetting.java (dans le répertoire (3) ci-dessus, donc), dont la version exacte compilée pour mon téléphone (le Nexus 4, disons, mais ça ne change pas grand-chose), est en principe exactement celle-ci. Il y a incontestablement du code (notamment autour des lignes 670–675) qui définit une préférence, dans le menu des réglages de l'application téléphonie (lequel n'est d'ailleurs pas totalement évident à trouver, mais bon…) censée permettre d'ignorer le capteur de proximité. (Le code vraiment opératoire est plus exactement dans packages/apps/InCallUI/src/com/android/incallui/ProximitySensor.java, autour des lignes 81–97, parce que dans Android, le code qui fait quelque chose est toujours aussi éloigné que possible du code qui règle la préférence demandant à faire le quelque chose en question.)

☞ Ça vaut la peine que je digresse sur la complexité de mettre du texte dans un menu sous Android, parce que ça donne une idée à quel point le machin est over-engineeré. Vous vous doutez bien qu'écrire directement "Enable proximity sensor" dans le code Java, ce serait trop facile : pour permettre au machin d'être traduit dans 72 langues, il faut écrire quelque chose comme <string name="proximity_mode_title">Enable proximity sensor</string> dans un fichier res/values/strings.xml (enfin, en l'occurrence res/values/cm_strings.xml, parce que c'est spécifique à CyanogenMod, mais peu importe), qui donne donc un nom proximity_mode_title à la chaîne "Enable proximity sensor" (et on mettra autre chose dans les autres langues, bien sûr). Maintenant, pour définir un bouton de préférences (=une entrée dans le menu) ayant cette chaîne pour titre, il faut aussi donner un nom au bouton : cette fois, c'est dans le fichier res/xml/call_feature_setting.xml qu'on va écrire quelque chose comme <CheckBoxPreference android:key="button_proximity_key" android:title="@string/proximity_mode_title" android:persistent="false" android:summary="@string/proximity_on_summary" /> pour définir le bouton button_proximity_key avec comme titre la chaîne qu'on vient de définir. Vous pensez qu'on va utiliser ensuite button_proximity_key directement dans le code Java ? Toujours pas ! Il est de bon ton de définir une constante private static final String BUTTON_PROXIMITY_KEY = "button_proximity_key" histoire de ne pas avoir, horresco referens, à écrire une chaîne directement dans le code Java. Et bien sûr, on va stocker l'objet Java correspondant, obtenu avec mButtonProximity = (CheckBoxPreference) findPreference(BUTTON_PROXIMITY_KEY), dans une variable mButtonProximity (enfin, un membre de la classe, peu importe), et c'est ça qu'on va utiliser dans le code. Ça c'est juste pour définir le bouton de la préférence. Le nom de la préférence elle-même, il va être défini comme public static final String SETTINGS_PROXIMITY_SENSOR = "proximity_sensor" dans src/com/android/phone/Constants.java pour pouvoir l'utiliser comme Constants.SETTINGS_PROXIMITY_SENSOR. C'est absolument hallucinant combien d'incantations propitiatoires, réparties dans plusieurs fichiers, Android oblige à prononcer pour mettre un malheureux bouton dans un menu pour contrôler une préférence système ! Et ça ne facilite vraiment pas le boulot de celui qui veut comparer le code du menu avec ce qu'il voit sur son téléphone.

Sauf que là, de surcroît, ils ont mis une variable de configuration (config_proximity_enable, définie dans res/values/config.xml, voyez les lignes 197–198, et utilisée dans CallFeaturesSetting.java vers les lignes 2602–2606) qui sert à faire disparaître cette option du menu. Ce que j'ai mis, bien sûr, un temps fou à comprendre en m'arrachant les cheveux à me demander pourquoi le code est là et n'a pas d'effet (et, du coup, si je lisais bien la bonne version). Je pense que les res/values/config.xml servent en général à fournir des configurations différentes sur des téléphones différents, mais je ne comprends pas bien pourquoi on voudrait désactiver l'entrée du menu qui permet de désactiver le senseur de proxiité (à part si le téléphone n'a pas de senseur de proximité, mais dans ce cas la valeur par défaut devrait plutôt être de ne pas chercher à l'utiliser, alors que c'est le contraire — je suis confusé). Encore moins pourquoi l'impossibilité de désactiver serait le défaut, ou pourquoi sur le Nexus 4 ou 5 CyanogenMod aurait voulu garder ce défaut : tout ça semble assez inexplicable.

Mais bon, le fait qu'en modifiant de force la préférence dans la base de données des préférences système, ça fonctionne :

Bref, pour demander à certains Android (au moins CyanogenMod) d'ignorer le capteur de proximité pendant les appels, on peut éditer la base de données de préférences en lançant (en tant que root)

sqlite3 /data/data/com.android.providers.settings/databases/settings.db

et y insérer une préférence proximity_sensor de valeur fausse,

INSERT INTO system ( name, value ) VALUES ( 'proximity_sensor', 0 ) ;

puis rebooter. Ceci aurait dû faire partie d'un menu de préférences, mais le réglage a été omis ou volontairement désactivé (ce qui n'empêche pas la commande ci-dessus de fonctionner).

↑Entry #2282 [older|newer] / ↑Entrée #2282 [précédente|suivante]

Recent entries / Entrées récentesIndex of all entries / Index de toutes les entrées