Rebounder Tech Blog

運用している当事者が書く、本番システムの記録。

INSTALL_FAILED_UPDATE_INCOMPATIBLEでDevice Owner機が詰む

公開 読了時間 約6分執筆: Rebounder 開発チーム(当該システムの運用当事者)

※本記事にはアフィリエイトリンクを含む場合があります。内容は広告の有無に影響されません。

結論

Device Owner化したAndroidキオスク端末でAPKの署名鍵をdebugから専用の本番鍵に切り替えると、既設機へのadb install -rはINSTALL_FAILED_UPDATE_INCOMPATIBLEで安全に失敗するが、そこでadb uninstallすると端末管理者権限自体が外れ、工場出荷リセットからの再プロビジョニングしか戻す手段がなくなる。

結論

Android を Device Owner(端末管理者)化して運用しているキオスク端末は、APK の署名鍵を切り替えた瞬間に「更新できない」と「アンインストールしたら戻れない」の二択に落ちる。

  • 署名が違う APK は adb install -r で安全に失敗する(INSTALL_FAILED_UPDATE_INCOMPATIBLE)。既存アプリはそのまま残る
  • そこで「アンインストールしてから入れ直せばいい」と判断すると、Device Owner としての端末管理者権限そのものが外れる
  • Device Owner は通常、工場出荷状態へのリセットからの再プロビジョニングでしか戻せない。現地で詰む

今回は、サイネージ端末(Google TV)に焼くキオスクアプリの署名を、検証用の debug 鍵から専用の本番鍵へ切り替えたことでこの状況に直面した。

症状

これまで現場の端末には、PoC 期間中からの運用で debug 鍵(0951ef53…)で署名した APK を焼いていた。本番運用に向けて、debug 鍵とは別の専用の release 鍵を作り、それで署名したビルドに切り替えた。

// app/build.gradle.kts
// 本番鍵(2026-10-28〜)。鍵はリポに入れず ~/.kimiteras-signing/ に置く。無い環境(CI 等)は debug 鍵に落ちる。
// 既設機(0951 鍵)には署名不一致で上書きできない — dist/APK-MANIFEST.md 参照。
val releaseKs = file(System.getProperty("user.home") + "/.kimiteras-signing/kimiteras-tvbridge-release.p12")
val releasePass = file(System.getProperty("user.home") + "/.kimiteras-signing/storepass.txt")
if (releaseKs.exists() && releasePass.exists()) {
    signingConfigs.create("kimiteras") {
        storeFile = releaseKs
        storeType = "pkcs12"
        storePassword = releasePass.readText().trim()
        keyAlias = "kimiteras-tvbridge"
        keyPassword = storePassword
    }
}
val signing = signingConfigs.findByName("kimiteras") ?: signingConfigs.getByName("debug")

この新しい鍵で署名した APK を、既に Device Owner として現場で稼働している端末へ adb install -r で上書きしようとすると、Android は署名が一致しないパッケージの更新を拒否する。アプリの台帳にもその旨が明記されている。

署名だけ本番鍵 kimiteras-tvbridge(cert SHA-256 B2:D5:99:38:…:C1:97:DC・v2+v3)。鍵は ~/.kimiteras-signing/(KB の外・リポに入れない)。⚠ 0951 鍵の既設機には install -r で入らない(署名不一致)→ 既設機は作り直す時にこの版へ。それまでの復旧(setup-monitor)は a90d6a95… を使う

— dist/APK-MANIFEST.md

Android のパッケージ管理は、同じ applicationId のアプリを上書き更新するとき、署名証明書が一致することを要求する。一致しなければ install -r は(データを壊さずに)安全に失敗し、エラーコード INSTALL_FAILED_UPDATE_INCOMPATIBLE を返す。ここまでは、一般的な Android アプリでも起きる話で、素直に読めば「アンインストールしてから入れ直せばいい」で終わる。

原因

Device Owner はそうではない。これは 2026-06-15 の現地アップデート手順に、同じ失敗コードへの対処として明記されている安全則そのものだった。

## 0. 安全則(必読・最重要)

- 絶対に `adb uninstall` しない。 uninstall すると Device Owner が消え、factory reset 無しでは復旧不能になる。
- 今回APKの署名鍵 `0951ef53…` は 現行本番(FCM版 2026-06-11)と同じ。よって 6/11 を入れた端末は `install -r` で更新できる。
- `install -r` は署名が違えば安全に失敗して既存アプリはそのまま残る(`INSTALL_FAILED_UPDATE_INCOMPATIBLE`)。
  失敗したら その端末は触らず、機種・IP を記録して撤退(古い鍵の端末=別対応)。失敗しても uninstall しない。

