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

Commit 5ac16ac5 authored by Mat Bevilacqua's avatar Mat Bevilacqua Committed by Android (Google) Code Review
Browse files

Merge "Delete data storage on changes to HAL data" into sc-dev

parents 66fa53a9 05347491
Loading
Loading
Loading
Loading
+29 −10
Original line number Diff line number Diff line
@@ -47,7 +47,8 @@ public class PowerStatsDataStorage {
    private static final long DELETE_AGE_MILLIS = 48 * MILLISECONDS_PER_HOUR;

    private final ReentrantLock mLock = new ReentrantLock();
    private File mDataStorageDir;
    private final File mDataStorageDir;
    private final String mDataStorageFilename;
    private final FileRotator mFileRotator;

    private static class DataElement {
@@ -168,6 +169,7 @@ public class PowerStatsDataStorage {
    public PowerStatsDataStorage(Context context, File dataStoragePath,
            String dataStorageFilename) {
        mDataStorageDir = dataStoragePath;
        mDataStorageFilename = dataStorageFilename;

        if (!mDataStorageDir.exists() && !mDataStorageDir.mkdirs()) {
            Slog.wtf(TAG, "mDataStorageDir does not exist: " + mDataStorageDir.getPath());
@@ -177,33 +179,35 @@ public class PowerStatsDataStorage {
            // filename, so any files that don't match the current version number can be deleted.
            File[] files = mDataStorageDir.listFiles();
            for (int i = 0; i < files.length; i++) {
                // Meter and model files are stored in the same directory.
                // Meter, model, and residency files are stored in the same directory.
                //
                // The format of filenames on disk is:
                //    log.powerstats.meter.version.timestamp
                //    log.powerstats.model.version.timestamp
                //    log.powerstats.residency.version.timestamp
                //
                // The format of dataStorageFilenames is:
                //    log.powerstats.meter.version
                //    log.powerstats.model.version
                //    log.powerstats.residency.version
                //
                // A PowerStatsDataStorage object is created for meter and model data.  Strip off
                // the version and check that the current file we're checking starts with the stem
                // (log.powerstats.meter or log.powerstats.model). If the stem matches and the
                // version number is different, delete the old file.
                int versionDot = dataStorageFilename.lastIndexOf('.');
                String beforeVersionDot = dataStorageFilename.substring(0, versionDot);
                // A PowerStatsDataStorage object is created for meter, model, and residency data.
                // Strip off the version and check that the current file we're checking starts with
                // the stem (log.powerstats.meter, log.powerstats.model, log.powerstats.residency).
                // If the stem matches and the version number is different, delete the old file.
                int versionDot = mDataStorageFilename.lastIndexOf('.');
                String beforeVersionDot = mDataStorageFilename.substring(0, versionDot);
                // Check that the stems match.
                if (files[i].getName().startsWith(beforeVersionDot)) {
                    // Check that the version number matches.  If not, delete the old file.
                    if (!files[i].getName().startsWith(dataStorageFilename)) {
                    if (!files[i].getName().startsWith(mDataStorageFilename)) {
                        files[i].delete();
                    }
                }
            }

            mFileRotator = new FileRotator(mDataStorageDir,
                                           dataStorageFilename,
                                           mDataStorageFilename,
                                           ROTATE_AGE_MILLIS,
                                           DELETE_AGE_MILLIS);
        }
@@ -242,4 +246,19 @@ public class PowerStatsDataStorage {
    public void read(DataElementReadCallback callback) throws IOException {
        mFileRotator.readMatching(new DataReader(callback), Long.MIN_VALUE, Long.MAX_VALUE);
    }

    /**
     * Deletes all stored log data.
     */
    public void deleteLogs() {
        File[] files = mDataStorageDir.listFiles();
        for (int i = 0; i < files.length; i++) {
            int versionDot = mDataStorageFilename.lastIndexOf('.');
            String beforeVersionDot = mDataStorageFilename.substring(0, versionDot);
            // Check that the stems before the version match.
            if (files[i].getName().startsWith(beforeVersionDot)) {
                files[i].delete();
            }
        }
    }
}
+101 −10
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import android.hardware.power.stats.StateResidencyResult;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.AtomicFile;
import android.util.Slog;
import android.util.proto.ProtoInputStream;
import android.util.proto.ProtoOutputStream;
@@ -41,14 +42,17 @@ import com.android.server.powerstats.ProtoStreamUtils.StateResidencyResultUtils;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;

/**
 * PowerStatsLogger is responsible for logging model and meter energy data to on-device storage.
 * Messages are sent to its message handler to request that energy data be logged, at which time it
 * queries the PowerStats HAL and logs the data to on-device storage.  The on-device storage is
 * dumped to file by calling writeModelDataToFile, writeMeterDataToFile, or writeResidencyDataToFile
 * with a file descriptor that points to the output file.
 * PowerStatsLogger is responsible for logging model, meter, and residency data to on-device
 * storage.  Messages are sent to its message handler to request that energy data be logged, at
 * which time it queries the PowerStats HAL and logs the data to on-device storage.  The on-device
 * storage is dumped to file by calling writeModelDataToFile, writeMeterDataToFile, or
 * writeResidencyDataToFile with a file descriptor that points to the output file.
 */
public final class PowerStatsLogger extends Handler {
    private static final String TAG = PowerStatsLogger.class.getSimpleName();
@@ -61,6 +65,10 @@ public final class PowerStatsLogger extends Handler {
    private final PowerStatsDataStorage mPowerStatsModelStorage;
    private final PowerStatsDataStorage mPowerStatsResidencyStorage;
    private final IPowerStatsHALWrapper mPowerStatsHALWrapper;
    private File mDataStoragePath;
    private boolean mDeleteMeterDataOnBoot;
    private boolean mDeleteModelDataOnBoot;
    private boolean mDeleteResidencyDataOnBoot;

    @Override
    public void handleMessage(Message msg) {
@@ -230,16 +238,99 @@ public final class PowerStatsLogger extends Handler {
        pos.flush();
    }

    public PowerStatsLogger(Context context, File dataStoragePath, String meterFilename,
            String modelFilename, String residencyFilename,
    private boolean dataChanged(String cachedFilename, byte[] dataCurrent) {
        boolean dataChanged = false;

        if (mDataStoragePath.exists() || mDataStoragePath.mkdirs()) {
            final File cachedFile = new File(mDataStoragePath, cachedFilename);

            if (cachedFile.exists()) {
                // Get the byte array for the cached data.
                final byte[] dataCached = new byte[(int) cachedFile.length()];

                // Get the cached data from file.
                try {
                    final FileInputStream fis = new FileInputStream(cachedFile.getPath());
                    fis.read(dataCached);
                } catch (IOException e) {
                    Slog.e(TAG, "Failed to read cached data from file");
                }

                // If the cached and current data are different, delete the data store.
                dataChanged = !Arrays.equals(dataCached, dataCurrent);
            } else {
                // Either the cached file was somehow deleted, or this is the first
                // boot of the device and we're creating the file for the first time.
                // In either case, delete the log files.
                dataChanged = true;
            }
        }

        return dataChanged;
    }

    private void updateCacheFile(String cacheFilename, byte[] data) {
        try {
            final AtomicFile atomicCachedFile =
                    new AtomicFile(new File(mDataStoragePath, cacheFilename));
            final FileOutputStream fos = atomicCachedFile.startWrite();
            fos.write(data);
            atomicCachedFile.finishWrite(fos);
        } catch (IOException e) {
            Slog.e(TAG, "Failed to write current data to cached file");
        }
    }

    public boolean getDeleteMeterDataOnBoot() {
        return mDeleteMeterDataOnBoot;
    }

    public boolean getDeleteModelDataOnBoot() {
        return mDeleteModelDataOnBoot;
    }

    public boolean getDeleteResidencyDataOnBoot() {
        return mDeleteResidencyDataOnBoot;
    }

    public PowerStatsLogger(Context context, File dataStoragePath,
            String meterFilename, String meterCacheFilename,
            String modelFilename, String modelCacheFilename,
            String residencyFilename, String residencyCacheFilename,
            IPowerStatsHALWrapper powerStatsHALWrapper) {
        super(Looper.getMainLooper());
        mPowerStatsHALWrapper = powerStatsHALWrapper;
        mPowerStatsMeterStorage = new PowerStatsDataStorage(context, dataStoragePath,
        mDataStoragePath = dataStoragePath;

        mPowerStatsMeterStorage = new PowerStatsDataStorage(context, mDataStoragePath,
            meterFilename);
        mPowerStatsModelStorage = new PowerStatsDataStorage(context, dataStoragePath,
        mPowerStatsModelStorage = new PowerStatsDataStorage(context, mDataStoragePath,
            modelFilename);
        mPowerStatsResidencyStorage = new PowerStatsDataStorage(context, dataStoragePath,
        mPowerStatsResidencyStorage = new PowerStatsDataStorage(context, mDataStoragePath,
            residencyFilename);

        final Channel[] channels = mPowerStatsHALWrapper.getEnergyMeterInfo();
        final byte[] channelBytes = ChannelUtils.getProtoBytes(channels);
        mDeleteMeterDataOnBoot = dataChanged(meterCacheFilename, channelBytes);
        if (mDeleteMeterDataOnBoot) {
            mPowerStatsMeterStorage.deleteLogs();
            updateCacheFile(meterCacheFilename, channelBytes);
        }

        final EnergyConsumer[] energyConsumers = mPowerStatsHALWrapper.getEnergyConsumerInfo();
        final byte[] energyConsumerBytes = EnergyConsumerUtils.getProtoBytes(energyConsumers);
        mDeleteModelDataOnBoot = dataChanged(modelCacheFilename, energyConsumerBytes);
        if (mDeleteModelDataOnBoot) {
            mPowerStatsModelStorage.deleteLogs();
            updateCacheFile(modelCacheFilename, energyConsumerBytes);
        }

        final PowerEntity[] powerEntities = mPowerStatsHALWrapper.getPowerEntityInfo();
        final byte[] powerEntityBytes = PowerEntityUtils.getProtoBytes(powerEntities);
        mDeleteResidencyDataOnBoot = dataChanged(residencyCacheFilename, powerEntityBytes);
        if (mDeleteResidencyDataOnBoot) {
            mPowerStatsResidencyStorage.deleteLogs();
            updateCacheFile(residencyCacheFilename, powerEntityBytes);
        }
    }
}
+44 −6
Original line number Diff line number Diff line
@@ -61,8 +61,12 @@ public class PowerStatsService extends SystemService {
    private static final String MODEL_FILENAME = "log.powerstats.model." + DATA_STORAGE_VERSION;
    private static final String RESIDENCY_FILENAME =
            "log.powerstats.residency." + DATA_STORAGE_VERSION;
    private static final String METER_CACHE_FILENAME = "meterCache";
    private static final String MODEL_CACHE_FILENAME = "modelCache";
    private static final String RESIDENCY_CACHE_FILENAME = "residencyCache";

    private final Injector mInjector;
    private File mDataStoragePath;

    private Context mContext;
    @Nullable
@@ -98,6 +102,18 @@ public class PowerStatsService extends SystemService {
            return RESIDENCY_FILENAME;
        }

        String createMeterCacheFilename() {
            return METER_CACHE_FILENAME;
        }

        String createModelCacheFilename() {
            return MODEL_CACHE_FILENAME;
        }

        String createResidencyCacheFilename() {
            return RESIDENCY_CACHE_FILENAME;
        }

        IPowerStatsHALWrapper createPowerStatsHALWrapperImpl() {
            return PowerStatsHALWrapper.getPowerStatsHalImpl();
        }
@@ -112,10 +128,15 @@ public class PowerStatsService extends SystemService {
        }

        PowerStatsLogger createPowerStatsLogger(Context context, File dataStoragePath,
                String meterFilename, String modelFilename, String residencyFilename,
                String meterFilename, String meterCacheFilename,
                String modelFilename, String modelCacheFilename,
                String residencyFilename, String residencyCacheFilename,
                IPowerStatsHALWrapper powerStatsHALWrapper) {
            return new PowerStatsLogger(context, dataStoragePath, meterFilename,
                modelFilename, residencyFilename, powerStatsHALWrapper);
            return new PowerStatsLogger(context, dataStoragePath,
                meterFilename, meterCacheFilename,
                modelFilename, modelCacheFilename,
                residencyFilename, residencyCacheFilename,
                powerStatsHALWrapper);
        }

        BatteryTrigger createBatteryTrigger(Context context, PowerStatsLogger powerStatsLogger) {
@@ -187,14 +208,31 @@ public class PowerStatsService extends SystemService {
        mPullAtomCallback = mInjector.createStatsPullerImpl(mContext, mPowerStatsInternal);
    }

    @VisibleForTesting
    public boolean getDeleteMeterDataOnBoot() {
        return mPowerStatsLogger.getDeleteMeterDataOnBoot();
    }

    @VisibleForTesting
    public boolean getDeleteModelDataOnBoot() {
        return mPowerStatsLogger.getDeleteModelDataOnBoot();
    }

    @VisibleForTesting
    public boolean getDeleteResidencyDataOnBoot() {
        return mPowerStatsLogger.getDeleteResidencyDataOnBoot();
    }

    private void onBootCompleted() {
        if (getPowerStatsHal().isInitialized()) {
            if (DEBUG) Slog.d(TAG, "Starting PowerStatsService loggers");
            mDataStoragePath = mInjector.createDataStoragePath();

            // Only start logger and triggers if initialization is successful.
            mPowerStatsLogger = mInjector.createPowerStatsLogger(mContext,
                mInjector.createDataStoragePath(), mInjector.createMeterFilename(),
                mInjector.createModelFilename(), mInjector.createResidencyFilename(),
            mPowerStatsLogger = mInjector.createPowerStatsLogger(mContext, mDataStoragePath,
                mInjector.createMeterFilename(), mInjector.createMeterCacheFilename(),
                mInjector.createModelFilename(), mInjector.createModelCacheFilename(),
                mInjector.createResidencyFilename(), mInjector.createResidencyCacheFilename(),
                getPowerStatsHal());
            mBatteryTrigger = mInjector.createBatteryTrigger(mContext, mPowerStatsLogger);
            mTimerTrigger = mInjector.createTimerTrigger(mContext, mPowerStatsLogger);
+84 −0
Original line number Diff line number Diff line
@@ -49,6 +49,12 @@ public class ProtoStreamUtils {
    private static final String TAG = ProtoStreamUtils.class.getSimpleName();

    static class PowerEntityUtils {
        public static byte[] getProtoBytes(PowerEntity[] powerEntity) {
            ProtoOutputStream pos = new ProtoOutputStream();
            packProtoMessage(powerEntity, pos);
            return pos.getBytes();
        }

        public static void packProtoMessage(PowerEntity[] powerEntity,
                ProtoOutputStream pos) {
            if (powerEntity == null) return;
@@ -260,6 +266,12 @@ public class ProtoStreamUtils {
    }

    static class ChannelUtils {
        public static byte[] getProtoBytes(Channel[] channel) {
            ProtoOutputStream pos = new ProtoOutputStream();
            packProtoMessage(channel, pos);
            return pos.getBytes();
        }

        public static void packProtoMessage(Channel[] channel, ProtoOutputStream pos) {
            if (channel == null) return;

@@ -396,6 +408,12 @@ public class ProtoStreamUtils {
    }

    static class EnergyConsumerUtils {
        public static byte[] getProtoBytes(EnergyConsumer[] energyConsumer) {
            ProtoOutputStream pos = new ProtoOutputStream();
            packProtoMessage(energyConsumer, pos);
            return pos.getBytes();
        }

        public static void packProtoMessage(EnergyConsumer[] energyConsumer,
                ProtoOutputStream pos) {
            if (energyConsumer == null) return;
@@ -410,6 +428,72 @@ public class ProtoStreamUtils {
            }
        }

        public static EnergyConsumer[] unpackProtoMessage(byte[] data) throws IOException {
            final ProtoInputStream pis = new ProtoInputStream(new ByteArrayInputStream(data));
            List<EnergyConsumer> energyConsumerList = new ArrayList<EnergyConsumer>();

            while (true) {
                try {
                    int nextField = pis.nextField();
                    EnergyConsumer energyConsumer = new EnergyConsumer();

                    if (nextField == (int) PowerStatsServiceModelProto.ENERGY_CONSUMER) {
                        long token = pis.start(PowerStatsServiceModelProto.ENERGY_CONSUMER);
                        energyConsumerList.add(unpackEnergyConsumerProto(pis));
                        pis.end(token);
                    } else if (nextField == ProtoInputStream.NO_MORE_FIELDS) {
                        return energyConsumerList.toArray(
                            new EnergyConsumer[energyConsumerList.size()]);
                    } else {
                        Slog.e(TAG, "Unhandled field in proto: "
                                + ProtoUtils.currentFieldToString(pis));
                    }
                } catch (WireTypeMismatchException wtme) {
                    Slog.e(TAG, "Wire Type mismatch in proto: "
                            + ProtoUtils.currentFieldToString(pis));
                }
            }
        }

        private static EnergyConsumer unpackEnergyConsumerProto(ProtoInputStream pis)
                throws IOException {
            final EnergyConsumer energyConsumer = new EnergyConsumer();

            while (true) {
                try {
                    switch (pis.nextField()) {
                        case (int) EnergyConsumerProto.ID:
                            energyConsumer.id = pis.readInt(EnergyConsumerProto.ID);
                            break;

                        case (int) EnergyConsumerProto.ORDINAL:
                            energyConsumer.ordinal = pis.readInt(EnergyConsumerProto.ORDINAL);
                            break;

                        case (int) EnergyConsumerProto.TYPE:
                            energyConsumer.type = (byte) pis.readInt(EnergyConsumerProto.TYPE);
                            break;

                        case (int) EnergyConsumerProto.NAME:
                            energyConsumer.name = pis.readString(EnergyConsumerProto.NAME);
                            break;

                        case ProtoInputStream.NO_MORE_FIELDS:
                            return energyConsumer;

                        default:
                            Slog.e(TAG, "Unhandled field in EnergyConsumerProto: "
                                    + ProtoUtils.currentFieldToString(pis));
                            break;

                    }
                } catch (WireTypeMismatchException wtme) {
                    Slog.e(TAG, "Wire Type mismatch in EnergyConsumerProto: "
                            + ProtoUtils.currentFieldToString(pis));
                }
            }
        }

        public static void print(EnergyConsumer[] energyConsumer) {
            if (energyConsumer == null) return;

+351 −12

File changed.

Preview size limit exceeded, changes collapsed.