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

Commit d3e41e42 authored by Ritesh Reddy's avatar Ritesh Reddy Committed by Android (Google) Code Review
Browse files

Merge "Enabled Backup/Restore of Package UsageStatistics"

parents 6c94f5bb 8a6ce2cb
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -19,6 +19,8 @@ package android.app.usage;
import android.content.ComponentName;
import android.content.res.Configuration;

import java.io.IOException;

/**
 * UsageStatsManager local system service interface.
 *
@@ -109,4 +111,9 @@ public abstract class UsageStatsManagerInternal {
        public abstract void onParoleStateChanged(boolean isParoleOn);
    }

    /*  Backup/Restore API */
    public abstract byte[] getBackupPayload(int user, String key);

    public abstract void applyRestoredPayload(int user, String key, byte[] payload);

}
+4 −2
Original line number Diff line number Diff line
@@ -47,6 +47,7 @@ public class SystemBackupAgent extends BackupAgentHelper {
    private static final String PREFERRED_HELPER = "preferred_activities";
    private static final String NOTIFICATION_HELPER = "notifications";
    private static final String PERMISSION_HELPER = "permissions";
    private static final String USAGE_STATS_HELPER = "usage_stats";

    // These paths must match what the WallpaperManagerService uses.  The leaf *_FILENAME
    // are also used in the full-backup file format, so must not change unless steps are
@@ -96,7 +97,7 @@ public class SystemBackupAgent extends BackupAgentHelper {
        addHelper(PREFERRED_HELPER, new PreferredActivityBackupHelper());
        addHelper(NOTIFICATION_HELPER, new NotificationBackupHelper(this));
        addHelper(PERMISSION_HELPER, new PermissionBackupHelper());

        addHelper(USAGE_STATS_HELPER, new UsageStatsBackupHelper(this));
        super.onBackup(oldState, data, newState);
    }

@@ -131,6 +132,7 @@ public class SystemBackupAgent extends BackupAgentHelper {
        addHelper(PREFERRED_HELPER, new PreferredActivityBackupHelper());
        addHelper(NOTIFICATION_HELPER, new NotificationBackupHelper(this));
        addHelper(PERMISSION_HELPER, new PermissionBackupHelper());
        addHelper(USAGE_STATS_HELPER, new UsageStatsBackupHelper(this));

        try {
            super.onRestore(data, appVersionCode, newState);
+70 −0
Original line number Diff line number Diff line
package com.android.server.backup;


import android.app.backup.BlobBackupHelper;
import android.app.usage.IUsageStatsManager;
import android.app.usage.UsageStatsManagerInternal;
import android.content.Context;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.util.Log;

import com.android.server.LocalServices;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;

public class UsageStatsBackupHelper extends BlobBackupHelper {
    static final String TAG = "UsgStatsBackupHelper";   // must be < 23 chars
    static final boolean DEBUG = false;

    // Current version of the blob schema
    static final int BLOB_VERSION = 1;

    // Key under which the payload blob is stored
    // same as UsageStatsBackupHelperAssistant.KEY_USAGE_STATS
    static final String KEY_USAGE_STATS = "usage_stats";

    public UsageStatsBackupHelper(Context context) {
        super(BLOB_VERSION, KEY_USAGE_STATS);
    }

    @Override
    protected byte[] getBackupPayload(String key) {
        if(KEY_USAGE_STATS.equals(key)) {
            UsageStatsManagerInternal localUsageStatsManager = LocalServices.getService(UsageStatsManagerInternal.class);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            DataOutputStream out  = new DataOutputStream(baos);
            try{
                out.writeInt(UserHandle.USER_SYSTEM);
                out.write(localUsageStatsManager.getBackupPayload(UserHandle.USER_SYSTEM, key));
            } catch (IOException ioe){
                if (DEBUG) Log.e(TAG, "Failed to backup Usage Stats", ioe);
                baos.reset();
            }
            return baos.toByteArray();
        }
        return null;
    }


    @Override
    protected void applyRestoredPayload(String key, byte[] payload)  {
        if (KEY_USAGE_STATS.equals(key)) {
            UsageStatsManagerInternal localUsageStatsManager = LocalServices.getService(UsageStatsManagerInternal.class);
            DataInputStream in = new DataInputStream(new ByteArrayInputStream(payload));
            try{
                int user = in.readInt();
                byte[] restoreData = new byte[payload.length - 4];
                in.read(restoreData, 0, payload.length-4);
                localUsageStatsManager.applyRestoredPayload(user, key, restoreData);
            } catch (IOException ioe){
                if (DEBUG) Log.e(TAG, "Failed to restore Usage Stats", ioe);
            }
        }
    }
}
+195 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.server.usage;

import android.app.usage.TimeSparseArray;
import android.app.usage.UsageStats;
import android.app.usage.UsageStatsManager;
import android.os.Build;
import android.util.AtomicFile;
@@ -25,6 +26,10 @@ import android.util.TimeUtils;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
@@ -39,6 +44,14 @@ import java.util.List;
class UsageStatsDatabase {
    private static final int CURRENT_VERSION = 3;

    // Current version of the backup schema
    static final int BACKUP_STATE_VERSION = 1;

    // Key under which the payload blob is stored
    // same as UsageStatsBackupHelper.KEY_USAGE_STATS
    static final String KEY_USAGE_STATS = "usage_stats";


    private static final String TAG = "UsageStatsDatabase";
    private static final boolean DEBUG = UsageStatsService.DEBUG;
    private static final String BAK_SUFFIX = ".bak";
@@ -539,4 +552,186 @@ class UsageStatsDatabase {
            stats.lastTimeSaved = f.getLastModifiedTime();
        }
    }


    /* Backup/Restore Code */
    protected byte[] getBackupPayload(String key){
        synchronized (mLock) {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            if (KEY_USAGE_STATS.equals(key)) {
                prune(System.currentTimeMillis());
                DataOutputStream out = new DataOutputStream(baos);
                try {
                    out.writeInt(BACKUP_STATE_VERSION);

                    out.writeInt(mSortedStatFiles[UsageStatsManager.INTERVAL_DAILY].size());
                    for(int i = 0; i<mSortedStatFiles[UsageStatsManager.INTERVAL_DAILY].size(); i++){
                        writeIntervalStatsToStream(out, mSortedStatFiles[UsageStatsManager.INTERVAL_DAILY].valueAt(i));
                    }

                    out.writeInt(mSortedStatFiles[UsageStatsManager.INTERVAL_WEEKLY].size());
                    for(int i = 0; i<mSortedStatFiles[UsageStatsManager.INTERVAL_WEEKLY].size(); i++){
                        writeIntervalStatsToStream(out, mSortedStatFiles[UsageStatsManager.INTERVAL_WEEKLY].valueAt(i));
                    }

                    out.writeInt(mSortedStatFiles[UsageStatsManager.INTERVAL_MONTHLY].size());
                    for(int i = 0; i<mSortedStatFiles[UsageStatsManager.INTERVAL_MONTHLY].size(); i++){
                        writeIntervalStatsToStream(out, mSortedStatFiles[UsageStatsManager.INTERVAL_MONTHLY].valueAt(i));
                    }

                    out.writeInt(mSortedStatFiles[UsageStatsManager.INTERVAL_YEARLY].size());
                    for(int i = 0; i<mSortedStatFiles[UsageStatsManager.INTERVAL_YEARLY].size(); i++){
                        writeIntervalStatsToStream(out, mSortedStatFiles[UsageStatsManager.INTERVAL_YEARLY].valueAt(i));
                    }
                    if (DEBUG) Slog.i(TAG, "Written " + baos.size() + " bytes of data");
                } catch (IOException ioe){
                    Slog.d(TAG, "Failed to write data to output stream", ioe);
                    baos.reset();
                }
            }
            return baos.toByteArray();
        }

    }

    protected void applyRestoredPayload(String key, byte[] payload){
        synchronized (mLock) {
            if (KEY_USAGE_STATS.equals(key)) {
                // Read stats files for the current device configs
                IntervalStats dailyConfigSource = getLatestUsageStats(UsageStatsManager.INTERVAL_DAILY);
                IntervalStats weeklyConfigSource = getLatestUsageStats(UsageStatsManager.INTERVAL_WEEKLY);
                IntervalStats monthlyConfigSource = getLatestUsageStats(UsageStatsManager.INTERVAL_MONTHLY);
                IntervalStats yearlyConfigSource = getLatestUsageStats(UsageStatsManager.INTERVAL_YEARLY);

                // Delete all stats files
                for(int i = 0; i<mIntervalDirs.length; i++){
                    deleteDirectoryContents(mIntervalDirs[i]);
                }
                try {
                    DataInputStream in = new DataInputStream(new ByteArrayInputStream(payload));
                    int stateVersion = in.readInt();

                    int fileCount = in.readInt();
                    for(int i = 0; i<fileCount; i++){
                        IntervalStats stats = deserializeIntervalStats(getIntervalStatsBytes(in));
                        stats = mergeStats(stats, dailyConfigSource);
                        putUsageStats(UsageStatsManager.INTERVAL_DAILY, stats);
                    }

                    fileCount = in.readInt();
                    for(int i = 0; i<fileCount; i++){
                        IntervalStats stats = deserializeIntervalStats(getIntervalStatsBytes(in));
                        stats = mergeStats(stats, weeklyConfigSource);
                        putUsageStats(UsageStatsManager.INTERVAL_WEEKLY, stats);
                    }

                    fileCount = in.readInt();
                    for(int i = 0; i<fileCount; i++){
                        IntervalStats stats = deserializeIntervalStats(getIntervalStatsBytes(in));
                        stats = mergeStats(stats, monthlyConfigSource);
                        putUsageStats(UsageStatsManager.INTERVAL_MONTHLY, stats);
                    }

                    fileCount = in.readInt();
                    for(int i = 0; i<fileCount; i++){
                        IntervalStats stats = deserializeIntervalStats(getIntervalStatsBytes(in));
                        stats = mergeStats(stats, yearlyConfigSource);
                        putUsageStats(UsageStatsManager.INTERVAL_YEARLY, stats);
                    }
                    if (DEBUG) Slog.i(TAG, "Completed Restoring UsageStats");
                } catch (IOException ioe){
                    Slog.d(TAG, "Failed to read data from input stream", ioe);
                }
                finally {
                    indexFilesLocked();
                }
            }
        }
    }

    /**
     * Get the Configuration Statistics from the current device statistics and merge them
     * with the backed up usage statistics.
     */
    private IntervalStats mergeStats(IntervalStats beingRestored, IntervalStats onDevice) {
        beingRestored.activeConfiguration = onDevice.activeConfiguration;
        beingRestored.configurations.putAll(onDevice.configurations);
        beingRestored.events = onDevice.events;
        return beingRestored;
    }

    private void writeIntervalStatsToStream(DataOutputStream out, AtomicFile statsFile) throws IOException{
        IntervalStats stats = new IntervalStats();
        try {
            UsageStatsXml.read(statsFile, stats);
        } catch (IOException e) {
            Slog.e(TAG, "Failed to read usage stats file", e);
            out.writeInt(0);
            return;
        }
        sanitizeIntervalStatsForBackup(stats);
        byte[] data = serializeIntervalStats(stats);
        out.writeInt(data.length);
        out.write(data);
    }

    private static byte[] getIntervalStatsBytes(DataInputStream in) throws IOException {
        int length = in.readInt();
        byte[] buffer = new byte[length];
        in.read(buffer, 0, length);
        return buffer;
    }

    private static void sanitizeIntervalStatsForBackup(IntervalStats stats) {
        if (stats == null) return;
        stats.activeConfiguration = null;
        stats.configurations.clear();
        if (stats.events != null) stats.events.clear();
    }

    private static byte[] serializeIntervalStats(IntervalStats stats) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        DataOutputStream out = new DataOutputStream(baos);
        try {
            out.writeLong(stats.beginTime);
            UsageStatsXml.write(out, stats);
        } catch (IOException ioe) {
            Slog.d(TAG, "Serializing IntervalStats Failed", ioe);
            baos.reset();
        }
        return baos.toByteArray();
    }

    private static IntervalStats deserializeIntervalStats(byte[] data) {
        ByteArrayInputStream bais = new ByteArrayInputStream(data);
        DataInputStream in = new DataInputStream(bais);
        IntervalStats stats = new IntervalStats();
        try {
            stats.beginTime = in.readLong();
            UsageStatsXml.read(in, stats);
        } catch (IOException ioe) {
            Slog.d(TAG, "DeSerializing IntervalStats Failed", ioe);
            stats = null;
        }
        return stats;
    }

    private static void deleteDirectoryContents(File directory){
        File[] files = directory.listFiles();
        for (File file : files) {
            deleteDirectory(file);
        }
    }

    private static void deleteDirectory(File directory) {
        File[] files = directory.listFiles();
        for (File file : files) {
            if (!file.isDirectory()) {
                file.delete();
            } else {
                deleteDirectory(file);
            }
        }
        directory.delete();
    }
}
+23 −0
Original line number Diff line number Diff line
@@ -1310,6 +1310,8 @@ public class UsageStatsService extends SystemService implements
            }
            UsageStatsService.this.dump(args, pw);
        }


    }

    /**
@@ -1416,5 +1418,26 @@ public class UsageStatsService extends SystemService implements
                AppIdleStateChangeListener listener) {
            UsageStatsService.this.removeListener(listener);
        }

        @Override
        public byte[] getBackupPayload(int user, String key) {
            // Check to ensure that only user 0's data is b/r for now
            if (user == UserHandle.USER_SYSTEM) {
                final UserUsageStatsService userStats =
                        getUserDataAndInitializeIfNeededLocked(user, checkAndGetTimeLocked());
                return userStats.getBackupPayload(key);
            } else {
                return null;
            }
        }

        @Override
        public void applyRestoredPayload(int user, String key, byte[] payload) {
            if (user == UserHandle.USER_SYSTEM) {
                final UserUsageStatsService userStats =
                        getUserDataAndInitializeIfNeededLocked(user, checkAndGetTimeLocked());
                userStats.applyRestoredPayload(key, payload);
            }
        }
    }
}
Loading