iPhoneのバッテリーが1日持たない理由を、iOSの隠しログで突き止めた 〜 犯人はアプリではなく「電波」だった 〜


注)本記事の内容は正確性を保証するものではない。

(2026年8月7日 追記)YouTube動画を追加しました。 https://youtu.be/r4u2b1w6-84

目次

はじめに:設定画面の「◯%」では何も分からない

iPhoneでいろいろなアプリを常時動作させていたら、バッテリーが1日持たなくなった。バッテリー残量が20%という警告が出たので、省電力モードに変えたり、位置情報記録アプリのGPS使用を制限したりした。これほど、早くバッテリーの電力量が低下したのは珍しい。OSがバージョンアップし機能が向上しても、バッテリーがもたなければ機能を制限しなければいけなくなるので、今のうちに対策を行なっておきたい。


また、設定 → バッテリーを開いたが、しかしそこに並ぶのは「アプリA 12%、アプリB 8%……」という按分比率だけで、いくつかの決定的な問いに答えてくれない。

  • そのアプリは何で電気を食っているのか(通信か、GPSか、画面か)
  • 常時動作をやめたら、実際に何分伸びるのか
  • そもそも、リストに出ていない消費はどこへ行ったのか

私が最終的にやりたいのは、バッテリーが1日十分もつようにするためには、常時動作させるアプリをどのように決めればいいかを決めるためのシミュレータの作成だが、そのためには按分%ではなく、絶対値と内訳が要る。

結論から言うと、iOSの内部にはそれが全部入っているデータベースがある。
そして掘っていくうちに、当初の仮説は3回ひっくり返った。

本記事は、シミュレータの実現可能性や、消費電力増大の原因究明をClaude Opus 5に依頼した際の「調査ドキュメント」である。

1. まず設計:どこまでが可能で、どこからが不可能か

シミュレータを作ると決めた時点で、実現可能性を3つのレベルに分けて整理した。

レベル内容実現可能性
AiPhone上のアプリが他アプリの消費電力を読み取る不可能(サンドボックスの原則的制約)
BMac側で動く推定シミュレータ+実測データの投入十分可能
C「あと何時間持つか」の高精度予測条件付き・±20〜30%が限界

つまり「アプリとして自動計測」は最初から諦めるしかない。
計測は別手段で行い、シミュレータはキャリブレーション済みの係数で動かす——これが唯一の現実解だ。

データをどう取るか:4つの層

手段取れるもの難易度判定
① 設定 → バッテリーアプリ別の按分%、画面上/背面の時間無料・全ユーザー粒度不足
sysdiagnose の PowerLogアプリ別 × ハードウェア別のエネルギー(1時間ごと)本命
③ Xcode InstrumentsCPU/GPU/通信ごとの電力インパクト自作アプリ限定で対象外
④ 外部ハードで実測(INA226 + ESP32)端末全体の電流の絶対値②の校正用に有用

③は強力なツールだが、自分がビルドしたアプリにしかアタッチできない
LINEやXの計測には使えないので、今回の用途からは外れる。

②のPowerLogが本命だ。
iOSは内部的にエネルギー会計をSQLite形式で持っていて、脱獄せずとも sysdiagnose 経由で取り出せる。

2. PowerLogを取り出す(ここは3ステップだけ)

意外にも手順は短い。

Step 1|iPhoneでsysdiagnoseを生成
電源ボタン + 音量上下ボタンを同時押し振動があればOK。生成には数分〜10分かかる。

ボタンの同時押しは少し難しい。各ボタンを押すタイミングによっては、画面のスクリーンショット(画面コピー)を撮ってしまったり、「スライドで電源オフ」の画面を表示してしまう。
下記の画面が出たら成功。

Step 2|取り出す
設定 → プライバシーとセキュリティ → 解析と改善 → 解析データ
リストを sysdiagnose で検索するとtar.gzが見つかるので、共有 → AirDropでMacへ。200〜500MB程度ある。

作成中は「IN_PROGRESS_…」のtmpファイルが表示される

Step 3|Macで展開

tar -xzf sysdiagnose_*.tar.gz
cd sysdiagnose_*/logs/powerlogs
ls

ここに powerlog_YYYY-MM-DD_HH-MM_XXXXXXXX.PLSQL というファイルがある。
拡張子が .gz でなくても正常で、これがそのままSQLiteデータベースである。
.gz になっているのは Archives/ 内の過去日分のみ)。
最初「圧縮されていないが大丈夫か」と戸惑ったが、file * で確認すれば SQLite 3.x database と出る。