— dist/UPDATE-2026-06-15-night-off-recovery.md

Android の Device Owner は、組織が端末全体を管理する前提の仕組みで、通常のアプリと違って adb uninstall のような操作で簡単に外れないよう作られている……という理解は半分しか正しくない。実際には逆で、Device Owner アプリ自身をアンインストールすると、端末管理者権限はそこで一緒に消える。 多くの機種では、消えた Device Owner を再度有効化する経路は「アカウントが一切追加されていない工場出荷状態」からの再プロビジョニングしかなく、現地での dpm set-device-owner 再実行だけでは戻らない。

つまり INSTALL_FAILED_UPDATE_INCOMPATIBLE は、Device Owner 機にとっては「もう一段安全な」失敗ではなく、ここで止まるべき最後の分岐点になる。署名を揃え直す前に uninstall してしまうと、署名の問題を解決する前に、端末管理者権限という土台のほうを先に失う。6 月の時点ではこれは「違う debug 鍵で手元ビルドした端末が稀に混在する」ことへの保険だったが、10 月に専用の release 鍵へ切り替えたことで、既設機すべてがこの分岐の対象になった。

対応

実際に取った判断は、全既設機を一括で本番鍵へ切り替えることではなく、鍵の混在をそのまま許容することだった。

  • 新規に構築する機体には、最初から本番鍵で署名した APK を焼く
  • 既設機(0951 鍵で稼働中)は、そのまま 0951 鍵のビルドで運用を続ける
  • 既設機を本番鍵に切り替えるのは、その端末が壊れて再プロビジョニング(工場出荷リセットからの再構築)が必要になったタイミングに合わせる

一括切り替えをすれば鍵は1本に揃うが、そのために現地で全既設機を工場出荷状態へ戻す作業が必要になる。署名鍵を分けたままにしておくコストと比べて、そちらのほうが現時点では高いという判断になる。

運用ルール

Device Owner 化した端末群の APK を更新する前に、まず確認すべきことは一つだけある。今から焼く APK は、その端末に今入っている APK と同じ鍵で署名されているか。 違うなら install -r はどうせ失敗するので、先に確かめれば INSTALL_FAILED_UPDATE_INCOMPATIBLE 自体を見ずに済む。

もし現地で INSTALL_FAILED_UPDATE_INCOMPATIBLE に遭遇したら、取るべき行動は一つだけ。

  • adb uninstall しない
  • その端末は触らず、機種と IP を記録して撤退する
  • 鍵が揃っている別の APK を焼くか、次にその端末を作り直すタイミングに回す

「署名が違う」は Android が安全に教えてくれているエラーで、ここで止まれば端末は無傷のまま残る。被害が出るのは、このエラーを「じゃあ入れ直そう」の合図だと読んでしまった次の一手の方だった。

よくある質問

Q1INSTALL_FAILED_UPDATE_INCOMPATIBLEが出たらどうすればいいですか?

adb uninstallしないでください。署名が違えばinstall -rは安全に失敗して既存アプリは残ります。その端末は触らず機種とIPを記録して撤退し、鍵が揃っている別の機会か、次に端末を作り直すタイミングに回してください。

Q2なぜuninstallしてから入れ直せば済まないのですか?

Device Ownerとして稼働中の端末はuninstallした瞬間に端末管理者権限自体を失い、再度Device Ownerにするには工場出荷状態へのリセットからの再プロビジョニングが必要になるためです。署名を揃え直すより被害が大きくなります。

Q3既存の端末はどう運用していますか?

既設機は切り替え前の鍵(0951)で署名したビルドのまま運用を続けています。新しい専用の本番鍵に切り替えるのは、新規に構築する機体と、既設機が壊れて再プロビジョニングが必要になったタイミングだけです。

確認した環境

  • Android TV / compileSdk 34, minSdk 26, targetSdk 34(Kotlin, Gradle Kotlin DSL)
  • 2026-10-28 に専用の本番署名鍵(kimiteras-tvbridge)を導入。既設機はそれ以前の debug 鍵(0951ef53…)で稼働中

この記事の根拠

  • ファイル 27〜40行目コミット 650002e
  • Markdownファイル 40〜42行目コミット 650002e
  • Markdownファイル 9〜15行目コミット 650002e

本文の主張は、上の記録に書かれていることだけです。運用しているリポジトリは非公開のため リンクは張れませんが、どのファイルの何行目を、どのコミット時点で見て書いたかは 記事ごとに残しています。推測で書いた箇所はありません。