設定→端末情報→電池使用量がいかがなものか調べてみた
先日のAndroid関西支部「情報交換会」に参加してきました。
LTにも参加したんだけど、私は下っ端扱いだったので触れません。
きっとseitさんが書いてくれるんじゃないかな?w
さてさて、勉強会の中で、電源管理のお話がでました。
タスク切れば電源が持つってのは違うんだよ?
アプリいっぱい眠ってると電池が減る訳じゃないんだよ?
ってお話があって、
kobashinG「あれ?設定→端末情報→電池使用量ってアテにできないのかな?」
と疑問が湧いたので調べてみました。
設定アプリは
$ANDROID-HEAD/package/apps/Settings
にありました。
まずはAndroid端末で問題の画面に行って、
画面上の表示を確認。
それをgrep
$ cd ./package/apps/Settings $ find . -type f -print | xargs grep --color -n "電池使用量" /dev/null
結果からぽいのを探す。
./res/values-ja/strings.xml:864:"電池使用量"
次はAndroidManifest.xmlからこれを使ってるActivityを探す。
面倒なのでgrepした。
./AndroidManifest.xml:766: android:label="@string/power_usage_summary_title"
ほしい結果が得られたので、見に行く。
$ vim AndroidManifest.xml
765 <activity android:name=".fuelgauge.PowerUsageSummary" 766 android:label="@string/power_usage_summary_title" 767 android:clearTaskOnLaunch="true" 768 > 769 <intent-filter> 770 <action android:name="android.intent.action.MAIN" /> 771 <action android:name="android.intent.action.POWER_USAGE_SUMMARY" /> 772 <category android:name="android.intent.category.DEFAULT" /> 773 <category android:name="com.android.settings.SHORTCUT" /> 774 </intent-filter> 775 </activity>
どうやら、PowerUsageSummaryってActivityの様子。
早速追っかける。
onCreate()から怪しいとこを読みあさって、
見つけたのがこの処理。
private void processAppUsage() { SensorManager sensorManager = (SensorManager)getSystemService(Context.SENSOR_SERVICE); final int which = mStatsType; final int speedSteps = mPowerProfile.getNumSpeedSteps(); final double[] powerCpuNormal = new double[speedSteps]; final long[] cpuSpeedStepTimes = new long[speedSteps]; for (int p = 0; p < speedSteps; p++) { powerCpuNormal[p] = mPowerProfile.getAveragePower(PowerProfile.POWER_CPU_ACTIVE, p); } final double averageCostPerByte = getAverageDataCost(); long uSecTime = mStats.computeBatteryRealtime(SystemClock.elapsedRealtime() * 1000, which); mStatsPeriod = uSecTime; updateStatsPeriod(uSecTime); SparseArray<? extends Uid> uidStats = mStats.getUidStats(); final int NU = uidStats.size(); for (int iu = 0; iu < NU; iu++) { Uid u = uidStats.valueAt(iu); double power = 0; double highestDrain = 0; String packageWithHighestDrain = null; //mUsageList.add(new AppUsage(u.getUid(), new double[] {power})); Map<String, ? extends BatteryStats.Uid.Proc> processStats = u.getProcessStats(); long cpuTime = 0; long cpuFgTime = 0; long gpsTime = 0; if (processStats.size() > 0) { // Process CPU time for (Map.Entry<String, ? extends BatteryStats.Uid.Proc> ent : processStats.entrySet()) { if (DEBUG) Log.i(TAG, "Process name = " + ent.getKey()); Uid.Proc ps = ent.getValue(); final long userTime = ps.getUserTime(which); final long systemTime = ps.getSystemTime(which); final long foregroundTime = ps.getForegroundTime(which); cpuFgTime += foregroundTime * 10; // convert to millis final long tmpCpuTime = (userTime + systemTime) * 10; // convert to millis int totalTimeAtSpeeds = 0; // Get the total first for (int step = 0; step < speedSteps; step++) { cpuSpeedStepTimes[step] = ps.getTimeAtCpuSpeedStep(step, which); totalTimeAtSpeeds += cpuSpeedStepTimes[step]; } if (totalTimeAtSpeeds == 0) totalTimeAtSpeeds = 1; // Then compute the ratio of time spent at each speed double processPower = 0; for (int step = 0; step < speedSteps; step++) { double ratio = (double) cpuSpeedStepTimes[step] / totalTimeAtSpeeds; processPower += ratio * tmpCpuTime * powerCpuNormal[step]; } cpuTime += tmpCpuTime; power += processPower; if (highestDrain < processPower) { highestDrain = processPower; packageWithHighestDrain = ent.getKey(); } } if (DEBUG) Log.i(TAG, "Max drain of " + highestDrain + " by " + packageWithHighestDrain); } if (cpuFgTime > cpuTime) { if (DEBUG && cpuFgTime > cpuTime + 10000) { Log.i(TAG, "WARNING! Cputime is more than 10 seconds behind Foreground time"); } cpuTime = cpuFgTime; // Statistics may not have been gathered yet. } power /= 1000; // Add cost of data traffic power += (u.getTcpBytesReceived(mStatsType) + u.getTcpBytesSent(mStatsType)) * averageCostPerByte; // Process Sensor usage Map<Integer, ? extends BatteryStats.Uid.Sensor> sensorStats = u.getSensorStats(); for (Map.Entry<Integer, ? extends BatteryStats.Uid.Sensor> sensorEntry : sensorStats.entrySet()) { Uid.Sensor sensor = sensorEntry.getValue(); int sensorType = sensor.getHandle(); BatteryStats.Timer timer = sensor.getSensorTime(); long sensorTime = timer.getTotalTimeLocked(uSecTime, which) / 1000; double multiplier = 0; switch (sensorType) { case Uid.Sensor.GPS: multiplier = mPowerProfile.getAveragePower(PowerProfile.POWER_GPS_ON); gpsTime = sensorTime; break; default: android.hardware.Sensor sensorData = sensorManager.getDefaultSensor(sensorType); if (sensorData != null) { multiplier = sensorData.getPower(); if (DEBUG) { Log.i(TAG, "Got sensor " + sensorData.getName() + " with power = " + multiplier); } } } power += (multiplier * sensorTime) / 1000; } // Add the app to the list if it is consuming power if (power != 0) { BatterySipper app = new BatterySipper(packageWithHighestDrain, DrainType.APP, 0, u, new double[] {power}); app.cpuTime = cpuTime; app.gpsTime = gpsTime; app.cpuFgTime = cpuFgTime; mUsageList.add(app); } if (power > mMaxPower) mMaxPower = power; mTotalPower += power; if (DEBUG) Log.i(TAG, "Added power = " + power); } }
ちょっと長ったらしいんだけど、上から順に
・CPU使用時間
・TCPのrecv/sendの量
・sensor使用時間
を計算してる。
計上されていくのは「power」って子。
そいつを最後にBatterySipperってのに入れてリスト化。
そいつはどこにいくのかっていうと、
private void refreshStats() { ....省略 processAppUsage(); processMiscUsage(); mAppListGroup.setOrderingAsAdded(false); Collections.sort(mUsageList); for (BatterySipper sipper : mUsageList) { if (sipper.getSortValue() < MIN_POWER_THRESHOLD) continue; final double percentOfTotal = ((sipper.getSortValue() / mTotalPower) * 100); if (percentOfTotal < 1) continue; PowerGaugePreference pref = new PowerGaugePreference(this, ....省略 if (DEBUG) setTitle("Battery total uAh = " + ((mTotalPower * 1000) / 3600));
sipperっていうのが、BatterySipperのインスタンスで、
sipper.getSortValue()で突っ込んだ「power」が取得できる。
DEBUGで切られてるけど、mTotalPowerが総Ahとすると、
final double percentOfTotal = ((sipper.getSortValue() / mTotalPower) * 100);
ここでパーセント計算してるかな。
とすると、
結局「電池使用量」さんは
・CPU使用時間
・TCPのrecv/sendの量
・sensor使用時間
をしっかり見てることになる。
ここを目安に、アプリのお行儀を調べるのもありじゃないかなー?