作業用にコピーしてから開く。

cp powerlog_*.PLSQL ~/work/pl/pl.PLSQL
sqlite3 ~/work/pl/pl.PLSQL

(補足)sqlite3は、軽量なデータベース管理システム「SQLite3」を起動するコマンドで、上記命令は、「pl.PLSQL」とファイル名変更しコピーしたデータベースファイルを開き、SQLite3の対話型シェル(操作画面)を起動するものであり、以降の調査は、このデータベースファイルに対する各種クエリを実行することによって行われた。

3. データベースの構造:たった2つのテーブルが主役

SQLite3の対話型シェル(操作画面)で、「.tables」の命令を実行すると257個のテーブルが出てきた。
圧倒されるが、必要なのは実質2つだ。

テーブル内容
PLAccountingOperator_EventNone_NodesノードID ↔ 名前(アプリのBundle ID/ハードウェア名)の対応表
PLAccountingOperator_Aggregate_RootNodeEnergyアプリ別 × ハードウェア別のエネルギー(1時間ごと集計)

構造の肝は、RootNodeEnergyNodeID(=誰が)と RootNodeID(=何を使って)の2つの外部キーを持つ点にある。
つまり同じ対応表に2回JOINする(結合する)と、例えば、「Ringアプリが、セルラー無線で、何µWh使ったか」が一発で分かる表ができあがる。

SELECT app.Name AS app, hw.Name AS hardware, ROUND(SUM(e.Energy)) AS energy
FROM PLAccountingOperator_Aggregate_RootNodeEnergy e
JOIN PLAccountingOperator_EventNone_Nodes app ON app.ID = e.NodeID
JOIN PLAccountingOperator_EventNone_Nodes hw  ON hw.ID  = e.RootNodeID
GROUP BY app.Name, hw.Name
ORDER BY energy DESC;

下図(図4-B)の上段の表が、各アプリが何を使って電力を消費したかを示すものである。
iPhoneの「設定」のバッテリーの画面(設定画面)に表示されているのは、その表の右端に表示された「全体に占める割合」であり、何を使った消費量かまでは分からない。
使ったのはGPSなのかセルラーなのかBLEなのかによって打つべき手はまったく変わる。


上図(図4-B)の下段の表で、注目してほしいのは色の分布です。8/6の3行はBB列(赤)だけに値があり、8/3〜8/5の行はWiFi-Data列(青)に集中しています。つまり同じアプリでも、日によって使っているハードウェアがまるで違う。
セルラーで通信していた日とWi-Fiで通信していた日が、はっきり分かれて見えるわけです。

4. 第一の発見:突出した犯人はいなかった

まずアプリ別ランキングを取った(上位のみ抜粋)。

ノード割合
com.apple.apsd(プッシュ通知デーモン)10.21%
com.apple.lock-screen5.85%
BB-Standard(セルラー待機)5.69%
bbl.intl.bambulab.com4.46%
WiFi-Idle3.66%
Bluetooth3.61%
com.ring3.46%
com.thetileapp.tile3.43%
com.apple.CarPlayHomeLocation3.35%

上位30件を足しても76%で、残り24%は裾野に広がっている。
1位でも10%しかない。「これを切れば解決」という構造ではなく、多数の小口の積み上げだった。
1日持たない理由がこれで説明できる。

そして最大の発見は1位の正体だ。
com.apple.apsd はプッシュ通知デーモン、つまり全アプリの通知を集約して受け取っているOSのプロセスである。

ここが重要なのだが、この10.2%は設定画面のアプリ別リストには一切現れない
各アプリに配賦されていないのだ。
「アプリを1つ常時動作に追加するコスト」は、そのアプリ自身の消費 + apsdへの寄与分の合計であり、後者は公式UIからは完全に不可視になっている。

PowerLogを掘る価値が、まさにここにあった。

機能クラスタで束ねると構造が見える

個別のBundle IDを眺めていても頭に入らないので、機能で束ねてみた。

クラスタ合計
通知・端末間連携(apsd, continuity.notifications, alloy.home, sharingd)約16.6%
無線待機(BB-Standard, WiFi-Idle, BB-Pssi)約10.3%
位置情報(CarPlayHomeLocation, Maps, routined, GPS)約7.8%
BLE常時接続(Bluetooth, Tile)約7.0%
ロック画面表示(lock-screen, SmartNewsウィジェット)約7.0%

さらに、通知のコストは二重であることも見えた。
apsd(無線を起こす)10.2% + lock-screen(画面が点灯する)5.85% = 約16%
通知を1件減らすと、無線と画面の両方に効く。単一の対策としては最大の効果が見込める。

