Loading services/core/java/com/android/server/stats/pull/BatteryHealthUtility.java 0 → 100644 +89 −0 Original line number Diff line number Diff line /* * Copyright 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.stats.pull; import android.util.StatsEvent; import com.android.internal.util.FrameworkStatsLog; import java.math.BigInteger; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Locale; /** * Utility class to redact Battery Health data from HealthServiceWrapper * * @hide */ public abstract class BatteryHealthUtility { /** * Create a StatsEvent corresponding to the Battery Health data, the fields * of which are redacted to preserve users' privacy. * The redaction consists in truncating the timestamps to the Monday of the * corresponding week, and reducing the battery serial into the last byte * of its MD5. */ public static StatsEvent buildStatsEvent(int atomTag, android.hardware.health.BatteryHealthData data, int chargeStatus, int chargePolicy) throws NoSuchAlgorithmException { int manufacturingDate = secondsToWeekYYYYMMDD(data.batteryManufacturingDateSeconds); int firstUsageDate = secondsToWeekYYYYMMDD(data.batteryFirstUsageSeconds); long stateOfHealth = data.batteryStateOfHealth; int partStatus = data.batteryPartStatus; int serialHashTruncated = stringToIntHash(data.batterySerialNumber) & 0xFF; // Last byte return FrameworkStatsLog.buildStatsEvent(atomTag, manufacturingDate, firstUsageDate, (int) stateOfHealth, serialHashTruncated, partStatus, chargeStatus, chargePolicy); } private static int secondsToWeekYYYYMMDD(long seconds) { Calendar calendar = Calendar.getInstance(); long millis = seconds * 1000L; calendar.setTimeInMillis(millis); // Truncate all date information, up to week, which is rounded to // MONDAY calendar.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY); calendar.set(Calendar.HOUR_OF_DAY, 0); calendar.set(Calendar.MINUTE, 0); calendar.set(Calendar.SECOND, 0); calendar.set(Calendar.MILLISECOND, 0); SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd", Locale.US); String formattedDate = sdf.format(calendar.getTime()); return Integer.parseInt(formattedDate); } private static int stringToIntHash(String data) throws NoSuchAlgorithmException { if (data == null || data.isEmpty()) { return 0; } MessageDigest digest = MessageDigest.getInstance("MD5"); byte[] hashBytes = digest.digest(data.getBytes()); // Convert to integer (simplest way, but potential for loss of information) BigInteger bigInt = new BigInteger(1, hashBytes); return bigInt.intValue(); } } services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +52 −1 Original line number Diff line number Diff line Loading @@ -119,6 +119,8 @@ import android.net.NetworkStats; import android.net.NetworkTemplate; import android.net.wifi.WifiManager; import android.os.AsyncTask; import android.os.BatteryManager; import android.os.BatteryProperty; import android.os.BatteryStats; import android.os.BatteryStatsInternal; import android.os.BatteryStatsManager; Loading Loading @@ -243,6 +245,7 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.security.NoSuchAlgorithmException; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.ArrayList; Loading Loading @@ -769,6 +772,7 @@ public class StatsPullAtomService extends SystemService { case FrameworkStatsLog.FULL_BATTERY_CAPACITY: case FrameworkStatsLog.BATTERY_VOLTAGE: case FrameworkStatsLog.BATTERY_CYCLE_COUNT: case FrameworkStatsLog.BATTERY_HEALTH: synchronized (mHealthHalLock) { return pullHealthHalLocked(atomTag, data); } Loading Loading @@ -999,6 +1003,7 @@ public class StatsPullAtomService extends SystemService { registerFullBatteryCapacity(); registerBatteryVoltage(); registerBatteryCycleCount(); registerBatteryHealth(); registerSettingsStats(); registerInstalledIncrementalPackages(); registerKeystoreStorageStats(); Loading Loading @@ -4365,7 +4370,15 @@ public class StatsPullAtomService extends SystemService { ); } int pullHealthHalLocked(int atomTag, List<StatsEvent> pulledData) { private void registerBatteryHealth() { int tagId = FrameworkStatsLog.BATTERY_HEALTH; mStatsManager.setPullAtomCallback(tagId, null, // use default PullAtomMetadata values DIRECT_EXECUTOR, mStatsCallbackImpl); } @GuardedBy("mHealthHalLock") private int pullHealthHalLocked(int atomTag, List<StatsEvent> pulledData) { if (mHealthService == null) { return StatsManager.PULL_SKIP; } Loading Loading @@ -4396,6 +4409,44 @@ public class StatsPullAtomService extends SystemService { case FrameworkStatsLog.BATTERY_CYCLE_COUNT: pulledValue = healthInfo.batteryCycleCount; break; case FrameworkStatsLog.BATTERY_HEALTH: android.hardware.health.BatteryHealthData bhd; try { bhd = mHealthService.getBatteryHealthData(); } catch (RemoteException | IllegalStateException e) { return StatsManager.PULL_SKIP; } if (bhd == null) { return StatsManager.PULL_SKIP; } StatsEvent batteryHealthEvent; try { BatteryProperty chargeStatusProperty = new BatteryProperty(); BatteryProperty chargePolicyProperty = new BatteryProperty(); if (0 > mHealthService.getProperty( BatteryManager.BATTERY_PROPERTY_STATUS, chargeStatusProperty)) { return StatsManager.PULL_SKIP; } if (0 > mHealthService.getProperty( BatteryManager.BATTERY_PROPERTY_CHARGING_POLICY, chargePolicyProperty)) { return StatsManager.PULL_SKIP; } int chargeStatus = (int) chargeStatusProperty.getLong(); int chargePolicy = (int) chargePolicyProperty.getLong(); batteryHealthEvent = BatteryHealthUtility.buildStatsEvent( atomTag, bhd, chargeStatus, chargePolicy); pulledData.add(batteryHealthEvent); return StatsManager.PULL_SUCCESS; } catch (RemoteException | IllegalStateException e) { Slog.e(TAG, "Failed to add pulled data", e); } catch (NoSuchAlgorithmException e) { Slog.e(TAG, "Could not find message digest algorithm", e); } return StatsManager.PULL_SKIP; default: return StatsManager.PULL_SKIP; } Loading Loading
services/core/java/com/android/server/stats/pull/BatteryHealthUtility.java 0 → 100644 +89 −0 Original line number Diff line number Diff line /* * Copyright 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.stats.pull; import android.util.StatsEvent; import com.android.internal.util.FrameworkStatsLog; import java.math.BigInteger; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Locale; /** * Utility class to redact Battery Health data from HealthServiceWrapper * * @hide */ public abstract class BatteryHealthUtility { /** * Create a StatsEvent corresponding to the Battery Health data, the fields * of which are redacted to preserve users' privacy. * The redaction consists in truncating the timestamps to the Monday of the * corresponding week, and reducing the battery serial into the last byte * of its MD5. */ public static StatsEvent buildStatsEvent(int atomTag, android.hardware.health.BatteryHealthData data, int chargeStatus, int chargePolicy) throws NoSuchAlgorithmException { int manufacturingDate = secondsToWeekYYYYMMDD(data.batteryManufacturingDateSeconds); int firstUsageDate = secondsToWeekYYYYMMDD(data.batteryFirstUsageSeconds); long stateOfHealth = data.batteryStateOfHealth; int partStatus = data.batteryPartStatus; int serialHashTruncated = stringToIntHash(data.batterySerialNumber) & 0xFF; // Last byte return FrameworkStatsLog.buildStatsEvent(atomTag, manufacturingDate, firstUsageDate, (int) stateOfHealth, serialHashTruncated, partStatus, chargeStatus, chargePolicy); } private static int secondsToWeekYYYYMMDD(long seconds) { Calendar calendar = Calendar.getInstance(); long millis = seconds * 1000L; calendar.setTimeInMillis(millis); // Truncate all date information, up to week, which is rounded to // MONDAY calendar.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY); calendar.set(Calendar.HOUR_OF_DAY, 0); calendar.set(Calendar.MINUTE, 0); calendar.set(Calendar.SECOND, 0); calendar.set(Calendar.MILLISECOND, 0); SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd", Locale.US); String formattedDate = sdf.format(calendar.getTime()); return Integer.parseInt(formattedDate); } private static int stringToIntHash(String data) throws NoSuchAlgorithmException { if (data == null || data.isEmpty()) { return 0; } MessageDigest digest = MessageDigest.getInstance("MD5"); byte[] hashBytes = digest.digest(data.getBytes()); // Convert to integer (simplest way, but potential for loss of information) BigInteger bigInt = new BigInteger(1, hashBytes); return bigInt.intValue(); } }
services/core/java/com/android/server/stats/pull/StatsPullAtomService.java +52 −1 Original line number Diff line number Diff line Loading @@ -119,6 +119,8 @@ import android.net.NetworkStats; import android.net.NetworkTemplate; import android.net.wifi.WifiManager; import android.os.AsyncTask; import android.os.BatteryManager; import android.os.BatteryProperty; import android.os.BatteryStats; import android.os.BatteryStatsInternal; import android.os.BatteryStatsManager; Loading Loading @@ -243,6 +245,7 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.security.NoSuchAlgorithmException; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.ArrayList; Loading Loading @@ -769,6 +772,7 @@ public class StatsPullAtomService extends SystemService { case FrameworkStatsLog.FULL_BATTERY_CAPACITY: case FrameworkStatsLog.BATTERY_VOLTAGE: case FrameworkStatsLog.BATTERY_CYCLE_COUNT: case FrameworkStatsLog.BATTERY_HEALTH: synchronized (mHealthHalLock) { return pullHealthHalLocked(atomTag, data); } Loading Loading @@ -999,6 +1003,7 @@ public class StatsPullAtomService extends SystemService { registerFullBatteryCapacity(); registerBatteryVoltage(); registerBatteryCycleCount(); registerBatteryHealth(); registerSettingsStats(); registerInstalledIncrementalPackages(); registerKeystoreStorageStats(); Loading Loading @@ -4365,7 +4370,15 @@ public class StatsPullAtomService extends SystemService { ); } int pullHealthHalLocked(int atomTag, List<StatsEvent> pulledData) { private void registerBatteryHealth() { int tagId = FrameworkStatsLog.BATTERY_HEALTH; mStatsManager.setPullAtomCallback(tagId, null, // use default PullAtomMetadata values DIRECT_EXECUTOR, mStatsCallbackImpl); } @GuardedBy("mHealthHalLock") private int pullHealthHalLocked(int atomTag, List<StatsEvent> pulledData) { if (mHealthService == null) { return StatsManager.PULL_SKIP; } Loading Loading @@ -4396,6 +4409,44 @@ public class StatsPullAtomService extends SystemService { case FrameworkStatsLog.BATTERY_CYCLE_COUNT: pulledValue = healthInfo.batteryCycleCount; break; case FrameworkStatsLog.BATTERY_HEALTH: android.hardware.health.BatteryHealthData bhd; try { bhd = mHealthService.getBatteryHealthData(); } catch (RemoteException | IllegalStateException e) { return StatsManager.PULL_SKIP; } if (bhd == null) { return StatsManager.PULL_SKIP; } StatsEvent batteryHealthEvent; try { BatteryProperty chargeStatusProperty = new BatteryProperty(); BatteryProperty chargePolicyProperty = new BatteryProperty(); if (0 > mHealthService.getProperty( BatteryManager.BATTERY_PROPERTY_STATUS, chargeStatusProperty)) { return StatsManager.PULL_SKIP; } if (0 > mHealthService.getProperty( BatteryManager.BATTERY_PROPERTY_CHARGING_POLICY, chargePolicyProperty)) { return StatsManager.PULL_SKIP; } int chargeStatus = (int) chargeStatusProperty.getLong(); int chargePolicy = (int) chargePolicyProperty.getLong(); batteryHealthEvent = BatteryHealthUtility.buildStatsEvent( atomTag, bhd, chargeStatus, chargePolicy); pulledData.add(batteryHealthEvent); return StatsManager.PULL_SUCCESS; } catch (RemoteException | IllegalStateException e) { Slog.e(TAG, "Failed to add pulled data", e); } catch (NoSuchAlgorithmException e) { Slog.e(TAG, "Could not find message digest algorithm", e); } return StatsManager.PULL_SKIP; default: return StatsManager.PULL_SKIP; } Loading