Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c4b9427f authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Create puller for batteryHealthData" into main

parents ed5cc9b3 a5af2d38
Loading
Loading
Loading
Loading
+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();
    }
}
+52 −1
Original line number Diff line number Diff line
@@ -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;
@@ -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;
@@ -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);
                        }
@@ -999,6 +1003,7 @@ public class StatsPullAtomService extends SystemService {
        registerFullBatteryCapacity();
        registerBatteryVoltage();
        registerBatteryCycleCount();
        registerBatteryHealth();
        registerSettingsStats();
        registerInstalledIncrementalPackages();
        registerKeystoreStorageStats();
@@ -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;
        }
@@ -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;
        }