下記の2つのブログ記事で、周囲の環境に応じて「通知」を最適化する効果(意識を削がれ過ぎない)について紹介したが、周囲の環境に応じて通知を制限することが、バッテリーの持ちにも効果があるようだ。

5. 単位を確定させる:Energy 1 = 1 µWh

Energy カラムの単位はドキュメント化されておらず、iOSバージョンで解釈が揺れる。
ここは実測で校正するしかない。

PLBatteryAgent_EventBackward_Battery には電圧(mV)と電流(mA)の生値が約20秒間隔で入っている。
画面オフのアイドル時を見ると -11mA × 4.231V ≒ 46mW
一方、PowerLogの同日合計から換算した値は約985mW。

  • µWh前提 → 985mW(妥当)
  • mWh前提 → 985W(あり得ない)
  • nWh前提 → 0.985mW(あり得ない)

Energy 1単位 = 1 µWh で確定。これでシミュレータの出力単位が決まった。

6. 第二の発見:7/31に段差があった(そして落とし穴)

次に日次推移を見た。
ここで大きな罠にはまりかけた。

SELECT date(timestamp,'unixepoch','localtime') AS day,
       COUNT(DISTINCT strftime('%H',datetime(timestamp,'unixepoch','localtime'))) AS hours_logged,
       ROUND(SUM(Energy)/1000.0) AS mWh
FROM PLAccountingOperator_Aggregate_RootNodeEnergy
GROUP BY day ORDER BY day;

データは約707時間分(7/8〜8/6)あった。
単純に平均すると320mW、1日あたり7.7Wh。
iPhoneのバッテリーは14〜17Whなので、計算上は2日近く持つことになる。
体感とまるで合わない。

原因は hours_logged カラムにあった。
7/23以前はこの値が1なのだ。
つまり、古いデータは間引き・圧縮されており、1日1時間分しか残っていない。
29.5日の平均を出したつもりが、スカスカのデータで希釈されていただけだった。

実質的な分析対象は7/24〜8/6の14日間のみ。
そこを mWh ÷ hours_logged で正しく再計算すると——

期間平均電力1日換算
7/24〜7/30428 mW10.3 Wh/日
7/31〜8/05655 mW15.7 Wh/日
8/06(20時間分)985 mW23.6 Wh/日

7/31を境に、消費が約1.5倍に跳ね上がっている。
直近の週は1日でバッテリー1本を使い切る水準で、体感と完全に一致した。

Opus 5によると、上図のグレー網掛け領域は、古いデータの部分であり、間引き・圧縮されており、1日1時間分しか残っていない部分で、比較データとしては使えない部分とのこと。
また、W1/W2は、Week 1 / Week 2 の略で、それぞれの期間は、W1 = 7/24〜30、W2 = 7/31〜8/5とのこと。
このように期間を区切っているのは、7/23以前はログが間引かれていて使えないので開始が7/24になり、日次推移で7/31に段差が出たのでそこを境界にしているとのことだった。

7. 第三の発見:犯人はRing……のはずだった

段差が見つかれば、あとは前後で何が変わったかを比べればいい。アプリ別に週次比較を取った。

アプリW1(7/24-30)W2(7/31-8/5)差分
com.ring
(Ringアプリ)
20 mWh/日1,255 mWh/日+1,235
com.apple.apsd
(プッシュ通知デーモン)
6591,573+914
BB-Standard
(セルラーモデムの待機・接続維持)
626930+303
com.readdle.smartemail
(Sparkアプリ)
279558+279
com.thetileapp.tile
(Tileアプリ)
291464+173

Ringが63倍。全体の増加分(約5,400 mWh/日)のうち1,235、実に23%をRing単独が占めていた。

ハードウェア別で見ても整合する。

ハードウェアW1W2差分
BB(セルラー)2,3395,289+2,951
CPU2,0102,609+599
WiFi-Data8471,237+390
GPS1,069318-750

増分はBBに圧倒的に集中しているので、セルラー通信が圧倒的に電力を消費していることが分かった。
Ring(+1,235) + apsd(+914) + BB-Standard(+303) + Spark(+279) = 2,731で、BBの増分をほぼ説明しきる。

犯人は、ドアホンシステムのRingだ——この時点ではそう結論した。

ちなみに、当初「削減候補No.1」と睨んでいたCarPlay関連のGPS消費は、W2では-750と大きく減っていた
位置情報の学習が収束したらしく、放っておいても自然に解決していた項目だった。

