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

Commit b5c9f6e2 authored by Mat Bevilacqua's avatar Mat Bevilacqua Committed by Automerger Merge Worker
Browse files

Merge "Delete data storage on changes to HAL data" into sc-dev am: 5ac16ac5

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/13536152

MUST ONLY BE SUBMITTED BY AUTOMERGER

Change-Id: Ib1aa26137296e5fce380c01602d19984ab99ef89
parents 08a307c6 5ac16ac5
Loading
Loading
Loading
Loading
+29 −10
Original line number Original line Diff line number Diff line
@@ -47,7 +47,8 @@ public class PowerStatsDataStorage {
    private static final long DELETE_AGE_MILLIS = 48 * MILLISECONDS_PER_HOUR;
    private static final long DELETE_AGE_MILLIS = 48 * MILLISECONDS_PER_HOUR;


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


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


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


            mFileRotator = new FileRotator(mDataStorageDir,
            mFileRotator = new FileRotator(mDataStorageDir,
                                           dataStorageFilename,
                                           mDataStorageFilename,
                                           ROTATE_AGE_MILLIS,
                                           ROTATE_AGE_MILLIS,
                                           DELETE_AGE_MILLIS);
                                           DELETE_AGE_MILLIS);
        }
        }
@@ -242,4 +246,19 @@ public class PowerStatsDataStorage {
    public void read(DataElementReadCallback callback) throws IOException {
    public void read(DataElementReadCallback callback) throws IOException {
        mFileRotator.readMatching(new DataReader(callback), Long.MIN_VALUE, Long.MAX_VALUE);
        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 Original line Diff line number Diff line
@@ -26,6 +26,7 @@ import android.hardware.power.stats.StateResidencyResult;
import android.os.Handler;
import android.os.Handler;
import android.os.Looper;
import android.os.Looper;
import android.os.Message;
import android.os.Message;
import android.util.AtomicFile;
import android.util.Slog;
import android.util.Slog;
import android.util.proto.ProtoInputStream;
import android.util.proto.ProtoInputStream;
import android.util.proto.ProtoOutputStream;
import android.util.proto.ProtoOutputStream;
@@ -41,14 +42,17 @@ import com.android.server.powerstats.ProtoStreamUtils.StateResidencyResultUtils;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.IOException;
import java.util.Arrays;


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


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


    public PowerStatsLogger(Context context, File dataStoragePath, String meterFilename,
    private boolean dataChanged(String cachedFilename, byte[] dataCurrent) {
            String modelFilename, String residencyFilename,
        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) {
            IPowerStatsHALWrapper powerStatsHALWrapper) {
        super(Looper.getMainLooper());
        super(Looper.getMainLooper());
        mPowerStatsHALWrapper = powerStatsHALWrapper;
        mPowerStatsHALWrapper = powerStatsHALWrapper;
        mPowerStatsMeterStorage = new PowerStatsDataStorage(context, dataStoragePath,
        mDataStoragePath = dataStoragePath;

        mPowerStatsMeterStorage = new PowerStatsDataStorage(context, mDataStoragePath,
            meterFilename);
            meterFilename);
        mPowerStatsModelStorage = new PowerStatsDataStorage(context, dataStoragePath,
        mPowerStatsModelStorage = new PowerStatsDataStorage(context, mDataStoragePath,
            modelFilename);
            modelFilename);
        mPowerStatsResidencyStorage = new PowerStatsDataStorage(context, dataStoragePath,
        mPowerStatsResidencyStorage = new PowerStatsDataStorage(context, mDataStoragePath,
            residencyFilename);
            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 Original line 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 MODEL_FILENAME = "log.powerstats.model." + DATA_STORAGE_VERSION;
    private static final String RESIDENCY_FILENAME =
    private static final String RESIDENCY_FILENAME =
            "log.powerstats.residency." + DATA_STORAGE_VERSION;
            "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 final Injector mInjector;
    private File mDataStoragePath;


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


        String createMeterCacheFilename() {
            return METER_CACHE_FILENAME;
        }

        String createModelCacheFilename() {
            return MODEL_CACHE_FILENAME;
        }

        String createResidencyCacheFilename() {
            return RESIDENCY_CACHE_FILENAME;
        }

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


        PowerStatsLogger createPowerStatsLogger(Context context, File dataStoragePath,
        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) {
                IPowerStatsHALWrapper powerStatsHALWrapper) {
            return new PowerStatsLogger(context, dataStoragePath, meterFilename,
            return new PowerStatsLogger(context, dataStoragePath,
                modelFilename, residencyFilename, powerStatsHALWrapper);
                meterFilename, meterCacheFilename,
                modelFilename, modelCacheFilename,
                residencyFilename, residencyCacheFilename,
                powerStatsHALWrapper);
        }
        }


        BatteryTrigger createBatteryTrigger(Context context, PowerStatsLogger powerStatsLogger) {
        BatteryTrigger createBatteryTrigger(Context context, PowerStatsLogger powerStatsLogger) {
@@ -187,14 +208,31 @@ public class PowerStatsService extends SystemService {
        mPullAtomCallback = mInjector.createStatsPullerImpl(mContext, mPowerStatsInternal);
        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() {
    private void onBootCompleted() {
        if (getPowerStatsHal().isInitialized()) {
        if (getPowerStatsHal().isInitialized()) {
            if (DEBUG) Slog.d(TAG, "Starting PowerStatsService loggers");
            if (DEBUG) Slog.d(TAG, "Starting PowerStatsService loggers");
            mDataStoragePath = mInjector.createDataStoragePath();


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


    static class PowerEntityUtils {
    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,
        public static void packProtoMessage(PowerEntity[] powerEntity,
                ProtoOutputStream pos) {
                ProtoOutputStream pos) {
            if (powerEntity == null) return;
            if (powerEntity == null) return;
@@ -260,6 +266,12 @@ public class ProtoStreamUtils {
    }
    }


    static class ChannelUtils {
    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) {
        public static void packProtoMessage(Channel[] channel, ProtoOutputStream pos) {
            if (channel == null) return;
            if (channel == null) return;


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


    static class EnergyConsumerUtils {
    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,
        public static void packProtoMessage(EnergyConsumer[] energyConsumer,
                ProtoOutputStream pos) {
                ProtoOutputStream pos) {
            if (energyConsumer == null) return;
            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) {
        public static void print(EnergyConsumer[] energyConsumer) {
            if (energyConsumer == null) return;
            if (energyConsumer == null) return;


+351 −12

File changed.

Preview size limit exceeded, changes collapsed.