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 Lesinskipackage com.android.server.usage;
173516800b611a79339a3c188332d13a26e9086b09Adam Lesinski
187f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinskiimport android.app.usage.ConfigurationStats;
193516800b611a79339a3c188332d13a26e9086b09Adam Lesinskiimport android.app.usage.TimeSparseArray;
203516800b611a79339a3c188332d13a26e9086b09Adam Lesinskiimport android.app.usage.UsageEvents;
213516800b611a79339a3c188332d13a26e9086b09Adam Lesinskiimport android.app.usage.UsageStats;
227f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinskiimport android.content.res.Configuration;
233516800b611a79339a3c188332d13a26e9086b09Adam Lesinskiimport android.util.ArrayMap;
249d9607527f5bbf49c96565b63b90e36276b0dda7Adam Lesinskiimport android.util.ArraySet;
253516800b611a79339a3c188332d13a26e9086b09Adam Lesinski
263516800b611a79339a3c188332d13a26e9086b09Adam Lesinskiclass IntervalStats {
273516800b611a79339a3c188332d13a26e9086b09Adam Lesinski    public long beginTime;
283516800b611a79339a3c188332d13a26e9086b09Adam Lesinski    public long endTime;
293516800b611a79339a3c188332d13a26e9086b09Adam Lesinski    public long lastTimeSaved;
3037a46b48dcb7e34ee3669cfb2ed78af08bfca3c7Adam Lesinski    public final ArrayMap<String, UsageStats> packageStats = new ArrayMap<>();
317f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski    public final ArrayMap<Configuration, ConfigurationStats> configurations = new ArrayMap<>();
327f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski    public Configuration activeConfiguration;
333516800b611a79339a3c188332d13a26e9086b09Adam Lesinski    public TimeSparseArray<UsageEvents.Event> events;
343516800b611a79339a3c188332d13a26e9086b09Adam Lesinski
359d9607527f5bbf49c96565b63b90e36276b0dda7Adam Lesinski    // A string cache. This is important as when we're parsing XML files, we don't want to
369d9607527f5bbf49c96565b63b90e36276b0dda7Adam Lesinski    // keep hundreds of strings that have the same contents. We will read the string
379d9607527f5bbf49c96565b63b90e36276b0dda7Adam Lesinski    // and only keep it if it's not in the cache. The GC will take care of the
389d9607527f5bbf49c96565b63b90e36276b0dda7Adam Lesinski    // strings that had identical copies in the cache.
399d9607527f5bbf49c96565b63b90e36276b0dda7Adam Lesinski    private final ArraySet<String> mStringCache = new ArraySet<>();
403516800b611a79339a3c188332d13a26e9086b09Adam Lesinski
417f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski    /**
427f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski     * Gets the UsageStats object for the given package, or creates one and adds it internally.
437f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski     */
443516800b611a79339a3c188332d13a26e9086b09Adam Lesinski    UsageStats getOrCreateUsageStats(String packageName) {
4537a46b48dcb7e34ee3669cfb2ed78af08bfca3c7Adam Lesinski        UsageStats usageStats = packageStats.get(packageName);
463516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        if (usageStats == null) {
473516800b611a79339a3c188332d13a26e9086b09Adam Lesinski            usageStats = new UsageStats();
487f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski            usageStats.mPackageName = getCachedStringRef(packageName);
493516800b611a79339a3c188332d13a26e9086b09Adam Lesinski            usageStats.mBeginTimeStamp = beginTime;
503516800b611a79339a3c188332d13a26e9086b09Adam Lesinski            usageStats.mEndTimeStamp = endTime;
5137a46b48dcb7e34ee3669cfb2ed78af08bfca3c7Adam Lesinski            packageStats.put(usageStats.mPackageName, usageStats);
523516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        }
533516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        return usageStats;
543516800b611a79339a3c188332d13a26e9086b09Adam Lesinski    }
553516800b611a79339a3c188332d13a26e9086b09Adam Lesinski
567f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski    /**
577f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski     * Gets the ConfigurationStats object for the given configuration, or creates one and adds it
587f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski     * internally.
597f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski     */
607f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski    ConfigurationStats getOrCreateConfigurationStats(Configuration config) {
617f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski        ConfigurationStats configStats = configurations.get(config);
627f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski        if (configStats == null) {
637f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski            configStats = new ConfigurationStats();
647f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski            configStats.mBeginTimeStamp = beginTime;
657f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski            configStats.mEndTimeStamp = endTime;
667f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski            configStats.mConfiguration = config;
677f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski            configurations.put(config, configStats);
687f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski        }
697f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski        return configStats;
707f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski    }
717f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski
727f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski    /**
737f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski     * Builds a UsageEvents.Event, but does not add it internally.
747f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski     */
757f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski    UsageEvents.Event buildEvent(String packageName, String className) {
767f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski        UsageEvents.Event event = new UsageEvents.Event();
777f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski        event.mPackage = getCachedStringRef(packageName);
787f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski        if (className != null) {
797f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski            event.mClass = getCachedStringRef(className);
807f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski        }
817f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski        return event;
827f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski    }
837f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski
84978a1ed5aa2752cd36ff51df91d2d2d8be2171d9Adam Lesinski    private boolean isStatefulEvent(int eventType) {
85978a1ed5aa2752cd36ff51df91d2d2d8be2171d9Adam Lesinski        switch (eventType) {
86978a1ed5aa2752cd36ff51df91d2d2d8be2171d9Adam Lesinski            case UsageEvents.Event.MOVE_TO_FOREGROUND:
87978a1ed5aa2752cd36ff51df91d2d2d8be2171d9Adam Lesinski            case UsageEvents.Event.MOVE_TO_BACKGROUND:
88978a1ed5aa2752cd36ff51df91d2d2d8be2171d9Adam Lesinski            case UsageEvents.Event.END_OF_DAY:
89978a1ed5aa2752cd36ff51df91d2d2d8be2171d9Adam Lesinski            case UsageEvents.Event.CONTINUE_PREVIOUS_DAY:
90978a1ed5aa2752cd36ff51df91d2d2d8be2171d9Adam Lesinski                return true;
91978a1ed5aa2752cd36ff51df91d2d2d8be2171d9Adam Lesinski        }
92978a1ed5aa2752cd36ff51df91d2d2d8be2171d9Adam Lesinski        return false;
93978a1ed5aa2752cd36ff51df91d2d2d8be2171d9Adam Lesinski    }
94978a1ed5aa2752cd36ff51df91d2d2d8be2171d9Adam Lesinski
953516800b611a79339a3c188332d13a26e9086b09Adam Lesinski    void update(String packageName, long timeStamp, int eventType) {
963516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        UsageStats usageStats = getOrCreateUsageStats(packageName);
973516800b611a79339a3c188332d13a26e9086b09Adam Lesinski
983516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        // TODO(adamlesinski): Ensure that we recover from incorrect event sequences
993516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        // like double MOVE_TO_BACKGROUND, etc.
1003516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        if (eventType == UsageEvents.Event.MOVE_TO_BACKGROUND ||
1013516800b611a79339a3c188332d13a26e9086b09Adam Lesinski                eventType == UsageEvents.Event.END_OF_DAY) {
1023516800b611a79339a3c188332d13a26e9086b09Adam Lesinski            if (usageStats.mLastEvent == UsageEvents.Event.MOVE_TO_FOREGROUND ||
1033516800b611a79339a3c188332d13a26e9086b09Adam Lesinski                    usageStats.mLastEvent == UsageEvents.Event.CONTINUE_PREVIOUS_DAY) {
1043516800b611a79339a3c188332d13a26e9086b09Adam Lesinski                usageStats.mTotalTimeInForeground += timeStamp - usageStats.mLastTimeUsed;
1053516800b611a79339a3c188332d13a26e9086b09Adam Lesinski            }
1063516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        }
107978a1ed5aa2752cd36ff51df91d2d2d8be2171d9Adam Lesinski
108978a1ed5aa2752cd36ff51df91d2d2d8be2171d9Adam Lesinski        if (isStatefulEvent(eventType)) {
109978a1ed5aa2752cd36ff51df91d2d2d8be2171d9Adam Lesinski            usageStats.mLastEvent = eventType;
110978a1ed5aa2752cd36ff51df91d2d2d8be2171d9Adam Lesinski        }
111978a1ed5aa2752cd36ff51df91d2d2d8be2171d9Adam Lesinski
112c8e8729244d75584ce71a74d29c452fe538a22c5Adam Lesinski        if (eventType != UsageEvents.Event.SYSTEM_INTERACTION) {
113c8e8729244d75584ce71a74d29c452fe538a22c5Adam Lesinski            usageStats.mLastTimeUsed = timeStamp;
114c8e8729244d75584ce71a74d29c452fe538a22c5Adam Lesinski        }
1153516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        usageStats.mEndTimeStamp = timeStamp;
1167f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski
1177f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski        if (eventType == UsageEvents.Event.MOVE_TO_FOREGROUND) {
1187f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski            usageStats.mLaunchCount += 1;
1197f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski        }
1207f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski
1217f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski        endTime = timeStamp;
1227f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski    }
1237f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski
1247f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski    void updateConfigurationStats(Configuration config, long timeStamp) {
1257f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski        if (activeConfiguration != null) {
1267f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski            ConfigurationStats activeStats = configurations.get(activeConfiguration);
1277f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski            activeStats.mTotalTimeActive += timeStamp - activeStats.mLastTimeActive;
1287f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski            activeStats.mLastTimeActive = timeStamp - 1;
1297f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski        }
1307f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski
1317f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski        if (config != null) {
1327f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski            ConfigurationStats configStats = getOrCreateConfigurationStats(config);
1337f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski            configStats.mLastTimeActive = timeStamp;
1347f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski            configStats.mActivationCount += 1;
1357f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski            activeConfiguration = configStats.mConfiguration;
1367f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski        }
1377f61e96db7c90c1f4418359672aa4656aebee500Adam Lesinski
1383516800b611a79339a3c188332d13a26e9086b09Adam Lesinski        endTime = timeStamp;
1393516800b611a79339a3c188332d13a26e9086b09Adam Lesinski    }
1403516800b611a79339a3c188332d13a26e9086b09Adam Lesinski
1419d9607527f5bbf49c96565b63b90e36276b0dda7Adam Lesinski    private String getCachedStringRef(String str) {
1429d9607527f5bbf49c96565b63b90e36276b0dda7Adam Lesinski        final int index = mStringCache.indexOf(str);
1439d9607527f5bbf49c96565b63b90e36276b0dda7Adam Lesinski        if (index < 0) {
1449d9607527f5bbf49c96565b63b90e36276b0dda7Adam Lesinski            mStringCache.add(str);
1459d9607527f5bbf49c96565b63b90e36276b0dda7Adam Lesinski            return str;
1469d9607527f5bbf49c96565b63b90e36276b0dda7Adam Lesinski        }
1479d9607527f5bbf49c96565b63b90e36276b0dda7Adam Lesinski        return mStringCache.valueAt(index);
1489d9607527f5bbf49c96565b63b90e36276b0dda7Adam Lesinski    }
1493516800b611a79339a3c188332d13a26e9086b09Adam Lesinski}
150