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).