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}