古いAndroidにadb restoreする方法

#tl;dr
adb backupしたファイルは、メジャーバージョンの古いAndroidにはリストアできません。
[android backup extractor](https://sourceforge.net/projects/adbextractor/)でリパックしましょう。

#はじめに
Google Pixel/Pixel XLの日本での発売がアナウンスされなくてやきもきしている人も多いことと思います。
そう言う私もPixel待ちのまま、気が付けば二月。例年通りであれば五月のGoogle I/OでO Developer Previewが発表されてしまうはずです。PixelでODPを試験する予定だったのに、日本での発売見込みすらたっていません。

てことで、Nexus 5XにODPが来るはず！との予測のもと、普段使いのNexus 5Xを予備機に回すことにして、普段使い用にZenfone3(2017/2月現在Android6.0.1)を購入しました。

#OSバージョン古くなっちゃうけどいいの！？
よくない。けど、Nexusからメーカー機に移る以上仕方が無いでしょ？それに、Android6.0.1なら比較的安定してるし……

#バックアップが戻せない
まず、セットアップ時にGoogleアカウントを入れても、Android7.xデバイスからの環境移行はしてくれませんでした。
まあ、そんなこともあるさと、諸々adb設定を済ませたのち、

```java:移行元
adb backup -all -apk -nosystem -shared -f backup.ab
```
とやって、Nexus 5Xのバックアップを取得します。完了したら、Zenfone3につなぎ替えて

```java:移行先
adb restore backup.ab
```
です。
（パスワード入れると即座に終了するバックアップアプリ)
あれ？
もっかいコマンドを叩いてみても同じです。

こういう時は、logcatを見るに限ります。

```java:logcat
BackupManagerService: Wrong header version: 4
BackupManagerService: Invalid restore data; aborting.
BackupManagerService: Full restore processing complete.
```
ほうWrong header versionとな……

aospを探すと見つかりました。
[BackupManagerService.java](http://tools.oesf.biz/android-7.1.1_r1.0/xref/frameworks/base/services/backup/java/com/android/server/backup/BackupManagerService.java)

```BackupManagerService.java
final int archiveVersion = Integer.parseInt(s);
if (archiveVersion <= BACKUP_FILE_VERSION) {
  ...中略...
} else Slog.w(TAG, "Wrong header version: " + s);
```
なるほど、バージョンが古いとエラーになる、と。

Android7.0のソースでは以下の様に定義されています。

```BackupManagerService.java
static final int BACKUP_FILE_VERSION = 4;
```

Android6.0/5.0

```BackupManagerService.java
static final int BACKUP_FILE_VERSION = 3;
```
Android4.4.3

```BackupManagerService.java
static final int BACKUP_FILE_VERSION = 2;
```
Android4.4

```BackupManagerService.java
static final int BACKUP_FILE_VERSION = 1;
```
てな具合に定義されておりました。
ざっと見た感じリストア時の後方互換性はありそうですが、ヘッダバージョンを指定してバックアップを作成する機能はサポートされていないようです。

#ではどうするか
リストアを諦めて全部のアプリを手動でインストールし直すしかないのでしょうか。

「adb backup」でググると、[android backup extractor](https://sourceforge.net/projects/adbextractor/)というツールに辿り着きます。[github](https://github.com/nelenkov/android-backup-extractor)

このツールは主にadb backupしたファイルから特定のファイルを取り出すのに使われるようなのですが、pack機能もあります。てことは、**一旦unpackして再度packしてやれば古いバージョンになるんじゃないか？**

#まずインストール
android backup extractorはJavaで書かれているので、上記リンクからダウンロードし、解凍しておきます。

次に、AES256が必要なので、[この記事](http://qiita.com/mizuki_takahashi/items/cc26a7fd51aa04396e92)を参考にJavaのポリシーを書き替えましょう。

#次に変換

```
java -jar abe.jar unpack backup.ab backup.tar <password>
```
で、バックアップファイルをtarに変換します。すかさず、

```
java -jar abe.jar pack backup.tar repack.ab <password>
```
とやって、リパックします。(BACKUP_FILE_VERSION=1として出力されます)

#そしてリストアへ
これを、

```
adb restore repack.ab
```
でリストアします。

パスワードを入れたら、固唾を呑んで見守ります。
**おお！リストア成功！**

(最初はapk一個で試しておくといいです)

#注意事項
- 各BACKUP_FILE_VERSIONの機能差分は調べてません。バージョン落とす際に何らかの情報が欠落している可能性は否定できません。ご利用は自己責任でどうぞ。
- Dropboxなどのバックアップ対象外のアプリのバックアップが取れないのはAndroidの仕様です。
- "-shared"でバックアップを取得したのに、一部共有ストレージの内容がリストアされないことがありました。原因は不明です。共有ストレージの内容は、ネットワーク経由でファイルコピーして移行しましょう。
- リストアできなくても泣かない

#最後に
私のZenfone3は本格運用を開始した直後に液晶内ゴミが見つかったため、一日でメーカー送りとなりました。皆様におかれましては、このような初歩ミスを犯さないようにご注意いただきたいと思います。

#余談
二段階認証のためのGoogle authenticatorアプリはadb backupでバックアップが取れないので、IIJ SmartKeyアプリに乗り換えておくといいです。