8. しかし、真犯人は別にいた

ここで、私(ユーザー)が最近の行動・状況を思い返して、下記の2つの心当たりをOpus 5に伝えた。

  1. Tracerという位置情報記録アプリのGPS使用を「常時」に変更した
  2. 設定画面のバッテリー項目に「電波状況がよくありません」の警告が複数のアプリに出ていた

データを確認した結果、Tracer説は否定された。 GPSは週次で1,069 → 318 mWh/日と大きく減っている。増えていない。

電波状況説は、強く裏付けられた。
Spark、Tile、iPhoneミラーリングの3つすべてに警告が出ており、ハードウェア別の増分はBBが+2,951で圧倒的だった。

ここで解釈が変わる。
Ringの増加(+1,235)は「Ringが大量のデータを送った」のではなく、弱電界の中でRingが通信したために、送信電力が跳ね上がったということだ。
同じ通信量でも、電波が悪ければ数倍のエネルギーを食う。

つまり構造はこうだった。

弱電界 × (Ring + apsd + Spark + ミラーリングの通信) = BB +2,951

Ringが引き金、弱電界が増幅器。
「犯人はRing」という前節の結論は、半分しか正しくなかった。

9. 決め手:Appleは「アプリ × 電波状況」を集計していた

仮説を検証するため、無線系のテーブルを洗い出した。
3つの決定的なテーブルが見つかった。

テーブル中身
PLBBAgent_EventPoint_TelephonyActivitysignalStrength, signalBars, currentRat, activeBand
PLAppTimeService_Aggregate_CellularConditionアプリ別 × 電波状況(SignalBars)の集計
PLProcessNetworkAgent_EventForward_HighCellularBWTransactionsプロセス別の高帯域セルラー通信

Appleが公式に「アプリ × 電波状況(電界強度に関連)」を集計しているという事実は、この掛け算構造がエネルギー会計上も本質的だとApple自身が認めているに等しい。

実際にそのSQLiteデータベースのテーブルを読んでみる。
iPhoneの画面の右上に表示されるSignalBars別に、1時間あたりのセルラー使用秒数を出した。


SignalBars記録時間セルラー使用秒/時間
1(弱)19h433秒
218h276秒
320h445秒
4(強)42h3,257秒

電波が強い時のほうが、セルラー使用時間は7倍以上長い。
にもかかわらず、BBの消費は電波が強い時間帯のほうが小さい。

通信量あたりのエネルギー効率が、電界強度によって桁違いに変わる

これが「電波状況がよくありません」という警告の実体だった。

そして、最悪の条件で通信していたアプリ

アプリ別にセルラー使用時間と平均SignalBarsを並べると、はっきりした外れ値が出た。

アプリセルラー使用時間平均SignalBars
com.thetileapp.tile615.8分2.77 ← 最悪
com.apple.WebKit.Networking392.0分3.45
com.ring313.3分3.92

Tileは3日間で10時間以上セルラーを使い、しかも全アプリ中で最も電波の悪い条件下で通信している(全体平均は3.4〜3.9)。

これは偶然ではなく構造的な必然だ。
TileはBLEで見失った時に位置を報告するアプリなので、通信が発生するのは移動中や屋内——つまり電波の悪い場所に偏る。
最も高コストな条件で通信するよう設計されているわけだ。

もう一つの発見が com.apple.WebKit.Networking(2位、392分)。
iOSではChromeを使っていても実体はWebKitなので、ブラウザ通信はすべてここに集約される。
設定画面のアプリ別リストには現れない隠れた消費で、PowerLogを見ない限り発見できない。

(注)なお CellularCondition の保持期間は3日しかないので、このカーブは8/4〜8/6分のみに基づくもの。

10. シミュレータの設計が根本から変わった

当初のモデルはこうだった。

E_total = Σ アプリごとの消費

しかし実データが示したのは、まったく別の構造だ。

E_radio = 電界強度係数 × Σ (アプリの通信イベント)

電界強度が全体にかかる乗算項になっている。
この係数が2〜3倍動くなら、アプリ構成をどれだけ最適化しても、電波の悪い場所にいるだけで効果が消し飛ぶ。

ここから3つの設計変更が導かれた。

① 「電波環境」を第一級の入力パラメータにする
出力も単一の数値ではなく、「良好な電界なら◯時間、弱電界なら◯時間」のレンジであるべきだ。

