10debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski/**
20debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski * Copyright (C) 2014 The Android Open Source Project
30debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski *
40debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski * Licensed under the Apache License, Version 2.0 (the "License"); you may not
50debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski * use this file except in compliance with the License. You may obtain a copy
60debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski * of the License at
70debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski *
80debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski * http://www.apache.org/licenses/LICENSE-2.0
90debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski *
100debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski * Unless required by applicable law or agreed to in writing, software
110debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
120debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
130debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski * License for the specific language governing permissions and limitations
140debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski * under the License.
150debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski */
160debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski
170debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinskipackage com.android.server.usage;
180debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski
190debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinskiimport android.app.usage.TimeSparseArray;
200debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinskiimport android.app.usage.UsageStatsManager;
2155717a613c23fc6b05cab86c3b0de283a027e9f6Amith Yamasaniimport android.os.Build;
220debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinskiimport android.util.AtomicFile;
230debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinskiimport android.util.Slog;
242dfb91412c27581d27b7eba172912d2893d69abeAdam Lesinskiimport android.util.TimeUtils;
250debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski
26d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinskiimport java.io.BufferedReader;
27d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinskiimport java.io.BufferedWriter;
288a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddyimport java.io.ByteArrayInputStream;
298a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddyimport java.io.ByteArrayOutputStream;
308a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddyimport java.io.DataInputStream;
318a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddyimport java.io.DataOutputStream;
320debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinskiimport java.io.File;
33d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinskiimport java.io.FileReader;
34d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinskiimport java.io.FileWriter;
350debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinskiimport java.io.FilenameFilter;
360debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinskiimport java.io.IOException;
370debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinskiimport java.util.ArrayList;
383516800b611a79339a3c188332d13a26e9086b09Adam Lesinskiimport java.util.List;
390debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski
403516800b611a79339a3c188332d13a26e9086b09Adam Lesinski/**
413516800b611a79339a3c188332d13a26e9086b09Adam Lesinski * Provides an interface to query for UsageStat data from an XML database.
423516800b611a79339a3c188332d13a26e9086b09Adam Lesinski */
430debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinskiclass UsageStatsDatabase {
4455717a613c23fc6b05cab86c3b0de283a027e9f6Amith Yamasani    private static final int CURRENT_VERSION = 3;
45d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski
468a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy    // Current version of the backup schema
47e35ae5cce1bcc1841b433ed1c5bef0a5c911cf15Ritesh Reddy    static final int BACKUP_VERSION = 1;
488a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy
498a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy    // Key under which the payload blob is stored
508a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy    // same as UsageStatsBackupHelper.KEY_USAGE_STATS
518a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy    static final String KEY_USAGE_STATS = "usage_stats";
528a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy
538a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy
540debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski    private static final String TAG = "UsageStatsDatabase";
550debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski    private static final boolean DEBUG = UsageStatsService.DEBUG;
561bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski    private static final String BAK_SUFFIX = ".bak";
570fb25fae8f664f7c9f4cb7e29b03824a16becd5cAdam Lesinski    private static final String CHECKED_IN_SUFFIX = UsageStatsXml.CHECKED_IN_SUFFIX;
580debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski
590debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski    private final Object mLock = new Object();
603516800b611a79339a3c188332d13a26e9086b09Adam Lesinski    private final File[] mIntervalDirs;
610debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski    private final TimeSparseArray<AtomicFile>[] mSortedStatFiles;
62d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski    private final UnixCalendar mCal;
63d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski    private final File mVersionFile;
6455717a613c23fc6b05cab86c3b0de283a027e9f6Amith Yamasani    private boolean mFirstUpdate;
6555717a613c23fc6b05cab86c3b0de283a027e9f6Amith Yamasani    private boolean mNewUpdate;
660debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski
670debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski    public UsageStatsDatabase(File dir) {
683516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        mIntervalDirs = new File[] {
690debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski                new File(dir, "daily"),
700debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski                new File(dir, "weekly"),
710debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski                new File(dir, "monthly"),
720debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski                new File(dir, "yearly"),
730debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski        };
74d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski        mVersionFile = new File(dir, "version");
753516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        mSortedStatFiles = new TimeSparseArray[mIntervalDirs.length];
76d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski        mCal = new UnixCalendar(0);
770debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski    }
780debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski
793516800b611a79339a3c188332d13a26e9086b09Adam Lesinski    /**
803516800b611a79339a3c188332d13a26e9086b09Adam Lesinski     * Initialize any directories required and index what stats are available.
813516800b611a79339a3c188332d13a26e9086b09Adam Lesinski     */
8266143fa5b34eea7413335111838fb692987b611aAdam Lesinski    public void init(long currentTimeMillis) {
830debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski        synchronized (mLock) {
843516800b611a79339a3c188332d13a26e9086b09Adam Lesinski            for (File f : mIntervalDirs) {
850debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski                f.mkdirs();
860debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski                if (!f.exists()) {
870debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski                    throw new IllegalStateException("Failed to create directory "
880debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski                            + f.getAbsolutePath());
890debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski                }
900debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski            }
910debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski
9255717a613c23fc6b05cab86c3b0de283a027e9f6Amith Yamasani            checkVersionAndBuildLocked();
9366143fa5b34eea7413335111838fb692987b611aAdam Lesinski            indexFilesLocked();
94d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski
9566143fa5b34eea7413335111838fb692987b611aAdam Lesinski            // Delete files that are in the future.
9666143fa5b34eea7413335111838fb692987b611aAdam Lesinski            for (TimeSparseArray<AtomicFile> files : mSortedStatFiles) {
9766143fa5b34eea7413335111838fb692987b611aAdam Lesinski                final int startIndex = files.closestIndexOnOrAfter(currentTimeMillis);
9866143fa5b34eea7413335111838fb692987b611aAdam Lesinski                if (startIndex < 0) {
9966143fa5b34eea7413335111838fb692987b611aAdam Lesinski                    continue;
1000debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski                }
1010debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski
10266143fa5b34eea7413335111838fb692987b611aAdam Lesinski                final int fileCount = files.size();
10366143fa5b34eea7413335111838fb692987b611aAdam Lesinski                for (int i = startIndex; i < fileCount; i++) {
10466143fa5b34eea7413335111838fb692987b611aAdam Lesinski                    files.valueAt(i).delete();
10566143fa5b34eea7413335111838fb692987b611aAdam Lesinski                }
10666143fa5b34eea7413335111838fb692987b611aAdam Lesinski
10766143fa5b34eea7413335111838fb692987b611aAdam Lesinski                // Remove in a separate loop because any accesses (valueAt)
10866143fa5b34eea7413335111838fb692987b611aAdam Lesinski                // will cause a gc in the SparseArray and mess up the order.
10966143fa5b34eea7413335111838fb692987b611aAdam Lesinski                for (int i = startIndex; i < fileCount; i++) {
11066143fa5b34eea7413335111838fb692987b611aAdam Lesinski                    files.removeAt(i);
11166143fa5b34eea7413335111838fb692987b611aAdam Lesinski                }
11266143fa5b34eea7413335111838fb692987b611aAdam Lesinski            }
11366143fa5b34eea7413335111838fb692987b611aAdam Lesinski        }
11466143fa5b34eea7413335111838fb692987b611aAdam Lesinski    }
11566143fa5b34eea7413335111838fb692987b611aAdam Lesinski
1161bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski    public interface CheckinAction {
1171bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski        boolean checkin(IntervalStats stats);
1181bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski    }
1191bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski
1201bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski    /**
1211bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski     * Calls {@link CheckinAction#checkin(IntervalStats)} on the given {@link CheckinAction}
1221bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski     * for all {@link IntervalStats} that haven't been checked-in.
1231bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski     * If any of the calls to {@link CheckinAction#checkin(IntervalStats)} returns false or throws
1241bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski     * an exception, the check-in will be aborted.
1251bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski     *
1261bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski     * @param checkinAction The callback to run when checking-in {@link IntervalStats}.
1271bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski     * @return true if the check-in succeeded.
1281bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski     */
1291bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski    public boolean checkinDailyFiles(CheckinAction checkinAction) {
1301bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski        synchronized (mLock) {
1311bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski            final TimeSparseArray<AtomicFile> files =
1321bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski                    mSortedStatFiles[UsageStatsManager.INTERVAL_DAILY];
1331bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski            final int fileCount = files.size();
1340fb25fae8f664f7c9f4cb7e29b03824a16becd5cAdam Lesinski
1350fb25fae8f664f7c9f4cb7e29b03824a16becd5cAdam Lesinski            // We may have holes in the checkin (if there was an error)
1360fb25fae8f664f7c9f4cb7e29b03824a16becd5cAdam Lesinski            // so find the last checked-in file and go from there.
1370fb25fae8f664f7c9f4cb7e29b03824a16becd5cAdam Lesinski            int lastCheckin = -1;
1380fb25fae8f664f7c9f4cb7e29b03824a16becd5cAdam Lesinski            for (int i = 0; i < fileCount - 1; i++) {
1390fb25fae8f664f7c9f4cb7e29b03824a16becd5cAdam Lesinski                if (files.valueAt(i).getBaseFile().getPath().endsWith(CHECKED_IN_SUFFIX)) {
1400fb25fae8f664f7c9f4cb7e29b03824a16becd5cAdam Lesinski                    lastCheckin = i;
1411bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski                }
1421bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski            }
1431bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski
1440fb25fae8f664f7c9f4cb7e29b03824a16becd5cAdam Lesinski            final int start = lastCheckin + 1;
1451bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski            if (start == fileCount - 1) {
1461bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski                return true;
1471bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski            }
1481bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski
1491bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski            try {
1501bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski                IntervalStats stats = new IntervalStats();
1511bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski                for (int i = start; i < fileCount - 1; i++) {
1521bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski                    UsageStatsXml.read(files.valueAt(i), stats);
1531bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski                    if (!checkinAction.checkin(stats)) {
1541bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski                        return false;
1551bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski                    }
1561bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski                }
1571bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski            } catch (IOException e) {
1581bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski                Slog.e(TAG, "Failed to check-in", e);
1591bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski                return false;
1601bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski            }
1611bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski
1621bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski            // We have successfully checked-in the stats, so rename the files so that they
1631bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski            // are marked as checked-in.
1641bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski            for (int i = start; i < fileCount - 1; i++) {
1651bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski                final AtomicFile file = files.valueAt(i);
1660fb25fae8f664f7c9f4cb7e29b03824a16becd5cAdam Lesinski                final File checkedInFile = new File(
1670fb25fae8f664f7c9f4cb7e29b03824a16becd5cAdam Lesinski                        file.getBaseFile().getPath() + CHECKED_IN_SUFFIX);
1681bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski                if (!file.getBaseFile().renameTo(checkedInFile)) {
1691bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski                    // We must return success, as we've already marked some files as checked-in.
1701bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski                    // It's better to repeat ourselves than to lose data.
1711bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski                    Slog.e(TAG, "Failed to mark file " + file.getBaseFile().getPath()
1721bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski                            + " as checked-in");
1731bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski                    return true;
1741bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski                }
1750fb25fae8f664f7c9f4cb7e29b03824a16becd5cAdam Lesinski
1760fb25fae8f664f7c9f4cb7e29b03824a16becd5cAdam Lesinski                // AtomicFile needs to set a new backup path with the same -c extension, so
1770fb25fae8f664f7c9f4cb7e29b03824a16becd5cAdam Lesinski                // we replace the old AtomicFile with the updated one.
1780fb25fae8f664f7c9f4cb7e29b03824a16becd5cAdam Lesinski                files.setValueAt(i, new AtomicFile(checkedInFile));
1791bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski            }
1801bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski        }
1811bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski        return true;
1821bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski    }
1831bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski
18466143fa5b34eea7413335111838fb692987b611aAdam Lesinski    private void indexFilesLocked() {
18566143fa5b34eea7413335111838fb692987b611aAdam Lesinski        final FilenameFilter backupFileFilter = new FilenameFilter() {
18666143fa5b34eea7413335111838fb692987b611aAdam Lesinski            @Override
18766143fa5b34eea7413335111838fb692987b611aAdam Lesinski            public boolean accept(File dir, String name) {
1881bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski                return !name.endsWith(BAK_SUFFIX);
18966143fa5b34eea7413335111838fb692987b611aAdam Lesinski            }
19066143fa5b34eea7413335111838fb692987b611aAdam Lesinski        };
19166143fa5b34eea7413335111838fb692987b611aAdam Lesinski
19266143fa5b34eea7413335111838fb692987b611aAdam Lesinski        // Index the available usage stat files on disk.
19366143fa5b34eea7413335111838fb692987b611aAdam Lesinski        for (int i = 0; i < mSortedStatFiles.length; i++) {
19466143fa5b34eea7413335111838fb692987b611aAdam Lesinski            if (mSortedStatFiles[i] == null) {
1950debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski                mSortedStatFiles[i] = new TimeSparseArray<>();
19666143fa5b34eea7413335111838fb692987b611aAdam Lesinski            } else {
19766143fa5b34eea7413335111838fb692987b611aAdam Lesinski                mSortedStatFiles[i].clear();
19866143fa5b34eea7413335111838fb692987b611aAdam Lesinski            }
19966143fa5b34eea7413335111838fb692987b611aAdam Lesinski            File[] files = mIntervalDirs[i].listFiles(backupFileFilter);
20066143fa5b34eea7413335111838fb692987b611aAdam Lesinski            if (files != null) {
20166143fa5b34eea7413335111838fb692987b611aAdam Lesinski                if (DEBUG) {
20266143fa5b34eea7413335111838fb692987b611aAdam Lesinski                    Slog.d(TAG, "Found " + files.length + " stat files for interval " + i);
20366143fa5b34eea7413335111838fb692987b611aAdam Lesinski                }
2040debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski
20566143fa5b34eea7413335111838fb692987b611aAdam Lesinski                for (File f : files) {
20666143fa5b34eea7413335111838fb692987b611aAdam Lesinski                    final AtomicFile af = new AtomicFile(f);
207e03f17c82eac7b6ce55e434fab29c7c428b46baaAdam Lesinski                    try {
208e03f17c82eac7b6ce55e434fab29c7c428b46baaAdam Lesinski                        mSortedStatFiles[i].put(UsageStatsXml.parseBeginTime(af), af);
209e03f17c82eac7b6ce55e434fab29c7c428b46baaAdam Lesinski                    } catch (IOException e) {
210e03f17c82eac7b6ce55e434fab29c7c428b46baaAdam Lesinski                        Slog.e(TAG, "failed to index file: " + f, e);
211e03f17c82eac7b6ce55e434fab29c7c428b46baaAdam Lesinski                    }
2120debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski                }
2130debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski            }
2140debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski        }
2150debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski    }
2160debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski
21755717a613c23fc6b05cab86c3b0de283a027e9f6Amith Yamasani    /**
21855717a613c23fc6b05cab86c3b0de283a027e9f6Amith Yamasani     * Is this the first update to the system from L to M?
21955717a613c23fc6b05cab86c3b0de283a027e9f6Amith Yamasani     */
22055717a613c23fc6b05cab86c3b0de283a027e9f6Amith Yamasani    boolean isFirstUpdate() {
22155717a613c23fc6b05cab86c3b0de283a027e9f6Amith Yamasani        return mFirstUpdate;
22255717a613c23fc6b05cab86c3b0de283a027e9f6Amith Yamasani    }
22355717a613c23fc6b05cab86c3b0de283a027e9f6Amith Yamasani
22455717a613c23fc6b05cab86c3b0de283a027e9f6Amith Yamasani    /**
22555717a613c23fc6b05cab86c3b0de283a027e9f6Amith Yamasani     * Is this a system update since we started tracking build fingerprint in the version file?
22655717a613c23fc6b05cab86c3b0de283a027e9f6Amith Yamasani     */
22755717a613c23fc6b05cab86c3b0de283a027e9f6Amith Yamasani    boolean isNewUpdate() {
22855717a613c23fc6b05cab86c3b0de283a027e9f6Amith Yamasani        return mNewUpdate;
22955717a613c23fc6b05cab86c3b0de283a027e9f6Amith Yamasani    }
23055717a613c23fc6b05cab86c3b0de283a027e9f6Amith Yamasani
23155717a613c23fc6b05cab86c3b0de283a027e9f6Amith Yamasani    private void checkVersionAndBuildLocked() {
232d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski        int version;
23355717a613c23fc6b05cab86c3b0de283a027e9f6Amith Yamasani        String buildFingerprint;
23455717a613c23fc6b05cab86c3b0de283a027e9f6Amith Yamasani        String currentFingerprint = getBuildFingerprint();
23555717a613c23fc6b05cab86c3b0de283a027e9f6Amith Yamasani        mFirstUpdate = true;
23655717a613c23fc6b05cab86c3b0de283a027e9f6Amith Yamasani        mNewUpdate = true;
237d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski        try (BufferedReader reader = new BufferedReader(new FileReader(mVersionFile))) {
238d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski            version = Integer.parseInt(reader.readLine());
23955717a613c23fc6b05cab86c3b0de283a027e9f6Amith Yamasani            buildFingerprint = reader.readLine();
24055717a613c23fc6b05cab86c3b0de283a027e9f6Amith Yamasani            if (buildFingerprint != null) {
24155717a613c23fc6b05cab86c3b0de283a027e9f6Amith Yamasani                mFirstUpdate = false;
24255717a613c23fc6b05cab86c3b0de283a027e9f6Amith Yamasani            }
24355717a613c23fc6b05cab86c3b0de283a027e9f6Amith Yamasani            if (currentFingerprint.equals(buildFingerprint)) {
24455717a613c23fc6b05cab86c3b0de283a027e9f6Amith Yamasani                mNewUpdate = false;
24555717a613c23fc6b05cab86c3b0de283a027e9f6Amith Yamasani            }
246d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski        } catch (NumberFormatException | IOException e) {
247d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski            version = 0;
248d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski        }
249d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski
250d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski        if (version != CURRENT_VERSION) {
251d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski            Slog.i(TAG, "Upgrading from version " + version + " to " + CURRENT_VERSION);
25237a46b48dcb7e34ee3669cfb2ed78af08bfca3c7Adam Lesinski            doUpgradeLocked(version);
25355717a613c23fc6b05cab86c3b0de283a027e9f6Amith Yamasani        }
254d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski
25555717a613c23fc6b05cab86c3b0de283a027e9f6Amith Yamasani        if (version != CURRENT_VERSION || mNewUpdate) {
256d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski            try (BufferedWriter writer = new BufferedWriter(new FileWriter(mVersionFile))) {
257d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                writer.write(Integer.toString(CURRENT_VERSION));
25855717a613c23fc6b05cab86c3b0de283a027e9f6Amith Yamasani                writer.write("\n");
25955717a613c23fc6b05cab86c3b0de283a027e9f6Amith Yamasani                writer.write(currentFingerprint);
26055717a613c23fc6b05cab86c3b0de283a027e9f6Amith Yamasani                writer.write("\n");
26155717a613c23fc6b05cab86c3b0de283a027e9f6Amith Yamasani                writer.flush();
262d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski            } catch (IOException e) {
263d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                Slog.e(TAG, "Failed to write new version");
264d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                throw new RuntimeException(e);
265d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski            }
266d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski        }
267d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski    }
268d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski
26955717a613c23fc6b05cab86c3b0de283a027e9f6Amith Yamasani    private String getBuildFingerprint() {
27055717a613c23fc6b05cab86c3b0de283a027e9f6Amith Yamasani        return Build.VERSION.RELEASE + ";"
27155717a613c23fc6b05cab86c3b0de283a027e9f6Amith Yamasani                + Build.VERSION.CODENAME + ";"
27255717a613c23fc6b05cab86c3b0de283a027e9f6Amith Yamasani                + Build.VERSION.INCREMENTAL;
27355717a613c23fc6b05cab86c3b0de283a027e9f6Amith Yamasani    }
27455717a613c23fc6b05cab86c3b0de283a027e9f6Amith Yamasani
27537a46b48dcb7e34ee3669cfb2ed78af08bfca3c7Adam Lesinski    private void doUpgradeLocked(int thisVersion) {
27637a46b48dcb7e34ee3669cfb2ed78af08bfca3c7Adam Lesinski        if (thisVersion < 2) {
277d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski            // Delete all files if we are version 0. This is a pre-release version,
278d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski            // so this is fine.
279d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski            Slog.i(TAG, "Deleting all usage stats files");
280d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski            for (int i = 0; i < mIntervalDirs.length; i++) {
281d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                File[] files = mIntervalDirs[i].listFiles();
282d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                if (files != null) {
283d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                    for (File f : files) {
284d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                        f.delete();
285d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                    }
286d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                }
287d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski            }
288d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski        }
289d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski    }
290d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski
29166143fa5b34eea7413335111838fb692987b611aAdam Lesinski    public void onTimeChanged(long timeDiffMillis) {
29266143fa5b34eea7413335111838fb692987b611aAdam Lesinski        synchronized (mLock) {
2932dfb91412c27581d27b7eba172912d2893d69abeAdam Lesinski            StringBuilder logBuilder = new StringBuilder();
2942dfb91412c27581d27b7eba172912d2893d69abeAdam Lesinski            logBuilder.append("Time changed by ");
2952dfb91412c27581d27b7eba172912d2893d69abeAdam Lesinski            TimeUtils.formatDuration(timeDiffMillis, logBuilder);
2962dfb91412c27581d27b7eba172912d2893d69abeAdam Lesinski            logBuilder.append(".");
2972dfb91412c27581d27b7eba172912d2893d69abeAdam Lesinski
2982dfb91412c27581d27b7eba172912d2893d69abeAdam Lesinski            int filesDeleted = 0;
2992dfb91412c27581d27b7eba172912d2893d69abeAdam Lesinski            int filesMoved = 0;
3002dfb91412c27581d27b7eba172912d2893d69abeAdam Lesinski
30166143fa5b34eea7413335111838fb692987b611aAdam Lesinski            for (TimeSparseArray<AtomicFile> files : mSortedStatFiles) {
30266143fa5b34eea7413335111838fb692987b611aAdam Lesinski                final int fileCount = files.size();
30366143fa5b34eea7413335111838fb692987b611aAdam Lesinski                for (int i = 0; i < fileCount; i++) {
30466143fa5b34eea7413335111838fb692987b611aAdam Lesinski                    final AtomicFile file = files.valueAt(i);
30566143fa5b34eea7413335111838fb692987b611aAdam Lesinski                    final long newTime = files.keyAt(i) + timeDiffMillis;
30666143fa5b34eea7413335111838fb692987b611aAdam Lesinski                    if (newTime < 0) {
3072dfb91412c27581d27b7eba172912d2893d69abeAdam Lesinski                        filesDeleted++;
30866143fa5b34eea7413335111838fb692987b611aAdam Lesinski                        file.delete();
30966143fa5b34eea7413335111838fb692987b611aAdam Lesinski                    } else {
31066143fa5b34eea7413335111838fb692987b611aAdam Lesinski                        try {
31166143fa5b34eea7413335111838fb692987b611aAdam Lesinski                            file.openRead().close();
31266143fa5b34eea7413335111838fb692987b611aAdam Lesinski                        } catch (IOException e) {
31366143fa5b34eea7413335111838fb692987b611aAdam Lesinski                            // Ignore, this is just to make sure there are no backups.
31466143fa5b34eea7413335111838fb692987b611aAdam Lesinski                        }
3150fb25fae8f664f7c9f4cb7e29b03824a16becd5cAdam Lesinski
3160fb25fae8f664f7c9f4cb7e29b03824a16becd5cAdam Lesinski                        String newName = Long.toString(newTime);
3170fb25fae8f664f7c9f4cb7e29b03824a16becd5cAdam Lesinski                        if (file.getBaseFile().getName().endsWith(CHECKED_IN_SUFFIX)) {
3180fb25fae8f664f7c9f4cb7e29b03824a16becd5cAdam Lesinski                            newName = newName + CHECKED_IN_SUFFIX;
3190fb25fae8f664f7c9f4cb7e29b03824a16becd5cAdam Lesinski                        }
3200fb25fae8f664f7c9f4cb7e29b03824a16becd5cAdam Lesinski
3210fb25fae8f664f7c9f4cb7e29b03824a16becd5cAdam Lesinski                        final File newFile = new File(file.getBaseFile().getParentFile(), newName);
3222dfb91412c27581d27b7eba172912d2893d69abeAdam Lesinski                        filesMoved++;
32366143fa5b34eea7413335111838fb692987b611aAdam Lesinski                        file.getBaseFile().renameTo(newFile);
32466143fa5b34eea7413335111838fb692987b611aAdam Lesinski                    }
32566143fa5b34eea7413335111838fb692987b611aAdam Lesinski                }
32666143fa5b34eea7413335111838fb692987b611aAdam Lesinski                files.clear();
32766143fa5b34eea7413335111838fb692987b611aAdam Lesinski            }
32866143fa5b34eea7413335111838fb692987b611aAdam Lesinski
3292dfb91412c27581d27b7eba172912d2893d69abeAdam Lesinski            logBuilder.append(" files deleted: ").append(filesDeleted);
3302dfb91412c27581d27b7eba172912d2893d69abeAdam Lesinski            logBuilder.append(" files moved: ").append(filesMoved);
3312dfb91412c27581d27b7eba172912d2893d69abeAdam Lesinski            Slog.i(TAG, logBuilder.toString());
3322dfb91412c27581d27b7eba172912d2893d69abeAdam Lesinski
33366143fa5b34eea7413335111838fb692987b611aAdam Lesinski            // Now re-index the new files.
33466143fa5b34eea7413335111838fb692987b611aAdam Lesinski            indexFilesLocked();
33566143fa5b34eea7413335111838fb692987b611aAdam Lesinski        }
33666143fa5b34eea7413335111838fb692987b611aAdam Lesinski    }
33766143fa5b34eea7413335111838fb692987b611aAdam Lesinski
3383516800b611a79339a3c188332d13a26e9086b09Adam Lesinski    /**
3393516800b611a79339a3c188332d13a26e9086b09Adam Lesinski     * Get the latest stats that exist for this interval type.
3403516800b611a79339a3c188332d13a26e9086b09Adam Lesinski     */
3413516800b611a79339a3c188332d13a26e9086b09Adam Lesinski    public IntervalStats getLatestUsageStats(int intervalType) {
3420debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski        synchronized (mLock) {
3433516800b611a79339a3c188332d13a26e9086b09Adam Lesinski            if (intervalType < 0 || intervalType >= mIntervalDirs.length) {
3443516800b611a79339a3c188332d13a26e9086b09Adam Lesinski                throw new IllegalArgumentException("Bad interval type " + intervalType);
3450debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski            }
3460debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski
3473516800b611a79339a3c188332d13a26e9086b09Adam Lesinski            final int fileCount = mSortedStatFiles[intervalType].size();
3480debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski            if (fileCount == 0) {
3490debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski                return null;
3500debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski            }
3510debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski
3520debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski            try {
3533516800b611a79339a3c188332d13a26e9086b09Adam Lesinski                final AtomicFile f = mSortedStatFiles[intervalType].valueAt(fileCount - 1);
3543516800b611a79339a3c188332d13a26e9086b09Adam Lesinski                IntervalStats stats = new IntervalStats();
3553516800b611a79339a3c188332d13a26e9086b09Adam Lesinski                UsageStatsXml.read(f, stats);
3560debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski                return stats;
3570debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski            } catch (IOException e) {
3580debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski                Slog.e(TAG, "Failed to read usage stats file", e);
3590debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski            }
3600debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski        }
3610debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski        return null;
3620debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski    }
3630debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski
3643516800b611a79339a3c188332d13a26e9086b09Adam Lesinski    /**
3657f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski     * Figures out what to extract from the given IntervalStats object.
3663516800b611a79339a3c188332d13a26e9086b09Adam Lesinski     */
3677f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski    interface StatCombiner<T> {
3687f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski
3697f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski        /**
3707f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski         * Implementations should extract interesting from <code>stats</code> and add it
3717f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski         * to the <code>accumulatedResult</code> list.
3727f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski         *
3737f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski         * If the <code>stats</code> object is mutable, <code>mutable</code> will be true,
3747f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski         * which means you should make a copy of the data before adding it to the
3757f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski         * <code>accumulatedResult</code> list.
3767f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski         *
3777f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski         * @param stats The {@link IntervalStats} object selected.
3787f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski         * @param mutable Whether or not the data inside the stats object is mutable.
3797f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski         * @param accumulatedResult The list to which to add extracted data.
3807f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski         */
3817f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski        void combine(IntervalStats stats, boolean mutable, List<T> accumulatedResult);
3827f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski    }
3837f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski
3847f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski    /**
3857f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski     * Find all {@link IntervalStats} for the given range and interval type.
3867f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski     */
3877f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski    public <T> List<T> queryUsageStats(int intervalType, long beginTime, long endTime,
3887f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski            StatCombiner<T> combiner) {
3893516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        synchronized (mLock) {
3903516800b611a79339a3c188332d13a26e9086b09Adam Lesinski            if (intervalType < 0 || intervalType >= mIntervalDirs.length) {
3913516800b611a79339a3c188332d13a26e9086b09Adam Lesinski                throw new IllegalArgumentException("Bad interval type " + intervalType);
3923516800b611a79339a3c188332d13a26e9086b09Adam Lesinski            }
3933516800b611a79339a3c188332d13a26e9086b09Adam Lesinski
394d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski            final TimeSparseArray<AtomicFile> intervalStats = mSortedStatFiles[intervalType];
395d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski
396d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski            if (endTime <= beginTime) {
397d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                if (DEBUG) {
398d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                    Slog.d(TAG, "endTime(" + endTime + ") <= beginTime(" + beginTime + ")");
399d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                }
4003516800b611a79339a3c188332d13a26e9086b09Adam Lesinski                return null;
4013516800b611a79339a3c188332d13a26e9086b09Adam Lesinski            }
4023516800b611a79339a3c188332d13a26e9086b09Adam Lesinski
403d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski            int startIndex = intervalStats.closestIndexOnOrBefore(beginTime);
4040debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski            if (startIndex < 0) {
405d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                // All the stats available have timestamps after beginTime, which means they all
406d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                // match.
407d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                startIndex = 0;
4083516800b611a79339a3c188332d13a26e9086b09Adam Lesinski            }
4093516800b611a79339a3c188332d13a26e9086b09Adam Lesinski
410d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski            int endIndex = intervalStats.closestIndexOnOrBefore(endTime);
4113516800b611a79339a3c188332d13a26e9086b09Adam Lesinski            if (endIndex < 0) {
412d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                // All the stats start after this range ends, so nothing matches.
413d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                if (DEBUG) {
414d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                    Slog.d(TAG, "No results for this range. All stats start after.");
415d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                }
416d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                return null;
417d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski            }
418d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski
419d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski            if (intervalStats.keyAt(endIndex) == endTime) {
420d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                // The endTime is exclusive, so if we matched exactly take the one before.
421d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                endIndex--;
422d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                if (endIndex < 0) {
423d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                    // All the stats start after this range ends, so nothing matches.
424d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                    if (DEBUG) {
425d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                        Slog.d(TAG, "No results for this range. All stats start after.");
426d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                    }
427d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                    return null;
428d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                }
4290debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski            }
4300debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski
4313a0831bfe6e69a2ad11b9f6fae04fb27daa40a88Adam Lesinski            final IntervalStats stats = new IntervalStats();
4323a0831bfe6e69a2ad11b9f6fae04fb27daa40a88Adam Lesinski            final ArrayList<T> results = new ArrayList<>();
4333a0831bfe6e69a2ad11b9f6fae04fb27daa40a88Adam Lesinski            for (int i = startIndex; i <= endIndex; i++) {
4343a0831bfe6e69a2ad11b9f6fae04fb27daa40a88Adam Lesinski                final AtomicFile f = intervalStats.valueAt(i);
4350debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski
4363a0831bfe6e69a2ad11b9f6fae04fb27daa40a88Adam Lesinski                if (DEBUG) {
4373a0831bfe6e69a2ad11b9f6fae04fb27daa40a88Adam Lesinski                    Slog.d(TAG, "Reading stat file " + f.getBaseFile().getAbsolutePath());
4383a0831bfe6e69a2ad11b9f6fae04fb27daa40a88Adam Lesinski                }
4390debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski
4403a0831bfe6e69a2ad11b9f6fae04fb27daa40a88Adam Lesinski                try {
4413516800b611a79339a3c188332d13a26e9086b09Adam Lesinski                    UsageStatsXml.read(f, stats);
4423516800b611a79339a3c188332d13a26e9086b09Adam Lesinski                    if (beginTime < stats.endTime) {
4437f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski                        combiner.combine(stats, false, results);
4440debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski                    }
4453a0831bfe6e69a2ad11b9f6fae04fb27daa40a88Adam Lesinski                } catch (IOException e) {
4463a0831bfe6e69a2ad11b9f6fae04fb27daa40a88Adam Lesinski                    Slog.e(TAG, "Failed to read usage stats file", e);
4473a0831bfe6e69a2ad11b9f6fae04fb27daa40a88Adam Lesinski                    // We continue so that we return results that are not
4483a0831bfe6e69a2ad11b9f6fae04fb27daa40a88Adam Lesinski                    // corrupt.
4490debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski                }
4500debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski            }
4513a0831bfe6e69a2ad11b9f6fae04fb27daa40a88Adam Lesinski            return results;
4520debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski        }
4530debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski    }
4540debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski
4553516800b611a79339a3c188332d13a26e9086b09Adam Lesinski    /**
4563516800b611a79339a3c188332d13a26e9086b09Adam Lesinski     * Find the interval that best matches this range.
4573516800b611a79339a3c188332d13a26e9086b09Adam Lesinski     *
4583516800b611a79339a3c188332d13a26e9086b09Adam Lesinski     * TODO(adamlesinski): Use endTimeStamp in best fit calculation.
4593516800b611a79339a3c188332d13a26e9086b09Adam Lesinski     */
4603516800b611a79339a3c188332d13a26e9086b09Adam Lesinski    public int findBestFitBucket(long beginTimeStamp, long endTimeStamp) {
4613516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        synchronized (mLock) {
4623516800b611a79339a3c188332d13a26e9086b09Adam Lesinski            int bestBucket = -1;
4633516800b611a79339a3c188332d13a26e9086b09Adam Lesinski            long smallestDiff = Long.MAX_VALUE;
4643516800b611a79339a3c188332d13a26e9086b09Adam Lesinski            for (int i = mSortedStatFiles.length - 1; i >= 0; i--) {
4653516800b611a79339a3c188332d13a26e9086b09Adam Lesinski                final int index = mSortedStatFiles[i].closestIndexOnOrBefore(beginTimeStamp);
4663516800b611a79339a3c188332d13a26e9086b09Adam Lesinski                int size = mSortedStatFiles[i].size();
4673516800b611a79339a3c188332d13a26e9086b09Adam Lesinski                if (index >= 0 && index < size) {
4683516800b611a79339a3c188332d13a26e9086b09Adam Lesinski                    // We have some results here, check if they are better than our current match.
4693516800b611a79339a3c188332d13a26e9086b09Adam Lesinski                    long diff = Math.abs(mSortedStatFiles[i].keyAt(index) - beginTimeStamp);
4703516800b611a79339a3c188332d13a26e9086b09Adam Lesinski                    if (diff < smallestDiff) {
4713516800b611a79339a3c188332d13a26e9086b09Adam Lesinski                        smallestDiff = diff;
4723516800b611a79339a3c188332d13a26e9086b09Adam Lesinski                        bestBucket = i;
4733516800b611a79339a3c188332d13a26e9086b09Adam Lesinski                    }
4743516800b611a79339a3c188332d13a26e9086b09Adam Lesinski                }
4753516800b611a79339a3c188332d13a26e9086b09Adam Lesinski            }
4763516800b611a79339a3c188332d13a26e9086b09Adam Lesinski            return bestBucket;
4773516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        }
4783516800b611a79339a3c188332d13a26e9086b09Adam Lesinski    }
4793516800b611a79339a3c188332d13a26e9086b09Adam Lesinski
4803516800b611a79339a3c188332d13a26e9086b09Adam Lesinski    /**
4813516800b611a79339a3c188332d13a26e9086b09Adam Lesinski     * Remove any usage stat files that are too old.
4823516800b611a79339a3c188332d13a26e9086b09Adam Lesinski     */
48366143fa5b34eea7413335111838fb692987b611aAdam Lesinski    public void prune(final long currentTimeMillis) {
4840debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski        synchronized (mLock) {
48566143fa5b34eea7413335111838fb692987b611aAdam Lesinski            mCal.setTimeInMillis(currentTimeMillis);
486d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski            mCal.addYears(-3);
4877f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski            pruneFilesOlderThan(mIntervalDirs[UsageStatsManager.INTERVAL_YEARLY],
4887f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski                    mCal.getTimeInMillis());
4890debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski
49066143fa5b34eea7413335111838fb692987b611aAdam Lesinski            mCal.setTimeInMillis(currentTimeMillis);
491d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski            mCal.addMonths(-6);
4923516800b611a79339a3c188332d13a26e9086b09Adam Lesinski            pruneFilesOlderThan(mIntervalDirs[UsageStatsManager.INTERVAL_MONTHLY],
4930debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski                    mCal.getTimeInMillis());
4940debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski
49566143fa5b34eea7413335111838fb692987b611aAdam Lesinski            mCal.setTimeInMillis(currentTimeMillis);
496d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski            mCal.addWeeks(-4);
4973516800b611a79339a3c188332d13a26e9086b09Adam Lesinski            pruneFilesOlderThan(mIntervalDirs[UsageStatsManager.INTERVAL_WEEKLY],
4980debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski                    mCal.getTimeInMillis());
4990debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski
50066143fa5b34eea7413335111838fb692987b611aAdam Lesinski            mCal.setTimeInMillis(currentTimeMillis);
501d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski            mCal.addDays(-7);
5023516800b611a79339a3c188332d13a26e9086b09Adam Lesinski            pruneFilesOlderThan(mIntervalDirs[UsageStatsManager.INTERVAL_DAILY],
5030debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski                    mCal.getTimeInMillis());
5043a0831bfe6e69a2ad11b9f6fae04fb27daa40a88Adam Lesinski
5053a0831bfe6e69a2ad11b9f6fae04fb27daa40a88Adam Lesinski            // We must re-index our file list or we will be trying to read
5063a0831bfe6e69a2ad11b9f6fae04fb27daa40a88Adam Lesinski            // deleted files.
5073a0831bfe6e69a2ad11b9f6fae04fb27daa40a88Adam Lesinski            indexFilesLocked();
5080debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski        }
5090debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski    }
5100debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski
5110debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski    private static void pruneFilesOlderThan(File dir, long expiryTime) {
5120debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski        File[] files = dir.listFiles();
5130debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski        if (files != null) {
5140debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski            for (File f : files) {
515a39871ef5e9bf6703f18af0725484b06f5c78216Brian Carlstrom                String path = f.getPath();
5161bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski                if (path.endsWith(BAK_SUFFIX)) {
5171bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski                    f = new File(path.substring(0, path.length() - BAK_SUFFIX.length()));
518a39871ef5e9bf6703f18af0725484b06f5c78216Brian Carlstrom                }
519e03f17c82eac7b6ce55e434fab29c7c428b46baaAdam Lesinski
520e03f17c82eac7b6ce55e434fab29c7c428b46baaAdam Lesinski                long beginTime;
521e03f17c82eac7b6ce55e434fab29c7c428b46baaAdam Lesinski                try {
522e03f17c82eac7b6ce55e434fab29c7c428b46baaAdam Lesinski                    beginTime = UsageStatsXml.parseBeginTime(f);
523e03f17c82eac7b6ce55e434fab29c7c428b46baaAdam Lesinski                } catch (IOException e) {
524e03f17c82eac7b6ce55e434fab29c7c428b46baaAdam Lesinski                    beginTime = 0;
525e03f17c82eac7b6ce55e434fab29c7c428b46baaAdam Lesinski                }
526e03f17c82eac7b6ce55e434fab29c7c428b46baaAdam Lesinski
5270debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski                if (beginTime < expiryTime) {
5280debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski                    new AtomicFile(f).delete();
5290debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski                }
5300debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski            }
5310debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski        }
5320debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski    }
5330debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski
5343516800b611a79339a3c188332d13a26e9086b09Adam Lesinski    /**
5353516800b611a79339a3c188332d13a26e9086b09Adam Lesinski     * Update the stats in the database. They may not be written to disk immediately.
5363516800b611a79339a3c188332d13a26e9086b09Adam Lesinski     */
5373516800b611a79339a3c188332d13a26e9086b09Adam Lesinski    public void putUsageStats(int intervalType, IntervalStats stats) throws IOException {
538e35ae5cce1bcc1841b433ed1c5bef0a5c911cf15Ritesh Reddy        if (stats == null) return;
5390debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski        synchronized (mLock) {
5403516800b611a79339a3c188332d13a26e9086b09Adam Lesinski            if (intervalType < 0 || intervalType >= mIntervalDirs.length) {
5413516800b611a79339a3c188332d13a26e9086b09Adam Lesinski                throw new IllegalArgumentException("Bad interval type " + intervalType);
5420debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski            }
5430debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski
5443516800b611a79339a3c188332d13a26e9086b09Adam Lesinski            AtomicFile f = mSortedStatFiles[intervalType].get(stats.beginTime);
5450debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski            if (f == null) {
5463516800b611a79339a3c188332d13a26e9086b09Adam Lesinski                f = new AtomicFile(new File(mIntervalDirs[intervalType],
5473516800b611a79339a3c188332d13a26e9086b09Adam Lesinski                        Long.toString(stats.beginTime)));
5483516800b611a79339a3c188332d13a26e9086b09Adam Lesinski                mSortedStatFiles[intervalType].put(stats.beginTime, f);
5490debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski            }
5500debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski
5513516800b611a79339a3c188332d13a26e9086b09Adam Lesinski            UsageStatsXml.write(f, stats);
5523516800b611a79339a3c188332d13a26e9086b09Adam Lesinski            stats.lastTimeSaved = f.getLastModifiedTime();
5530debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski        }
5540debc9aff4c0cbc28e083a948081d91b0f171319Adam Lesinski    }
5558a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy
5568a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy
5578a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy    /* Backup/Restore Code */
558e35ae5cce1bcc1841b433ed1c5bef0a5c911cf15Ritesh Reddy    byte[] getBackupPayload(String key) {
5598a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy        synchronized (mLock) {
5608a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy            ByteArrayOutputStream baos = new ByteArrayOutputStream();
5618a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy            if (KEY_USAGE_STATS.equals(key)) {
5628a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy                prune(System.currentTimeMillis());
5638a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy                DataOutputStream out = new DataOutputStream(baos);
5648a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy                try {
565e35ae5cce1bcc1841b433ed1c5bef0a5c911cf15Ritesh Reddy                    out.writeInt(BACKUP_VERSION);
5668a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy
5678a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy                    out.writeInt(mSortedStatFiles[UsageStatsManager.INTERVAL_DAILY].size());
568e35ae5cce1bcc1841b433ed1c5bef0a5c911cf15Ritesh Reddy                    for (int i = 0; i < mSortedStatFiles[UsageStatsManager.INTERVAL_DAILY].size();
569e35ae5cce1bcc1841b433ed1c5bef0a5c911cf15Ritesh Reddy                            i++) {
570e35ae5cce1bcc1841b433ed1c5bef0a5c911cf15Ritesh Reddy                        writeIntervalStatsToStream(out,
571e35ae5cce1bcc1841b433ed1c5bef0a5c911cf15Ritesh Reddy                                mSortedStatFiles[UsageStatsManager.INTERVAL_DAILY].valueAt(i));
5728a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy                    }
5738a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy
5748a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy                    out.writeInt(mSortedStatFiles[UsageStatsManager.INTERVAL_WEEKLY].size());
575e35ae5cce1bcc1841b433ed1c5bef0a5c911cf15Ritesh Reddy                    for (int i = 0; i < mSortedStatFiles[UsageStatsManager.INTERVAL_WEEKLY].size();
576e35ae5cce1bcc1841b433ed1c5bef0a5c911cf15Ritesh Reddy                            i++) {
577e35ae5cce1bcc1841b433ed1c5bef0a5c911cf15Ritesh Reddy                        writeIntervalStatsToStream(out,
578e35ae5cce1bcc1841b433ed1c5bef0a5c911cf15Ritesh Reddy                                mSortedStatFiles[UsageStatsManager.INTERVAL_WEEKLY].valueAt(i));
5798a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy                    }
5808a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy
5818a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy                    out.writeInt(mSortedStatFiles[UsageStatsManager.INTERVAL_MONTHLY].size());
582e35ae5cce1bcc1841b433ed1c5bef0a5c911cf15Ritesh Reddy                    for (int i = 0; i < mSortedStatFiles[UsageStatsManager.INTERVAL_MONTHLY].size();
583e35ae5cce1bcc1841b433ed1c5bef0a5c911cf15Ritesh Reddy                            i++) {
584e35ae5cce1bcc1841b433ed1c5bef0a5c911cf15Ritesh Reddy                        writeIntervalStatsToStream(out,
585e35ae5cce1bcc1841b433ed1c5bef0a5c911cf15Ritesh Reddy                                mSortedStatFiles[UsageStatsManager.INTERVAL_MONTHLY].valueAt(i));
5868a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy                    }
5878a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy
5888a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy                    out.writeInt(mSortedStatFiles[UsageStatsManager.INTERVAL_YEARLY].size());
589e35ae5cce1bcc1841b433ed1c5bef0a5c911cf15Ritesh Reddy                    for (int i = 0; i < mSortedStatFiles[UsageStatsManager.INTERVAL_YEARLY].size();
590e35ae5cce1bcc1841b433ed1c5bef0a5c911cf15Ritesh Reddy                            i++) {
591e35ae5cce1bcc1841b433ed1c5bef0a5c911cf15Ritesh Reddy                        writeIntervalStatsToStream(out,
592e35ae5cce1bcc1841b433ed1c5bef0a5c911cf15Ritesh Reddy                                mSortedStatFiles[UsageStatsManager.INTERVAL_YEARLY].valueAt(i));
5938a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy                    }
5948a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy                    if (DEBUG) Slog.i(TAG, "Written " + baos.size() + " bytes of data");
595e35ae5cce1bcc1841b433ed1c5bef0a5c911cf15Ritesh Reddy                } catch (IOException ioe) {
5968a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy                    Slog.d(TAG, "Failed to write data to output stream", ioe);
5978a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy                    baos.reset();
5988a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy                }
5998a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy            }
6008a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy            return baos.toByteArray();
6018a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy        }
6028a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy
6038a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy    }
6048a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy
605e35ae5cce1bcc1841b433ed1c5bef0a5c911cf15Ritesh Reddy    void applyRestoredPayload(String key, byte[] payload) {
6068a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy        synchronized (mLock) {
6078a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy            if (KEY_USAGE_STATS.equals(key)) {
6088a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy                // Read stats files for the current device configs
609e35ae5cce1bcc1841b433ed1c5bef0a5c911cf15Ritesh Reddy                IntervalStats dailyConfigSource =
610e35ae5cce1bcc1841b433ed1c5bef0a5c911cf15Ritesh Reddy                        getLatestUsageStats(UsageStatsManager.INTERVAL_DAILY);
611e35ae5cce1bcc1841b433ed1c5bef0a5c911cf15Ritesh Reddy                IntervalStats weeklyConfigSource =
612e35ae5cce1bcc1841b433ed1c5bef0a5c911cf15Ritesh Reddy                        getLatestUsageStats(UsageStatsManager.INTERVAL_WEEKLY);
613e35ae5cce1bcc1841b433ed1c5bef0a5c911cf15Ritesh Reddy                IntervalStats monthlyConfigSource =
614e35ae5cce1bcc1841b433ed1c5bef0a5c911cf15Ritesh Reddy                        getLatestUsageStats(UsageStatsManager.INTERVAL_MONTHLY);
615e35ae5cce1bcc1841b433ed1c5bef0a5c911cf15Ritesh Reddy                IntervalStats yearlyConfigSource =
616e35ae5cce1bcc1841b433ed1c5bef0a5c911cf15Ritesh Reddy                        getLatestUsageStats(UsageStatsManager.INTERVAL_YEARLY);
617e35ae5cce1bcc1841b433ed1c5bef0a5c911cf15Ritesh Reddy
6188a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy                try {
6198a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy                    DataInputStream in = new DataInputStream(new ByteArrayInputStream(payload));
620e35ae5cce1bcc1841b433ed1c5bef0a5c911cf15Ritesh Reddy                    int backupDataVersion = in.readInt();
621e35ae5cce1bcc1841b433ed1c5bef0a5c911cf15Ritesh Reddy
622e35ae5cce1bcc1841b433ed1c5bef0a5c911cf15Ritesh Reddy                    // Can't handle this backup set
623e35ae5cce1bcc1841b433ed1c5bef0a5c911cf15Ritesh Reddy                    if (backupDataVersion < 1 || backupDataVersion > BACKUP_VERSION) return;
624e35ae5cce1bcc1841b433ed1c5bef0a5c911cf15Ritesh Reddy
625e35ae5cce1bcc1841b433ed1c5bef0a5c911cf15Ritesh Reddy                    // Delete all stats files
626e35ae5cce1bcc1841b433ed1c5bef0a5c911cf15Ritesh Reddy                    // Do this after reading version and before actually restoring
627e35ae5cce1bcc1841b433ed1c5bef0a5c911cf15Ritesh Reddy                    for (int i = 0; i < mIntervalDirs.length; i++) {
628e35ae5cce1bcc1841b433ed1c5bef0a5c911cf15Ritesh Reddy                        deleteDirectoryContents(mIntervalDirs[i]);
629e35ae5cce1bcc1841b433ed1c5bef0a5c911cf15Ritesh Reddy                    }
6308a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy
6318a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy                    int fileCount = in.readInt();
632e35ae5cce1bcc1841b433ed1c5bef0a5c911cf15Ritesh Reddy                    for (int i = 0; i < fileCount; i++) {
6338a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy                        IntervalStats stats = deserializeIntervalStats(getIntervalStatsBytes(in));
6348a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy                        stats = mergeStats(stats, dailyConfigSource);
6358a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy                        putUsageStats(UsageStatsManager.INTERVAL_DAILY, stats);
6368a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy                    }
6378a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy
6388a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy                    fileCount = in.readInt();
639e35ae5cce1bcc1841b433ed1c5bef0a5c911cf15Ritesh Reddy                    for (int i = 0; i < fileCount; i++) {
6408a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy                        IntervalStats stats = deserializeIntervalStats(getIntervalStatsBytes(in));
6418a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy                        stats = mergeStats(stats, weeklyConfigSource);
6428a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy                        putUsageStats(UsageStatsManager.INTERVAL_WEEKLY, stats);
6438a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy                    }
6448a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy
6458a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy                    fileCount = in.readInt();
646e35ae5cce1bcc1841b433ed1c5bef0a5c911cf15Ritesh Reddy                    for (int i = 0; i < fileCount; i++) {
6478a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy                        IntervalStats stats = deserializeIntervalStats(getIntervalStatsBytes(in));
6488a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy                        stats = mergeStats(stats, monthlyConfigSource);
6498a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy                        putUsageStats(UsageStatsManager.INTERVAL_MONTHLY, stats);
6508a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy                    }
6518a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy
6528a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy                    fileCount = in.readInt();
653e35ae5cce1bcc1841b433ed1c5bef0a5c911cf15Ritesh Reddy                    for (int i = 0; i < fileCount; i++) {
6548a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy                        IntervalStats stats = deserializeIntervalStats(getIntervalStatsBytes(in));
6558a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy                        stats = mergeStats(stats, yearlyConfigSource);
6568a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy                        putUsageStats(UsageStatsManager.INTERVAL_YEARLY, stats);
6578a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy                    }
6588a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy                    if (DEBUG) Slog.i(TAG, "Completed Restoring UsageStats");
659e35ae5cce1bcc1841b433ed1c5bef0a5c911cf15Ritesh Reddy                } catch (IOException ioe) {
6608a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy                    Slog.d(TAG, "Failed to read data from input stream", ioe);
661e35ae5cce1bcc1841b433ed1c5bef0a5c911cf15Ritesh Reddy                } finally {
6628a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy                    indexFilesLocked();
6638a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy                }
6648a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy            }
6658a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy        }
6668a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy    }
6678a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy
6688a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy    /**
6698a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy     * Get the Configuration Statistics from the current device statistics and merge them
6708a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy     * with the backed up usage statistics.
6718a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy     */
6728a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy    private IntervalStats mergeStats(IntervalStats beingRestored, IntervalStats onDevice) {
673e35ae5cce1bcc1841b433ed1c5bef0a5c911cf15Ritesh Reddy        if (onDevice == null) return beingRestored;
674e35ae5cce1bcc1841b433ed1c5bef0a5c911cf15Ritesh Reddy        if (beingRestored == null) return null;
6758a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy        beingRestored.activeConfiguration = onDevice.activeConfiguration;
6768a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy        beingRestored.configurations.putAll(onDevice.configurations);
6778a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy        beingRestored.events = onDevice.events;
6788a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy        return beingRestored;
6798a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy    }
6808a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy
681e35ae5cce1bcc1841b433ed1c5bef0a5c911cf15Ritesh Reddy    private void writeIntervalStatsToStream(DataOutputStream out, AtomicFile statsFile)
682e35ae5cce1bcc1841b433ed1c5bef0a5c911cf15Ritesh Reddy            throws IOException {
6838a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy        IntervalStats stats = new IntervalStats();
6848a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy        try {
6858a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy            UsageStatsXml.read(statsFile, stats);
6868a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy        } catch (IOException e) {
6878a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy            Slog.e(TAG, "Failed to read usage stats file", e);
6888a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy            out.writeInt(0);
6898a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy            return;
6908a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy        }
6918a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy        sanitizeIntervalStatsForBackup(stats);
6928a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy        byte[] data = serializeIntervalStats(stats);
6938a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy        out.writeInt(data.length);
6948a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy        out.write(data);
6958a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy    }
6968a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy
6978a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy    private static byte[] getIntervalStatsBytes(DataInputStream in) throws IOException {
6988a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy        int length = in.readInt();
6998a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy        byte[] buffer = new byte[length];
7008a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy        in.read(buffer, 0, length);
7018a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy        return buffer;
7028a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy    }
7038a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy
7048a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy    private static void sanitizeIntervalStatsForBackup(IntervalStats stats) {
7058a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy        if (stats == null) return;
7068a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy        stats.activeConfiguration = null;
7078a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy        stats.configurations.clear();
7088a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy        if (stats.events != null) stats.events.clear();
7098a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy    }
7108a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy
7118a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy    private static byte[] serializeIntervalStats(IntervalStats stats) {
7128a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy        ByteArrayOutputStream baos = new ByteArrayOutputStream();
7138a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy        DataOutputStream out = new DataOutputStream(baos);
7148a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy        try {
7158a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy            out.writeLong(stats.beginTime);
7168a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy            UsageStatsXml.write(out, stats);
7178a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy        } catch (IOException ioe) {
7188a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy            Slog.d(TAG, "Serializing IntervalStats Failed", ioe);
7198a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy            baos.reset();
7208a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy        }
7218a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy        return baos.toByteArray();
7228a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy    }
7238a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy
7248a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy    private static IntervalStats deserializeIntervalStats(byte[] data) {
7258a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy        ByteArrayInputStream bais = new ByteArrayInputStream(data);
7268a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy        DataInputStream in = new DataInputStream(bais);
7278a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy        IntervalStats stats = new IntervalStats();
7288a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy        try {
7298a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy            stats.beginTime = in.readLong();
7308a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy            UsageStatsXml.read(in, stats);
7318a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy        } catch (IOException ioe) {
7328a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy            Slog.d(TAG, "DeSerializing IntervalStats Failed", ioe);
7338a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy            stats = null;
7348a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy        }
7358a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy        return stats;
7368a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy    }
7378a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy
738e35ae5cce1bcc1841b433ed1c5bef0a5c911cf15Ritesh Reddy    private static void deleteDirectoryContents(File directory) {
7398a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy        File[] files = directory.listFiles();
7408a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy        for (File file : files) {
7418a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy            deleteDirectory(file);
7428a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy        }
7438a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy    }
7448a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy
7458a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy    private static void deleteDirectory(File directory) {
7468a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy        File[] files = directory.listFiles();
747e35ae5cce1bcc1841b433ed1c5bef0a5c911cf15Ritesh Reddy        if (files != null) {
748e35ae5cce1bcc1841b433ed1c5bef0a5c911cf15Ritesh Reddy            for (File file : files) {
749e35ae5cce1bcc1841b433ed1c5bef0a5c911cf15Ritesh Reddy                if (!file.isDirectory()) {
750e35ae5cce1bcc1841b433ed1c5bef0a5c911cf15Ritesh Reddy                    file.delete();
751e35ae5cce1bcc1841b433ed1c5bef0a5c911cf15Ritesh Reddy                } else {
752e35ae5cce1bcc1841b433ed1c5bef0a5c911cf15Ritesh Reddy                    deleteDirectory(file);
753e35ae5cce1bcc1841b433ed1c5bef0a5c911cf15Ritesh Reddy                }
7548a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy            }
7558a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy        }
7568a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy        directory.delete();
7578a6ce2cb64f4d61ee426240c539dc8b00dac857fRitesh Reddy    }
758e35ae5cce1bcc1841b433ed1c5bef0a5c911cf15Ritesh Reddy}