13516800b611a79339a3c188332d13a26e9086b09Adam Lesinski/**
23516800b611a79339a3c188332d13a26e9086b09Adam Lesinski * Copyright (C) 2014 The Android Open Source Project
33516800b611a79339a3c188332d13a26e9086b09Adam Lesinski *
43516800b611a79339a3c188332d13a26e9086b09Adam Lesinski * Licensed under the Apache License, Version 2.0 (the "License"); you may not
53516800b611a79339a3c188332d13a26e9086b09Adam Lesinski * use this file except in compliance with the License. You may obtain a copy
63516800b611a79339a3c188332d13a26e9086b09Adam Lesinski * of the License at
73516800b611a79339a3c188332d13a26e9086b09Adam Lesinski *
83516800b611a79339a3c188332d13a26e9086b09Adam Lesinski * http://www.apache.org/licenses/LICENSE-2.0
93516800b611a79339a3c188332d13a26e9086b09Adam Lesinski *
103516800b611a79339a3c188332d13a26e9086b09Adam Lesinski * Unless required by applicable law or agreed to in writing, software
113516800b611a79339a3c188332d13a26e9086b09Adam Lesinski * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
123516800b611a79339a3c188332d13a26e9086b09Adam Lesinski * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
133516800b611a79339a3c188332d13a26e9086b09Adam Lesinski * License for the specific language governing permissions and limitations
143516800b611a79339a3c188332d13a26e9086b09Adam Lesinski * under the License.
153516800b611a79339a3c188332d13a26e9086b09Adam Lesinski */
163516800b611a79339a3c188332d13a26e9086b09Adam Lesinski
173c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinskipackage com.android.server.usage;
183c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski
197f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinskiimport android.app.usage.ConfigurationStats;
203516800b611a79339a3c188332d13a26e9086b09Adam Lesinskiimport android.app.usage.TimeSparseArray;
213516800b611a79339a3c188332d13a26e9086b09Adam Lesinskiimport android.app.usage.UsageEvents;
223c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinskiimport android.app.usage.UsageStats;
233c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinskiimport android.app.usage.UsageStatsManager;
247f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinskiimport android.content.res.Configuration;
2566143fa5b34eea7413335111838fb692987b611aAdam Lesinskiimport android.os.SystemClock;
261bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinskiimport android.content.Context;
271bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinskiimport android.text.format.DateUtils;
281bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinskiimport android.util.ArrayMap;
293c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinskiimport android.util.ArraySet;
303c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinskiimport android.util.Slog;
313c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski
321bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinskiimport com.android.internal.util.IndentingPrintWriter;
337f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinskiimport com.android.server.usage.UsageStatsDatabase.StatCombiner;
347f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski
353c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinskiimport java.io.File;
363c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinskiimport java.io.IOException;
373c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinskiimport java.text.SimpleDateFormat;
383516800b611a79339a3c188332d13a26e9086b09Adam Lesinskiimport java.util.ArrayList;
393516800b611a79339a3c188332d13a26e9086b09Adam Lesinskiimport java.util.Arrays;
403516800b611a79339a3c188332d13a26e9086b09Adam Lesinskiimport java.util.List;
413c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski
423c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski/**
433c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski * A per-user UsageStatsService. All methods are meant to be called with the main lock held
443c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski * in UsageStatsService.
453c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski */
463c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinskiclass UserUsageStatsService {
473c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski    private static final String TAG = "UsageStatsService";
483c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski    private static final boolean DEBUG = UsageStatsService.DEBUG;
493c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski    private static final SimpleDateFormat sDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
501bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski    private static final int sDateFormatFlags =
511bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski            DateUtils.FORMAT_SHOW_DATE
521bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski            | DateUtils.FORMAT_SHOW_TIME
531bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski            | DateUtils.FORMAT_SHOW_YEAR
541bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski            | DateUtils.FORMAT_NUMERIC_DATE;
553c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski
561bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski    private final Context mContext;
573c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski    private final UsageStatsDatabase mDatabase;
583516800b611a79339a3c188332d13a26e9086b09Adam Lesinski    private final IntervalStats[] mCurrentStats;
593c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski    private boolean mStatsChanged = false;
60d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski    private final UnixCalendar mDailyExpiryDate;
613c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski    private final StatsUpdatedListener mListener;
623c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski    private final String mLogPrefix;
633c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski
643c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski    interface StatsUpdatedListener {
653c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski        void onStatsUpdated();
663c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski    }
673c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski
681bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski    UserUsageStatsService(Context context, int userId, File usageStatsDir, StatsUpdatedListener listener) {
691bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski        mContext = context;
70d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski        mDailyExpiryDate = new UnixCalendar(0);
713c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski        mDatabase = new UsageStatsDatabase(usageStatsDir);
723516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        mCurrentStats = new IntervalStats[UsageStatsManager.INTERVAL_COUNT];
733c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski        mListener = listener;
743c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski        mLogPrefix = "User[" + Integer.toString(userId) + "] ";
753c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski    }
763c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski
7766143fa5b34eea7413335111838fb692987b611aAdam Lesinski    void init(final long currentTimeMillis) {
7866143fa5b34eea7413335111838fb692987b611aAdam Lesinski        mDatabase.init(currentTimeMillis);
793c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski
803c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski        int nullCount = 0;
813c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski        for (int i = 0; i < mCurrentStats.length; i++) {
823c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski            mCurrentStats[i] = mDatabase.getLatestUsageStats(i);
833c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski            if (mCurrentStats[i] == null) {
843516800b611a79339a3c188332d13a26e9086b09Adam Lesinski                // Find out how many intervals we don't have data for.
853516800b611a79339a3c188332d13a26e9086b09Adam Lesinski                // Ideally it should be all or none.
863c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski                nullCount++;
873c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski            }
883c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski        }
893c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski
903c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski        if (nullCount > 0) {
913c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski            if (nullCount != mCurrentStats.length) {
923c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski                // This is weird, but we shouldn't fail if something like this
933c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski                // happens.
943c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski                Slog.w(TAG, mLogPrefix + "Some stats have no latest available");
953c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski            } else {
963c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski                // This must be first boot.
973c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski            }
983c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski
993c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski            // By calling loadActiveStats, we will
1003c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski            // generate new stats for each bucket.
10166143fa5b34eea7413335111838fb692987b611aAdam Lesinski            loadActiveStats(currentTimeMillis, false);
1023c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski        } else {
1033c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski            // Set up the expiry date to be one day from the latest daily stat.
1043c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski            // This may actually be today and we will rollover on the first event
1053c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski            // that is reported.
1063c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski            mDailyExpiryDate.setTimeInMillis(
1073516800b611a79339a3c188332d13a26e9086b09Adam Lesinski                    mCurrentStats[UsageStatsManager.INTERVAL_DAILY].beginTime);
108d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski            mDailyExpiryDate.addDays(1);
109d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski            mDailyExpiryDate.truncateToDay();
110d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski            Slog.i(TAG, mLogPrefix + "Rollover scheduled @ " +
111d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                    sDateFormat.format(mDailyExpiryDate.getTimeInMillis()) +
112d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                    "(" + mDailyExpiryDate.getTimeInMillis() + ")");
1133c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski        }
1143c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski
1153c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski        // Now close off any events that were open at the time this was saved.
1163516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        for (IntervalStats stat : mCurrentStats) {
11737a46b48dcb7e34ee3669cfb2ed78af08bfca3c7Adam Lesinski            final int pkgCount = stat.packageStats.size();
1183c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski            for (int i = 0; i < pkgCount; i++) {
11937a46b48dcb7e34ee3669cfb2ed78af08bfca3c7Adam Lesinski                UsageStats pkgStats = stat.packageStats.valueAt(i);
1203516800b611a79339a3c188332d13a26e9086b09Adam Lesinski                if (pkgStats.mLastEvent == UsageEvents.Event.MOVE_TO_FOREGROUND ||
1213516800b611a79339a3c188332d13a26e9086b09Adam Lesinski                        pkgStats.mLastEvent == UsageEvents.Event.CONTINUE_PREVIOUS_DAY) {
1223516800b611a79339a3c188332d13a26e9086b09Adam Lesinski                    stat.update(pkgStats.mPackageName, stat.lastTimeSaved,
1233516800b611a79339a3c188332d13a26e9086b09Adam Lesinski                            UsageEvents.Event.END_OF_DAY);
1243c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski                    notifyStatsChanged();
1253c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski                }
1263c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski            }
1277f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski
1287f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski            stat.updateConfigurationStats(null, stat.lastTimeSaved);
1293c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski        }
1303c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski    }
1313c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski
13266143fa5b34eea7413335111838fb692987b611aAdam Lesinski    void onTimeChanged(long oldTime, long newTime) {
13366143fa5b34eea7413335111838fb692987b611aAdam Lesinski        persistActiveStats();
13466143fa5b34eea7413335111838fb692987b611aAdam Lesinski        mDatabase.onTimeChanged(newTime - oldTime);
13566143fa5b34eea7413335111838fb692987b611aAdam Lesinski        loadActiveStats(newTime, true);
13666143fa5b34eea7413335111838fb692987b611aAdam Lesinski    }
13766143fa5b34eea7413335111838fb692987b611aAdam Lesinski
1383516800b611a79339a3c188332d13a26e9086b09Adam Lesinski    void reportEvent(UsageEvents.Event event) {
1393c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski        if (DEBUG) {
1409d9607527f5bbf49c96565b63b90e36276b0dda7Adam Lesinski            Slog.d(TAG, mLogPrefix + "Got usage event for " + event.mPackage
1417f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski                    + "[" + event.mTimeStamp + "]: "
1427f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski                    + eventToString(event.mEventType));
1433c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski        }
1443c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski
1457f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski        if (event.mTimeStamp >= mDailyExpiryDate.getTimeInMillis()) {
1463c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski            // Need to rollover
14766143fa5b34eea7413335111838fb692987b611aAdam Lesinski            rolloverStats(event.mTimeStamp);
1483c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski        }
1493c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski
1507f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski        final IntervalStats currentDailyStats = mCurrentStats[UsageStatsManager.INTERVAL_DAILY];
1517f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski
1527f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski        final Configuration newFullConfig = event.mConfiguration;
1537f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski        if (event.mEventType == UsageEvents.Event.CONFIGURATION_CHANGE &&
1547f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski                currentDailyStats.activeConfiguration != null) {
1557f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski            // Make the event configuration a delta.
1567f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski            event.mConfiguration = Configuration.generateDelta(
1577f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski                    currentDailyStats.activeConfiguration, newFullConfig);
1587f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski        }
1597f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski
1607f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski        // Add the event to the daily list.
1617f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski        if (currentDailyStats.events == null) {
1627f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski            currentDailyStats.events = new TimeSparseArray<>();
1633516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        }
1647f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski        currentDailyStats.events.put(event.mTimeStamp, event);
1653516800b611a79339a3c188332d13a26e9086b09Adam Lesinski
1663516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        for (IntervalStats stats : mCurrentStats) {
1677f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski            if (event.mEventType == UsageEvents.Event.CONFIGURATION_CHANGE) {
1687f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski                stats.updateConfigurationStats(newFullConfig, event.mTimeStamp);
1697f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski            } else {
1707f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski                stats.update(event.mPackage, event.mTimeStamp, event.mEventType);
1717f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski            }
1723c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski        }
1733c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski
1743c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski        notifyStatsChanged();
1753c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski    }
1763c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski
1777f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski    private static final StatCombiner<UsageStats> sUsageStatsCombiner =
1787f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski            new StatCombiner<UsageStats>() {
1797f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski                @Override
1807f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski                public void combine(IntervalStats stats, boolean mutable,
1817f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski                        List<UsageStats> accResult) {
1827f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski                    if (!mutable) {
18337a46b48dcb7e34ee3669cfb2ed78af08bfca3c7Adam Lesinski                        accResult.addAll(stats.packageStats.values());
1847f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski                        return;
1857f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski                    }
1867f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski
18737a46b48dcb7e34ee3669cfb2ed78af08bfca3c7Adam Lesinski                    final int statCount = stats.packageStats.size();
1887f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski                    for (int i = 0; i < statCount; i++) {
18937a46b48dcb7e34ee3669cfb2ed78af08bfca3c7Adam Lesinski                        accResult.add(new UsageStats(stats.packageStats.valueAt(i)));
1907f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski                    }
1917f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski                }
1927f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski            };
1937f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski
1947f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski    private static final StatCombiner<ConfigurationStats> sConfigStatsCombiner =
1957f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski            new StatCombiner<ConfigurationStats>() {
1967f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski                @Override
1977f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski                public void combine(IntervalStats stats, boolean mutable,
1987f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski                        List<ConfigurationStats> accResult) {
1997f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski                    if (!mutable) {
2007f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski                        accResult.addAll(stats.configurations.values());
2017f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski                        return;
2027f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski                    }
2037f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski
2047f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski                    final int configCount = stats.configurations.size();
2057f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski                    for (int i = 0; i < configCount; i++) {
2067f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski                        accResult.add(new ConfigurationStats(stats.configurations.valueAt(i)));
2077f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski                    }
2087f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski                }
2097f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski            };
2107f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski
2117f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski    /**
2127f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski     * Generic query method that selects the appropriate IntervalStats for the specified time range
2137f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski     * and bucket, then calls the {@link com.android.server.usage.UsageStatsDatabase.StatCombiner}
2147f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski     * provided to select the stats to use from the IntervalStats object.
2157f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski     */
216d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski    private <T> List<T> queryStats(int intervalType, final long beginTime, final long endTime,
2177f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski            StatCombiner<T> combiner) {
218d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski        if (intervalType == UsageStatsManager.INTERVAL_BEST) {
219d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski            intervalType = mDatabase.findBestFitBucket(beginTime, endTime);
220d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski            if (intervalType < 0) {
221d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                // Nothing saved to disk yet, so every stat is just as equal (no rollover has
222d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                // occurred.
223d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                intervalType = UsageStatsManager.INTERVAL_DAILY;
224d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski            }
2253516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        }
2263516800b611a79339a3c188332d13a26e9086b09Adam Lesinski
227d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski        if (intervalType < 0 || intervalType >= mCurrentStats.length) {
2283516800b611a79339a3c188332d13a26e9086b09Adam Lesinski            if (DEBUG) {
229d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                Slog.d(TAG, mLogPrefix + "Bad intervalType used " + intervalType);
2303516800b611a79339a3c188332d13a26e9086b09Adam Lesinski            }
2313516800b611a79339a3c188332d13a26e9086b09Adam Lesinski            return null;
2323516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        }
2333516800b611a79339a3c188332d13a26e9086b09Adam Lesinski
234d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski        final IntervalStats currentStats = mCurrentStats[intervalType];
235d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski
236d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski        if (DEBUG) {
237d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski            Slog.d(TAG, mLogPrefix + "SELECT * FROM " + intervalType + " WHERE beginTime >= "
238d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                    + beginTime + " AND endTime < " + endTime);
239d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski        }
240d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski
241d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski        if (beginTime >= currentStats.endTime) {
2423c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski            if (DEBUG) {
2433c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski                Slog.d(TAG, mLogPrefix + "Requesting stats after " + beginTime + " but latest is "
244d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                        + currentStats.endTime);
2453c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski            }
2463c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski            // Nothing newer available.
2473516800b611a79339a3c188332d13a26e9086b09Adam Lesinski            return null;
2483c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski        }
2493c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski
250d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski        // Truncate the endTime to just before the in-memory stats. Then, we'll append the
251d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski        // in-memory stats to the results (if necessary) so as to avoid writing to disk too
252d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski        // often.
253d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski        final long truncatedEndTime = Math.min(currentStats.beginTime, endTime);
2543516800b611a79339a3c188332d13a26e9086b09Adam Lesinski
255d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski        // Get the stats from disk.
256d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski        List<T> results = mDatabase.queryUsageStats(intervalType, beginTime,
257d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                truncatedEndTime, combiner);
2583c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski        if (DEBUG) {
259d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski            Slog.d(TAG, "Got " + (results != null ? results.size() : 0) + " results from disk");
260d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski            Slog.d(TAG, "Current stats beginTime=" + currentStats.beginTime +
261d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                    " endTime=" + currentStats.endTime);
262d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski        }
263d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski
264d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski        // Now check if the in-memory stats match the range and add them if they do.
265d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski        if (beginTime < currentStats.endTime && endTime > currentStats.beginTime) {
266d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski            if (DEBUG) {
267d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                Slog.d(TAG, mLogPrefix + "Returning in-memory stats");
268d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski            }
269d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski
270d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski            if (results == null) {
271d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                results = new ArrayList<>();
272d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski            }
273d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski            combiner.combine(currentStats, true, results);
2743c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski        }
2753c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski
2763c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski        if (DEBUG) {
277d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski            Slog.d(TAG, mLogPrefix + "Results: " + (results != null ? results.size() : 0));
2783c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski        }
2793c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski        return results;
2803c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski    }
2813c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski
2827f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski    List<UsageStats> queryUsageStats(int bucketType, long beginTime, long endTime) {
2837f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski        return queryStats(bucketType, beginTime, endTime, sUsageStatsCombiner);
2847f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski    }
2857f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski
2867f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski    List<ConfigurationStats> queryConfigurationStats(int bucketType, long beginTime, long endTime) {
2877f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski        return queryStats(bucketType, beginTime, endTime, sConfigStatsCombiner);
2887f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski    }
2897f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski
290d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski    UsageEvents queryEvents(final long beginTime, final long endTime) {
291d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski        final ArraySet<String> names = new ArraySet<>();
292d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski        List<UsageEvents.Event> results = queryStats(UsageStatsManager.INTERVAL_DAILY,
293d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                beginTime, endTime, new StatCombiner<UsageEvents.Event>() {
294d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                    @Override
295d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                    public void combine(IntervalStats stats, boolean mutable,
296d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                            List<UsageEvents.Event> accumulatedResult) {
297d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                        if (stats.events == null) {
298d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                            return;
299d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                        }
300d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski
301d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                        final int startIndex = stats.events.closestIndexOnOrAfter(beginTime);
302d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                        if (startIndex < 0) {
303d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                            return;
304d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                        }
305d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski
306d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                        final int size = stats.events.size();
307d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                        for (int i = startIndex; i < size; i++) {
308d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                            if (stats.events.keyAt(i) >= endTime) {
309d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                                return;
310d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                            }
311d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski
312d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                            final UsageEvents.Event event = stats.events.valueAt(i);
313d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                            names.add(event.mPackage);
314d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                            if (event.mClass != null) {
315d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                                names.add(event.mClass);
316d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                            }
317d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                            accumulatedResult.add(event);
318d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                        }
319d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                    }
320d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                });
3213516800b611a79339a3c188332d13a26e9086b09Adam Lesinski
322d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski        if (results == null || results.isEmpty()) {
323d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski            return null;
3243516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        }
3253516800b611a79339a3c188332d13a26e9086b09Adam Lesinski
326d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski        String[] table = names.toArray(new String[names.size()]);
327d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski        Arrays.sort(table);
328d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski        return new UsageEvents(results, table);
3293516800b611a79339a3c188332d13a26e9086b09Adam Lesinski    }
3303516800b611a79339a3c188332d13a26e9086b09Adam Lesinski
3313c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski    void persistActiveStats() {
3323c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski        if (mStatsChanged) {
3333c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski            Slog.i(TAG, mLogPrefix + "Flushing usage stats to disk");
3343c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski            try {
3353c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski                for (int i = 0; i < mCurrentStats.length; i++) {
3363c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski                    mDatabase.putUsageStats(i, mCurrentStats[i]);
3373c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski                }
3383c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski                mStatsChanged = false;
3393c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski            } catch (IOException e) {
3403c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski                Slog.e(TAG, mLogPrefix + "Failed to persist active stats", e);
3413c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski            }
3423c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski        }
3433c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski    }
3443c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski
34566143fa5b34eea7413335111838fb692987b611aAdam Lesinski    private void rolloverStats(final long currentTimeMillis) {
34666143fa5b34eea7413335111838fb692987b611aAdam Lesinski        final long startTime = SystemClock.elapsedRealtime();
3473c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski        Slog.i(TAG, mLogPrefix + "Rolling over usage stats");
3483c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski
3493c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski        // Finish any ongoing events with an END_OF_DAY event. Make a note of which components
3503c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski        // need a new CONTINUE_PREVIOUS_DAY entry.
3517f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski        final Configuration previousConfig =
3527f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski                mCurrentStats[UsageStatsManager.INTERVAL_DAILY].activeConfiguration;
3533c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski        ArraySet<String> continuePreviousDay = new ArraySet<>();
3543516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        for (IntervalStats stat : mCurrentStats) {
35537a46b48dcb7e34ee3669cfb2ed78af08bfca3c7Adam Lesinski            final int pkgCount = stat.packageStats.size();
3563c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski            for (int i = 0; i < pkgCount; i++) {
35737a46b48dcb7e34ee3669cfb2ed78af08bfca3c7Adam Lesinski                UsageStats pkgStats = stat.packageStats.valueAt(i);
3583516800b611a79339a3c188332d13a26e9086b09Adam Lesinski                if (pkgStats.mLastEvent == UsageEvents.Event.MOVE_TO_FOREGROUND ||
3593516800b611a79339a3c188332d13a26e9086b09Adam Lesinski                        pkgStats.mLastEvent == UsageEvents.Event.CONTINUE_PREVIOUS_DAY) {
3603c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski                    continuePreviousDay.add(pkgStats.mPackageName);
3617f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski                    stat.update(pkgStats.mPackageName, mDailyExpiryDate.getTimeInMillis() - 1,
3627f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski                            UsageEvents.Event.END_OF_DAY);
36366143fa5b34eea7413335111838fb692987b611aAdam Lesinski                    notifyStatsChanged();
3643c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski                }
3653c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski            }
3667f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski
3677f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski            stat.updateConfigurationStats(null, mDailyExpiryDate.getTimeInMillis() - 1);
3683c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski        }
3693c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski
3703c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski        persistActiveStats();
37166143fa5b34eea7413335111838fb692987b611aAdam Lesinski        mDatabase.prune(currentTimeMillis);
37266143fa5b34eea7413335111838fb692987b611aAdam Lesinski        loadActiveStats(currentTimeMillis, false);
3733c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski
3743c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski        final int continueCount = continuePreviousDay.size();
3753c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski        for (int i = 0; i < continueCount; i++) {
3763c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski            String name = continuePreviousDay.valueAt(i);
3777f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski            final long beginTime = mCurrentStats[UsageStatsManager.INTERVAL_DAILY].beginTime;
3783516800b611a79339a3c188332d13a26e9086b09Adam Lesinski            for (IntervalStats stat : mCurrentStats) {
3797f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski                stat.update(name, beginTime, UsageEvents.Event.CONTINUE_PREVIOUS_DAY);
3807f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski                stat.updateConfigurationStats(previousConfig, beginTime);
38166143fa5b34eea7413335111838fb692987b611aAdam Lesinski                notifyStatsChanged();
3823c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski            }
3833c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski        }
3843c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski        persistActiveStats();
3853c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski
38666143fa5b34eea7413335111838fb692987b611aAdam Lesinski        final long totalTime = SystemClock.elapsedRealtime() - startTime;
3873c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski        Slog.i(TAG, mLogPrefix + "Rolling over usage stats complete. Took " + totalTime
3883c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski                + " milliseconds");
3893c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski    }
3903c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski
3913c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski    private void notifyStatsChanged() {
3923c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski        if (!mStatsChanged) {
3933c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski            mStatsChanged = true;
3943c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski            mListener.onStatsUpdated();
3953c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski        }
3963c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski    }
3973c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski
39866143fa5b34eea7413335111838fb692987b611aAdam Lesinski    /**
39966143fa5b34eea7413335111838fb692987b611aAdam Lesinski     * @param force To force all in-memory stats to be reloaded.
40066143fa5b34eea7413335111838fb692987b611aAdam Lesinski     */
40166143fa5b34eea7413335111838fb692987b611aAdam Lesinski    private void loadActiveStats(final long currentTimeMillis, boolean force) {
402d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski        final UnixCalendar tempCal = mDailyExpiryDate;
403d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski        for (int intervalType = 0; intervalType < mCurrentStats.length; intervalType++) {
40466143fa5b34eea7413335111838fb692987b611aAdam Lesinski            tempCal.setTimeInMillis(currentTimeMillis);
405d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski            UnixCalendar.truncateTo(tempCal, intervalType);
4063c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski
40766143fa5b34eea7413335111838fb692987b611aAdam Lesinski            if (!force && mCurrentStats[intervalType] != null &&
408d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                    mCurrentStats[intervalType].beginTime == tempCal.getTimeInMillis()) {
4093c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski                // These are the same, no need to load them (in memory stats are always newer
4103c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski                // than persisted stats).
4113c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski                continue;
4123c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski            }
4133c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski
414d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski            final long lastBeginTime = mDatabase.getLatestUsageStatsBeginTime(intervalType);
41566143fa5b34eea7413335111838fb692987b611aAdam Lesinski            if (lastBeginTime >= tempCal.getTimeInMillis()) {
4163516800b611a79339a3c188332d13a26e9086b09Adam Lesinski                if (DEBUG) {
417d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                    Slog.d(TAG, mLogPrefix + "Loading existing stats @ " +
418d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                            sDateFormat.format(lastBeginTime) + "(" + lastBeginTime +
419d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                            ") for interval " + intervalType);
4203516800b611a79339a3c188332d13a26e9086b09Adam Lesinski                }
421d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                mCurrentStats[intervalType] = mDatabase.getLatestUsageStats(intervalType);
4223c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski            } else {
423d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                mCurrentStats[intervalType] = null;
4243516800b611a79339a3c188332d13a26e9086b09Adam Lesinski            }
4253516800b611a79339a3c188332d13a26e9086b09Adam Lesinski
426d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski            if (mCurrentStats[intervalType] == null) {
4273516800b611a79339a3c188332d13a26e9086b09Adam Lesinski                if (DEBUG) {
428d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                    Slog.d(TAG, "Creating new stats @ " +
429d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                            sDateFormat.format(tempCal.getTimeInMillis()) + "(" +
430d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                            tempCal.getTimeInMillis() + ") for interval " + intervalType);
4313516800b611a79339a3c188332d13a26e9086b09Adam Lesinski
4323516800b611a79339a3c188332d13a26e9086b09Adam Lesinski                }
433d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                mCurrentStats[intervalType] = new IntervalStats();
434d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                mCurrentStats[intervalType].beginTime = tempCal.getTimeInMillis();
43566143fa5b34eea7413335111838fb692987b611aAdam Lesinski                mCurrentStats[intervalType].endTime = currentTimeMillis;
4363c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski            }
4373c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski        }
4383c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski        mStatsChanged = false;
43966143fa5b34eea7413335111838fb692987b611aAdam Lesinski        mDailyExpiryDate.setTimeInMillis(currentTimeMillis);
440d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski        mDailyExpiryDate.addDays(1);
441d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski        mDailyExpiryDate.truncateToDay();
442d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski        Slog.i(TAG, mLogPrefix + "Rollover scheduled @ " +
443d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                sDateFormat.format(mDailyExpiryDate.getTimeInMillis()) + "(" +
444d26bea3a1498d1b327ae37cc796fb8cd67e9c977Adam Lesinski                tempCal.getTimeInMillis() + ")");
4453c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski    }
4463c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski
4471bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski    //
4481bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski    // -- DUMP related methods --
4491bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski    //
4501bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski
4511bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski    void checkin(final IndentingPrintWriter pw) {
4521bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski        mDatabase.checkinDailyFiles(new UsageStatsDatabase.CheckinAction() {
4531bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski            @Override
4541bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski            public boolean checkin(IntervalStats stats) {
4551bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski                printIntervalStats(pw, stats, false);
4561bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski                return true;
4571bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski            }
4581bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski        });
4591bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski    }
4601bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski
4611bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski    void dump(IndentingPrintWriter pw) {
4621bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski        // This is not a check-in, only dump in-memory stats.
4631bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski        for (int interval = 0; interval < mCurrentStats.length; interval++) {
4641bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski            pw.print("In-memory ");
4651bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski            pw.print(intervalToString(interval));
4661bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski            pw.println(" stats");
4671bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski            printIntervalStats(pw, mCurrentStats[interval], true);
4681bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski        }
4691bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski    }
4701bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski
4711bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski    private String formatDateTime(long dateTime, boolean pretty) {
4721bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski        if (pretty) {
4731bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski            return "\"" + DateUtils.formatDateTime(mContext, dateTime, sDateFormatFlags) + "\"";
4741bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski        }
4751bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski        return Long.toString(dateTime);
4761bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski    }
4771bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski
4781bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski    private String formatElapsedTime(long elapsedTime, boolean pretty) {
4791bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski        if (pretty) {
4801bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski            return "\"" + DateUtils.formatElapsedTime(elapsedTime / 1000) + "\"";
4811bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski        }
4821bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski        return Long.toString(elapsedTime);
4831bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski    }
4841bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski
4851bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski    void printIntervalStats(IndentingPrintWriter pw, IntervalStats stats, boolean prettyDates) {
4861bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski        if (prettyDates) {
4871bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski            pw.printPair("timeRange", "\"" + DateUtils.formatDateRange(mContext,
4881bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski                    stats.beginTime, stats.endTime, sDateFormatFlags) + "\"");
4891bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski        } else {
4901bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski            pw.printPair("beginTime", stats.beginTime);
4911bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski            pw.printPair("endTime", stats.endTime);
4921bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski        }
4931bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski        pw.println();
4941bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski        pw.increaseIndent();
4951bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski        pw.println("packages");
4961bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski        pw.increaseIndent();
4971bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski        final ArrayMap<String, UsageStats> pkgStats = stats.packageStats;
4981bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski        final int pkgCount = pkgStats.size();
4991bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski        for (int i = 0; i < pkgCount; i++) {
5001bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski            final UsageStats usageStats = pkgStats.valueAt(i);
5011bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski            pw.printPair("package", usageStats.mPackageName);
5021bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski            pw.printPair("totalTime", formatElapsedTime(usageStats.mTotalTimeInForeground, prettyDates));
5031bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski            pw.printPair("lastTime", formatDateTime(usageStats.mLastTimeUsed, prettyDates));
5041bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski            pw.println();
5051bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski        }
5061bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski        pw.decreaseIndent();
5071bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski
5081bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski        pw.println("configurations");
5091bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski        pw.increaseIndent();
5101bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski        final ArrayMap<Configuration, ConfigurationStats> configStats =
5111bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski                stats.configurations;
5121bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski        final int configCount = configStats.size();
5131bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski        for (int i = 0; i < configCount; i++) {
5141bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski            final ConfigurationStats config = configStats.valueAt(i);
5151bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski            pw.printPair("config", Configuration.resourceQualifierString(config.mConfiguration));
5161bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski            pw.printPair("totalTime", formatElapsedTime(config.mTotalTimeActive, prettyDates));
5171bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski            pw.printPair("lastTime", formatDateTime(config.mLastTimeActive, prettyDates));
5181bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski            pw.printPair("count", config.mActivationCount);
5191bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski            pw.println();
5201bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski        }
5211bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski        pw.decreaseIndent();
5221bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski
5231bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski        pw.println("events");
5241bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski        pw.increaseIndent();
5251bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski        final TimeSparseArray<UsageEvents.Event> events = stats.events;
5261bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski        final int eventCount = events != null ? events.size() : 0;
5271bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski        for (int i = 0; i < eventCount; i++) {
5281bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski            final UsageEvents.Event event = events.valueAt(i);
5291bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski            pw.printPair("time", formatDateTime(event.mTimeStamp, prettyDates));
5301bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski            pw.printPair("type", eventToString(event.mEventType));
5311bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski            pw.printPair("package", event.mPackage);
5321bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski            if (event.mClass != null) {
5331bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski                pw.printPair("class", event.mClass);
5341bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski            }
5351bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski            if (event.mConfiguration != null) {
5361bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski                pw.printPair("config", Configuration.resourceQualifierString(event.mConfiguration));
5371bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski            }
5381bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski            pw.println();
5391bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski        }
5401bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski        pw.decreaseIndent();
5411bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski        pw.decreaseIndent();
5421bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski    }
5431bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski
5441bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski    private static String intervalToString(int interval) {
5451bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski        switch (interval) {
5461bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski            case UsageStatsManager.INTERVAL_DAILY:
5471bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski                return "daily";
5481bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski            case UsageStatsManager.INTERVAL_WEEKLY:
5491bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski                return "weekly";
5501bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski            case UsageStatsManager.INTERVAL_MONTHLY:
5511bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski                return "monthly";
5521bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski            case UsageStatsManager.INTERVAL_YEARLY:
5531bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski                return "yearly";
5541bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski            default:
5551bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski                return "?";
5561bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski        }
5571bb18c435dbf967f3a9bc9d680411471b8bab4acAdam Lesinski    }
5583c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski
5593c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski    private static String eventToString(int eventType) {
5603c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski        switch (eventType) {
5613516800b611a79339a3c188332d13a26e9086b09Adam Lesinski            case UsageEvents.Event.NONE:
5623c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski                return "NONE";
5633516800b611a79339a3c188332d13a26e9086b09Adam Lesinski            case UsageEvents.Event.MOVE_TO_BACKGROUND:
5643c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski                return "MOVE_TO_BACKGROUND";
5653516800b611a79339a3c188332d13a26e9086b09Adam Lesinski            case UsageEvents.Event.MOVE_TO_FOREGROUND:
5663c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski                return "MOVE_TO_FOREGROUND";
5673516800b611a79339a3c188332d13a26e9086b09Adam Lesinski            case UsageEvents.Event.END_OF_DAY:
5683c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski                return "END_OF_DAY";
5693516800b611a79339a3c188332d13a26e9086b09Adam Lesinski            case UsageEvents.Event.CONTINUE_PREVIOUS_DAY:
5703c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski                return "CONTINUE_PREVIOUS_DAY";
5717f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski            case UsageEvents.Event.CONFIGURATION_CHANGE:
5727f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski                return "CONFIGURATION_CHANGE";
5733c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski            default:
5743c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski                return "UNKNOWN";
5753c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski        }
5763c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski    }
5773c153519ca5f2b66b88901374383f943c9d77df7Adam Lesinski}
578