② 入力は「アプリのリスト」ではなく「(アプリ, 設定プロファイル) の組」にする
Ringは同じアプリのまま 20 → 1,255 mWh/日 に変わった。
「Ringを入れる/入れない」を選ぶモデルでは、この現実を表現できない。
検知感度、フェッチ間隔、通知ON/OFFといった設定パラメータが、消費を1〜2桁動かす。

③ 加法モデルを捨てる
iOSはバックグラウンド更新をコアレッシング(まとめ実行)するので、「アプリAが3%、Bが2%だから両方切れば5%節約」は成立しない。
2つ目以降のポーリングアプリは無線のテールエネルギーに相乗りするため、限界コストが下がる。
Ringの+1,235がBBの+2,951と非線形に結びついていたのが、まさにこの現象だ。

この非線形性こそが、単なる計算式ではなく「シミュレータ」を作る価値そのものだった。

そして実用上の示唆も出た。
通信の絶対量を減らすより、通信を電波の良い時間帯・Wi-Fi環境に寄せるほうが効果が大きい可能性がある。

11. 分かったことと、これからやること

14日間のPowerLogから確定したこと。

  • この端末は「画面を見すぎている端末」ではない。無線だけで全体の46%セルラー36.3% + Wi-Fi 10.0%)を占め、ディスプレイはわずか11%だった
  • セルラー消費の29%はプッシュ通知デーモン単独であり、これは設定画面に一切表示されない
  • 電界強度は、アプリ構成の最適化を無効化しうるほど支配的な要因である
  • 費用対効果の観点で最も見直し価値が高いのはTile(BLE常時スキャン + 最悪条件でのセルラー通信

手打ちSQLの限界

一方で、明確な限界にもぶつかった。CellularCondition テーブルの保持期間はわずか3日
RootNodeEnergy も7/23以前は間引かれている。
継続的に取得して蓄積しない限り、これ以上の検証はできない構造が確定した。

そこで次のフェーズはこうなる。

  1. 日次取得の自動化 — sysdiagnoseからPowerLogだけを抽出・保管するシェルスクリプト
  2. 正規化と蓄積 — 複数の .PLSQL から (日時, アプリ, ハードウェア, エネルギー, SignalBars, セルラー使用時間) の統合テーブルを作り、期間の重複を自動マージしてParquet/CSVで蓄積
  3. 分析 — SignalBars別のエネルギー効率カーブを正しく推定し、アプリ別の日次推移を作図
  4. シミュレータ — 得られた係数から、(アプリ構成, 設定, 想定電界環境) → 日次消費 を予測

並行して、1日1変数だけを動かす検証実験も始める。

変更する条件検証したい仮説
Day 1何も変えないベースライン確立
Day 25Gオート → 4G固定4G⇄5G切替の往復コスト
Day 3Tileのバックグラウンド更新OFF最大のセルラー消費者
Day 4Ringのモーション通知OFF7/31の変化点

おわりに:当初の問題意識は正しかった

無線が46%を占め、ディスプレイが11%しかない。
この構造が意味するのは、この端末の消費を支配しているのが常時接続のバックグラウンド動作だということだ。
「常時動作させるアプリをどう選ぶか」という最初の問題意識は、そのまま正しい着眼点だった。

ただし答えの形は変わった。
選ぶべきは「アプリ」ではなくアプリ × 設定 × 通信する場所」の組み合わせだった。

そして何より、設定画面が見せてくれる按分%は、真実の一部でしかない
プッシュ通知デーモンの10%も、WebKitに集約されたブラウザ通信も、電波の悪さという乗算項も、公式UIには存在しない。iOSは自分のエネルギー会計を精密に記録している。ただ、それを見せてくれないだけだ。

注意事項

sysdiagnoseには、Wi-Fi接続履歴、位置情報、インストール済みアプリの一覧、通信先など大量の個人情報が含まれているので、共有する場合はスクリーンショットの範囲に十分注意が必要。
また、PowerLogのテーブル構造はiOSのバージョンごとに変わります。この記事は iOS 26 時点のものです。

今回の調査で初めて、 sysdiagnose の PowerLog抽出を行なったが、本記事の情報を引き出すため、ターミナルでのBashコマンド実行、SQLite3の対話型シェル(操作画面)でのクエリ実行を数多く行う羽目になった。最終的な結論にたどり着くまで仮説が何度も変わり、Opus 5も試行錯誤しながら、次のクエリの実行をユーザーに指示しているようだった。sysdiagnoseからPowerLogだけを抽出・保管するシェルスクリプトを提案してくれるのはありがたいが、iOSがバージョンアップし、PowerLogのテーブル構造が変わるようであれば、また試行錯誤の調査が必要。

目次