Nexus 5X が突然壊れて以来、
私はメインのスマホとして
ZenFone 3 Deluxe ZS570KL を使っている。
購入時の OS は Android 6.0 Marshmallow だったが、
今年 3月にシステム更新のお知らせが表示されたので、
更新を行なったら Android 7.0 Nougat WW-5.14.44.1898 になった (3月26日)。
ところが!
この更新によって通話もデータ通信もできなくなってしまった。
LTE ネットワークへ接続できなくなっていて、
アンテナ・ピクトも表示されない (バツ印が付く)。
最初 SIM の接触不良を疑ったのだが、
SIM を入れ直しても接続できないまま。
更新作業は Wi-Fi のあるところ (自宅) で行なうわけで、
(モバイル) データ通信できなくても Wi-Fi でデータ通信できるから気付きにくい。
まさか更新で通話やデータ通信ができなくなってしまっているとは思わないので、
翌日 (3月27日) も気付かずに出かけてしまい、
出先でデータ通信できないことに初めて気付いて往生した。
出かける前に気付いていれば、
昔のスマホ (Nexus 5X とか) に SIM を入れ替えて使うこともできただろうに。
LTE で接続できないなら、
W-CDMA で接続してみてはどうだろう?と思ったので、
設定メニューで
「モバイルネットワーク」 を選び、
「有線ネットワークタイプ」 を
「2G/3G/4G」 から
「2G/3G」 へ変更 (つまり LTE 接続を抑制) してみた。
すると無事 3G (W-CDMA) ネットワークへ接続して
(アンテナ・ピクトが表示されて)
通話ができるようになった。
しかし 3G でもデータ通信はできない。
もちろん、
SIM を別のスマホに入れると何の問題もなく通話もデータ通信もできるので、
SIM や設定の問題ではなく、
Nougat な ZS570KL の問題。
この時点では ZS570KL の root 権限を取得していなかった
(まだブートローダをアンロックしていなかった) ので、
できることは限られている。
仕方ないので、
設定メニューの
「バックアップとリセット」 で
「データの初期化」
を行なってみると、
LTE へ接続できるようになった (3月28日)。
Android のバージョンを上げるたびに初期化を強要されてはかなわないと思いつつも、
いちおうこれで一件落着なのだが、
せっかく初期化したのだから、
この機会に (自分流にカスタマイズする前に)
ブートローダをアンロックすることにした。
昨年 9月11日に発売開始した ZS570KL だが、
アンロックの方法を ASUS が公開したのは半年近くが経過した 2月16日になってから。
アンロックツール ZS570KL_UnlockTool_V1.1.zip が
ASUS の Driver & Tools ページからダウンロードできる。
アンロックを行なうとデータの初期化が行なわれてしまうので、
アンロックするならカスタマイズする前の方がいい。
本当は購入直後にアンロックするのが一番なのであるが、
当時はアンロックツールが (少なくとも公式には) 公開されていなかったので、
いままでアンロックせずに使い続けてきた次第。
ASUS のページからダウンロードした zip ファイルを展開して得られた
UnlockTool-9.0.0.29_ZS570KL.apk を ZS570KL へインストールして実行すると、
警告画面 (→ 右図) が表示される。
アンロック (ロック解除) すると一切の保証が無くなるだけでなく、
ソフトウェアのアップデートも受けることができなくなると書いてある。
もちろん、
同ページから最新のソフトウェアをダウンロードすること自体は
(ロック解除とは関係なく) 可能なので、
ここで言う 「ソフトウェアのアップデート」 とは、
あくまで自動で行なわれる更新のことだろう。
「押してデバイスのロック解除を行う」 を押すと、
再起動と初期化が行なわれる。
これでブートローダがアンロックされ、
ボリューム大ボタンを押しながら電源を入れると、
ブートローダ・モードへ入ることができる。
Nexus 等のブートローダ・モードは、
起動する OS を選べたりするが、
ZS570KL のブートローダ・モードは、
通常の起動画面 (ASUS ロゴ) と変わらず、
ブートローダ・モードに入ってるか外見上は区別できないので注意が必要。
ZS570KL と USB ケーブルでつないだ PC 上で
fastboot コマンドを実行することで、
任意のブート・イメージ (TWRP など) を起動したり、
フラッシュメモリへ書込んだりすることが可能。
きちんと動作する
ZS570KL 用の TWRP
がちょうど公開されていた
(3月19日, これ以前のバージョンは画面が逆になるなど、いろいろ不具合があった)
ので、
ZS570KL をブートローダ・モードにした上で、
fastboot コマンドを使って起動してみる:
$ fastboot boot twrp-3.1.0-0-Z016D-20170319-N.img
downloading 'boot.img'...
OKAY [ 0.777s]
booting...
OKAY [ 0.394s]
finished. total time: 1.171s
$
ちょっと使ってみた限りでは、
問題無く使えるようだ。
早く TWRP 公式ページからダウンロードできるようになることを望む。
TWRP を ZS570KL の
recovery パーティションへ書込んでおくと、
ボリューム小ボタンを押しながら電源を入れることで、
(PC とつながなくても) TWRP を起動できる。
$ fastboot flash recovery twrp-3.1.0-0-Z016D-20170319-N.img
target reported max download size of 536870912 bytes
sending 'recovery' (19980 KB)...
OKAY [ 0.777s]
writing 'recovery'...
OKAY [ 0.021s]
finished. total time: 0.797s
$
アンロックしたらついでに root 権限も取得したくなるのが人情なので、
SuperSU v2.79
SuperSU-v2.79-201612051815.zip を TWRP からインストールした。
と、
ここまでは順調だったが、
ある日 (4月4日) 出先で唐突にデータ通信できなくなった。
外出中にデータ通信できなくなると往生する
(前述したように 3月27日に体験済み) ので、
こんなこともあろうかと
ZS570KL には普段使っている
IIJmio の
nano SIM の他に、
非常用として
Aeon Mobile の
micro SIM も入れていた。
電話の着信は両方の SIM で可能 (DSDS, Dual SIM Dual Standby) で、
電話の発信とデータ通信は、
どちらの SIM で行なうか選ぶ方式。
データ通信を Aeon Mobile へ切り替えると、
無事データ通信できた。
なぜ Aeon Mobile ではデータ通信できるのに、
IIJmio ではできないのだろう?
と思いつつも、
IIJmio でもアンテナ・ピクトは表示されるし、
IIJmio の電話番号に着信できるので、
そのまま使い続けた。
もちろん、
Aeon Mobile なら他の SIM でもデータ通信できるのか?とか、
micro/nano どちらの SIM トレイに入れるかでデータ通信の可否が変わるのか?とか、
いろいろ確認したいことはあり、
その都度いろんな実験をしている。
が、結論から言えばいずれも直接の関係は無さそうだった。
わざわざ他キャリアから Aeon Mobile へ MNP して新たに
nano SIM を作ってみたがデータ通信できなかったし、
IIJmio や mineo の nano SIM にゲタを履かせて
micro SIM トレイに入れてもデータ通信できるようにはならなかったし、
nano SIM トレイでも例えば香港3 の nano SIM なら
(ローミングで) データ通信できた。
そんなある日 (4月13日)、
IIJmio の電話番号へ電話がかかってきたので出たら、
音声が互いに聞こえなかった。
驚いて Aeon Mobile で通話実験したら、
Aeon Mobile でも着信/発信ともに音声が聞こえなくなっていた。
少なくとも 4月11日までは普通に通話できていたので、
2日間の間に何があったか思い出そうとしたが全く心当たりがなかった。
ある日突然、通話できなくなるなんてことが起きると大変困る。
# 4月21日追記: TWRP で DSP をリストアすると発症するらしい。
# 音声が聞こえない症状については Nougat への更新とは無関係だった。
データ通信ができないのはモバイル・ルータを持ち歩くことで対処できるが、
通話ができないのでは話にならない。
直ちに ZS570KL の使用を中止し、
IIJmio SIM を Pixel に入れて Pixel を持ち歩くことにした。
Pixel は Band 19 をサポートしていないので docomo (の MVNO) では使いにくく、
(日本では) あまり使っていなかった。
というわけで ZS570KL の使用を中止したので、
さらに思いきった実験ができるようになった。
手始めに再度初期化してみることにした。
今回は root 権限を取得済なので、
バックアップ/リストアが手軽にできる。
初期化してデータ通信が復活すれば、
徐々に設定を変えていくことで原因の切り分けも可能だろう。
ところが!
初期化したのに IIJmio のデータ通信は復活しなかった (4月14日)。
通話も着信/発信はできるけど音声が伝わらない。
ZS570KL 単体の問題ではなくて、
LTE ネットワークとの相性も関係あるのか?
ますます何が原因なのか分からなくなってしまった。
初期化ではなく Nougat をクリーン・インストールすれば、
通話もデータ通信も元通りできるようになるのかもしれない。
しかしいつ同じ症状が再発しないとも限らない。
突然データ通信できなくなったり、
音声が聞こえなくなったりするようでは安心して使えない。
しかもこの Nougat にはメニューボタンが使えないという
(私にとっては) 重大なバグもある
(本来は
「ASUS カスタマイズ設定」
でマルチタスクボタンの長押しでメニューを表示するように設定できるはずだが、
この Nougat では機能しない)。
通話やデータ通信ができなくなるのはおそらくバグだろうし、
メニューボタンのほうは明らかにバグ。
ついでに言うと、
カメラが EXIF に記録する位置情報が壊れていて、
他のソフト (Googleフォトを含む) で位置情報が認識できないというバグもある。
そのうち修正版が公開されるのだろうと思っていたが、
Android 7 Nougat WW-5.14.44.1898
が公開されてから一ヶ月がたつのに何も発表されない。
ASUS はバグを認識していないのか?
# 4月19日追記: WW-5.14.44.2096 が公開された。が、症状は変わらず (>_<)
事ここに至っては Nougat の使用を諦め、
Android 6 Marshmallow に戻すしかないと決断した。
といっても、
バージョンを戻す方法は (ASUS 公式には) 提供されていない。
が、
アンロックした ZS570KL であれば、
フラッシュメモリに直接書込めるので、
Marshmallow のイメージを書込めばよい。
つまりクリーン・インストール。
このブログを書いていて (いまごろ orz) 気付いたが、
上記 TWRP のダウンロードページ に、
「Downgrade_to_4.12.40.1698」 というディレクトリがあった。
Android 6 に戻したいという (私と同様な) 人が多いのだろう。
まず
ASUS の Driver & Tools ページから Marshmallow の最後のバージョン
WW-4.12.40.1698 の zip アーカイブをダウンロード。これを展開する:
senri:~/ZS570KL $ unzip UL-Z016-WW-4.12.40.1698-user.zip
Archive: UL-Z016-WW-4.12.40.1698-user.zip
signed by SignApk
extracting: system.patch.dat
inflating: META-INF/com/android/metadata
inflating: META-INF/com/google/android/update-binary
inflating: META-INF/com/google/android/updater-script
inflating: boot.img
inflating: file_contexts
inflating: firmware-update/NON-HLOS.bin
inflating: firmware-update/adspso.bin
inflating: firmware-update/cmnlib.mbn
inflating: firmware-update/cmnlib64.mbn
inflating: firmware-update/devcfg.mbn
inflating: firmware-update/emmc_appsboot.mbn
inflating: firmware-update/hyp.mbn
inflating: firmware-update/keymaster.mbn
inflating: firmware-update/mdtp.img
inflating: firmware-update/pmic.elf
inflating: firmware-update/rpm.mbn
inflating: firmware-update/tz.mbn
inflating: firmware-update/xbl.elf
inflating: logs/version
inflating: system.new.dat
inflating: system.transfer.list
inflating: META-INF/com/android/otacert
inflating: META-INF/MANIFEST.MF
inflating: META-INF/CERT.SF
inflating: META-INF/CERT.RSA
senri:~/ZS570KL $
zip アーカイブの中の
boot.img が android のブート・イメージ。
このファイルの中に、
Linux カーネルと initramfs
が含まれている。
これを boot パーティションへ書込む:
senri:~/ZS570KL $ fastboot flash boot boot.img
target reported max download size of 536870912 bytes
sending 'boot' (13305 KB)...
OKAY [ 0.524s]
writing 'boot'...
OKAY [ 0.021s]
finished. total time: 0.545s
senri:~/ZS570KL $
zip アーカイブの中の
firmware-update ディレクトリ下の 13個のファイルが、
ブートローダ等のファームウェア。
拡張子が *.bin のファイル (2個) はファイルシステムのイメージ。
NON-HLOS.bin は /firmware ディレクトリに、
adspso.bin は /dsp ディレクトリに、
それぞれ読み取り専用でマウントされる。
どちらも AMSS モデム関連のディレクトリ。
NON-HLOS.bin は modem パーティションへ、
adspso.bin は dsp パーティションへ、
それぞれ書込む:
senri:~/ZS570KL $ fastboot flash modem firmware-update/NON-HLOS.bin
target reported max download size of 536870912 bytes
sending 'modem' (81916 KB)...
OKAY [ 3.166s]
writing 'modem'...
OKAY [ 1.895s]
finished. total time: 5.060s
senri:~/ZS570KL $ fastboot flash dsp firmware-update/adspso.bin
target reported max download size of 536870912 bytes
sending 'dsp' (16384 KB)...
OKAY [ 0.652s]
writing 'dsp'...
OKAY [ 0.401s]
finished. total time: 1.053s
senri:~/ZS570KL $
拡張子 *.mbn は multi-boot binary ファイル (8個) で、
ARM の ELF (Executable and Linkable Format) オブジェクト。
keymaster.mbn と cmnlib.mbn は 32bit の共通ライブラリで、
keymaster は証明書の暗号鍵を管理する。
cmnlib64.mbn は 64bit の共通ライブラリ。
rpm.mbn と emmc_appsboot.mbn は 32bit ARM の ELF 実行ファイル。
rpm は 「resources & power manager」 を意味し、
プライマリ・ブートローダの役割を果たす。
emmc_appsboot はセカンダリ・ブートローダで、
このブートローダが android のカーネルを起動する。
残りの devcfg.mbn, hyp.mbn, tz.mbn は 64bit ARM の ELF 実行ファイル。
機能の詳細は分からないが、
devcfg は device config、
hyp は hypervisor、
tz は trust zone のことだと思われる。
詳細不明なファイルが多いが (^^;
以上 8個の *.mbn ファイルを、
それぞれ対応するパーティションへ
(emmc_appsboot.mbn は aboot パーティションへ、
それ以外はファイル名と同じ名称のパーティションへ) 書込む:
senri:~/ZS570KL $ fastboot flash cmnlib firmware-update/cmnlib.mbn
target reported max download size of 536870912 bytes
sending 'cmnlib' (200 KB)...
OKAY [ 0.029s]
writing 'cmnlib'...
(bootloader) partition_need_check.
OKAY [ 0.030s]
finished. total time: 0.060s
senri:~/ZS570KL $ fastboot flash cmnlib64 firmware-update/cmnlib64.mbn
target reported max download size of 536870912 bytes
sending 'cmnlib64' (254 KB)...
OKAY [ 0.030s]
writing 'cmnlib64'...
(bootloader) partition_need_check.
OKAY [ 0.030s]
finished. total time: 0.061s
senri:~/ZS570KL $ fastboot flash devcfg firmware-update/devcfg.mbn
target reported max download size of 536870912 bytes
sending 'devcfg' (39 KB)...
OKAY [ 0.040s]
writing 'devcfg'...
(bootloader) partition_need_check.
OKAY [ 0.030s]
finished. total time: 0.070s
senri:~/ZS570KL $ fastboot flash aboot firmware-update/emmc_appsboot.mbn
target reported max download size of 536870912 bytes
sending 'aboot' (749 KB)...
OKAY [ 0.047s]
writing 'aboot'...
(bootloader) partition_need_check.
OKAY [ 0.030s]
finished. total time: 0.077s
senri:~/ZS570KL $ fastboot flash hyp firmware-update/hyp.mbn
target reported max download size of 536870912 bytes
sending 'hyp' (257 KB)...
OKAY [ 0.038s]
writing 'hyp'...
(bootloader) partition_need_check.
OKAY [ 0.030s]
finished. total time: 0.069s
senri:~/ZS570KL $ fastboot flash keymaster firmware-update/keymaster.mbn
target reported max download size of 536870912 bytes
sending 'keymaster' (220 KB)...
OKAY [ 0.032s]
writing 'keymaster'...
(bootloader) partition_need_check.
OKAY [ 0.030s]
finished. total time: 0.062s
senri:~/ZS570KL $ fastboot flash rpm firmware-update/rpm.mbn
target reported max download size of 536870912 bytes
sending 'rpm' (224 KB)...
OKAY [ 0.039s]
writing 'rpm'...
(bootloader) partition_need_check.
OKAY [ 0.030s]
finished. total time: 0.069s
senri:~/ZS570KL $ fastboot flash tz firmware-update/tz.mbn
target reported max download size of 536870912 bytes
sending 'tz' (1600 KB)...
OKAY [ 0.084s]
writing 'tz'...
(bootloader) partition_need_check.
OKAY [ 0.030s]
finished. total time: 0.114s
senri:~/ZS570KL $
拡張子 *.elf は 64bit ARM の ELF 実行ファイル (2個)。
*.mbn との違いは不明 (誰か教えて!)。
pmic.elf は power management IC のこと?
xbl.elf は eXtended bootloader のことだろう。
それぞれ対応する同名のパーティションへ書込む:
senri:~/ZS570KL $ fastboot flash pmic firmware-update/pmic.elf
target reported max download size of 536870912 bytes
sending 'pmic' (41 KB)...
OKAY [ 0.040s]
writing 'pmic'...
(bootloader) partition_need_check.
OKAY [ 0.030s]
finished. total time: 0.070s
senri:~/ZS570KL $ fastboot flash xbl firmware-update/xbl.elf
target reported max download size of 536870912 bytes
sending 'xbl' (1780 KB)...
OKAY [ 0.090s]
writing 'xbl'...
(bootloader) partition_need_check.
OKAY [ 0.034s]
finished. total time: 0.124s
senri:~/ZS570KL $
mdtp.img は Mobile Device Theft Prevention のこと?
ファイルの形式すら不明だが、
mdtp パーティションへ書込む:
senri:~/ZS570KL $ fastboot flash mdtp firmware-update/mdtp.img
target reported max download size of 536870912 bytes
sending 'mdtp' (8086 KB)...
OKAY [ 0.324s]
writing 'mdtp'...
OKAY [ 0.218s]
finished. total time: 0.542s
senri:~/ZS570KL $
以上で zip アーカイブの中の
firmware-update ディレクトリ下の
13個のファイルの書き込みが完了した。
そして、
zip アーカイブの中の
system.new.dat と system.transfer.list が、
android を起動したときに /system にマウントされる
ext4 ファイルシステムのイメージ。
つまり Android OS の本体。
3.6GB ほどあり、
zip アーカイブの中の他のファイルと比べてケタ違いに大きい。
一般にファイルシステムのイメージは、
データが無い部分が散在する
「疎なファイル」(sparse file) になる。
フラッシュメモリに書込むときに、
データが無い 「0」 ばかりのブロックを書込むのは効率が悪いので、
「0」 なブロックを飛ばしたデータのファイル system.new.dat と、
そのデータをフラッシュメモリのどこへ書込むか指定するファイル
system.transfer.list に分かれている。
残念なことに fastboot コマンドは system.transfer.list を扱えないので、
あらかじめ system.new.dat と system.transfer.list から
ext4 ファイルシステムのイメージを作成しておいて
fastboot コマンドに与える必要がある。
sdat2img コマンドを使うと、
system.new.dat と system.transfer.list から、
(疎な) イメージ system.img を作成できる:
senri:~/ZS570KL $ sdat2img system.transfer.list system.new.dat system.img
sdat2img binary - version: 1.0
Android Marshmallow 6.0 detected!
Copying 32770 blocks into position 0...
Copying 2 blocks into position 33009...
Copying 32016 blocks into position 33519...
Copying 2 blocks into position 65536...
Copying 32257 blocks into position 66046...
Copying 2 blocks into position 98304...
Copying 2 blocks into position 98545...
Copying 32016 blocks into position 99055...
Copying 2 blocks into position 131072...
Copying 32257 blocks into position 131582...
Copying 2 blocks into position 163840...
Copying 2 blocks into position 164081...
Copying 32016 blocks into position 164591...
Copying 2 blocks into position 196608...
Copying 32257 blocks into position 197118...
Copying 2 blocks into position 229376...
Copying 2 blocks into position 229617...
Copying 32016 blocks into position 230127...
Copying 2 blocks into position 262144...
Copying 32257 blocks into position 262654...
Copying 2 blocks into position 294912...
Copying 2 blocks into position 295153...
Copying 32016 blocks into position 295663...
Copying 2 blocks into position 327680...
Copying 32257 blocks into position 328190...
Copying 2 blocks into position 360448...
Copying 32257 blocks into position 360958...
Copying 2 blocks into position 393216...
Copying 32257 blocks into position 393726...
Copying 2 blocks into position 425984...
Copying 32257 blocks into position 426494...
Copying 2 blocks into position 458752...
Copying 32257 blocks into position 459262...
Copying 2 blocks into position 491520...
Copying 32257 blocks into position 492030...
Copying 2 blocks into position 524288...
Copying 32257 blocks into position 524798...
Copying 2 blocks into position 557056...
Copying 32257 blocks into position 557566...
Copying 2 blocks into position 589824...
Copying 32257 blocks into position 590334...
Copying 2 blocks into position 622592...
Copying 32257 blocks into position 623102...
Copying 2 blocks into position 655360...
Copying 32257 blocks into position 655870...
Copying 2 blocks into position 688128...
Copying 32257 blocks into position 688638...
Copying 2 blocks into position 720896...
Copying 32257 blocks into position 721406...
Copying 2 blocks into position 753664...
Copying 32257 blocks into position 754174...
Copying 2 blocks into position 786432...
Copying 32257 blocks into position 786942...
Copying 2 blocks into position 819200...
Copying 2 blocks into position 819441...
Copying 32016 blocks into position 819951...
Copying 2 blocks into position 851968...
Copying 32250 blocks into position 852478...
Copying 2 blocks into position 884736...
Copying 2 blocks into position 884977...
Copying 32016 blocks into position 885487...
Copying 2 blocks into position 917504...
Copying 2820 blocks into position 918014...
Copying 2 blocks into position 950272...
Copying 24508 blocks into position 950782...
Copying 7689 blocks into position 975291...
Skipping command zero...
Skipping command erase...
Done! Output image: ~/ZS570KL/system.img
senri:~/ZS570KL $
そして、作成した (疎な) system.img を system パーティションへ書込む。
fastboot コマンドは、
与えられた system.img が疎なファイルであることを認識し、
まず system パーティション全体を erase した後、
必要なブロックのみを ZS570KL へ転送して書込んでいる:
senri:~/ZS570KL $ fastboot flash system system.img
target reported max download size of 536870912 bytes
Invalid sparse file format at header magi
erasing 'system'...
OKAY [ 0.002s]
sending sparse 'system' (513215 KB)...
OKAY [ 20.270s]
writing 'system'...
OKAY [ 4.815s]
sending sparse 'system' (524224 KB)...
OKAY [ 20.871s]
writing 'system'...
OKAY [ 5.201s]
sending sparse 'system' (523318 KB)...
OKAY [ 20.565s]
writing 'system'...
OKAY [ 4.793s]
sending sparse 'system' (486869 KB)...
OKAY [ 19.371s]
writing 'system'...
OKAY [ 4.688s]
sending sparse 'system' (513436 KB)...
OKAY [ 20.188s]
writing 'system'...
OKAY [ 4.710s]
sending sparse 'system' (512989 KB)...
OKAY [ 20.225s]
writing 'system'...
OKAY [ 4.705s]
sending sparse 'system' (506575 KB)...
OKAY [ 20.021s]
writing 'system'...
OKAY [ 22.541s]
sending sparse 'system' (67808 KB)...
OKAY [ 2.684s]
writing 'system'...
OKAY [ 0.475s]
finished. total time: 196.124s
senri:~/ZS570KL $
「header magi」 とは何ぞ? 東方より来たりし三賢者?
と思ったら、
「header magic」 と表示しようとしてるのだけど、
fastboot コマンドの内部のバッファの大きさが 1 バイト足らなくて、
1 文字欠けてしまったらしい
(まあ magic の語源は magi なので当たらずとも遠からじだが)。
system/core/libsparse/sparse_read.c を見ると、
static void verbose_error(bool verbose, int err, const char *fmt, ...)
{
char *s = "";
char *at = "";
…… (中略) ……
size = vsnprintf(NULL, 0, fmt, argp);
…… (中略) ……
at = malloc(size + 1);
…… (中略) ……
vsnprintf(at, size, fmt, argp);
…… (中略) ……
if (err == -EINVAL) {
sparse_print_verbose("Invalid sparse file format%s%s\n", s, at);
…… (中略) ……
struct sparse_file *sparse_file_import(int fd, bool verbose, bool crc)
{
…… (中略) ……
if (sparse_header.magic != SPARSE_HEADER_MAGIC) {
verbose_error(verbose, -EINVAL, "header magic");
return NULL;
}
などと書いてある。
せっかくメッセージの長さを調べて、
ちゃんと 1 バイト大きい (size + 1) バッファを確保しているのに、
vsnprintf の第2引数に (size + 1 ではなく) size を与えているために、
最後の 1 文字が欠けてしまうというバグ (>_<)
どうしてこんな単純なバグが放置されているのか?
system/core/libsparse/sparse_format.h によると、
「sparse file format」 というファイル形式があって、
その magic (フォーマット識別子) は 0xed26ff3a であるらしい。
それなら何故 system.new.dat と system.transfer.list
というファイル形式を使っているのだろう?
sparse file format にすれば 1つのファイルで済むように思われるのだが...
max download size (前述の例だと 536870912 bytes)
を超えるファイルを書込もうとすると、
fastboot はそのファイルが
「sparse file format」 であることを期待して、
magic が違うと
「Invalid sparse file format at header magic」 と表示する。
この場合、
通常のファイルとして読み込み直して、
内部で 「sparse file format」 へ変換するコード (sparse_read.c)
になっている。
この場合、
そのファイルが疎か否かは関係なく、
単に 1ブロックが全て同じ値かどうか調べているので、
(疎でない) 通常のファイルを与えた場合も、
必要なブロックのみを転送して書込む仕様になっていた。
以上で、
Android 6.0 Marshmallow WW-4.12.40.1698 のクリーン・インストールが完了した
(4月14日)。
数日間使っているが、全く何の問題も起きていない。
3月27日に問題が発覚してから 19日間、
さんざん苦しめられていたのが嘘のようだ。
もっと早く Marshmallow へ戻せばよかった。
OS のメジャーバージョンを上げるときは、
元に戻す方法を確認してからにすべきと改めて痛感した。
More...