Home | History | Annotate | Download | only in usage
      1 /**
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
      5  * use this file except in compliance with the License. You may obtain a copy
      6  * of the License at
      7  *
      8  * http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
     12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
     13  * License for the specific language governing permissions and limitations
     14  * under the License.
     15  */
     16 package com.android.server.usage;
     17 
     18 import android.app.usage.ConfigurationStats;
     19 import android.app.usage.EventList;
     20 import android.app.usage.EventStats;
     21 import android.app.usage.TimeSparseArray;
     22 import android.app.usage.UsageEvents;
     23 import android.app.usage.UsageStats;
     24 import android.content.res.Configuration;
     25 import android.util.ArrayMap;
     26 import android.util.ArraySet;
     27 
     28 import java.util.List;
     29 
     30 class IntervalStats {
     31     public long beginTime;
     32     public long endTime;
     33     public long lastTimeSaved;
     34     public final EventTracker interactiveTracker = new EventTracker();
     35     public final EventTracker nonInteractiveTracker = new EventTracker();
     36     public final EventTracker keyguardShownTracker = new EventTracker();
     37     public final EventTracker keyguardHiddenTracker = new EventTracker();
     38     public final ArrayMap<String, UsageStats> packageStats = new ArrayMap<>();
     39     public final ArrayMap<Configuration, ConfigurationStats> configurations = new ArrayMap<>();
     40     public Configuration activeConfiguration;
     41     public EventList events;
     42 
     43     // A string cache. This is important as when we're parsing XML files, we don't want to
     44     // keep hundreds of strings that have the same contents. We will read the string
     45     // and only keep it if it's not in the cache. The GC will take care of the
     46     // strings that had identical copies in the cache.
     47     private final ArraySet<String> mStringCache = new ArraySet<>();
     48 
     49     public static final class EventTracker {
     50         public long curStartTime;
     51         public long lastEventTime;
     52         public long duration;
     53         public int count;
     54 
     55         public void commitTime(long timeStamp) {
     56             if (curStartTime != 0) {
     57                 duration += timeStamp - duration;
     58                 curStartTime = 0;
     59             }
     60         }
     61 
     62         public void update(long timeStamp) {
     63             if (curStartTime == 0) {
     64                 // If we aren't already running, time to bump the count.
     65                 count++;
     66             }
     67             commitTime(timeStamp);
     68             curStartTime = timeStamp;
     69             lastEventTime = timeStamp;
     70         }
     71 
     72         void addToEventStats(List<EventStats> out, int event, long beginTime, long endTime) {
     73             if (count != 0 || duration != 0) {
     74                 EventStats ev = new EventStats();
     75                 ev.mEventType = event;
     76                 ev.mCount = count;
     77                 ev.mTotalTime = duration;
     78                 ev.mLastEventTime = lastEventTime;
     79                 ev.mBeginTimeStamp = beginTime;
     80                 ev.mEndTimeStamp = endTime;
     81                 out.add(ev);
     82             }
     83         }
     84 
     85     }
     86 
     87     /**
     88      * Gets the UsageStats object for the given package, or creates one and adds it internally.
     89      */
     90     UsageStats getOrCreateUsageStats(String packageName) {
     91         UsageStats usageStats = packageStats.get(packageName);
     92         if (usageStats == null) {
     93             usageStats = new UsageStats();
     94             usageStats.mPackageName = getCachedStringRef(packageName);
     95             usageStats.mBeginTimeStamp = beginTime;
     96             usageStats.mEndTimeStamp = endTime;
     97             packageStats.put(usageStats.mPackageName, usageStats);
     98         }
     99         return usageStats;
    100     }
    101 
    102     /**
    103      * Gets the ConfigurationStats object for the given configuration, or creates one and adds it
    104      * internally.
    105      */
    106     ConfigurationStats getOrCreateConfigurationStats(Configuration config) {
    107         ConfigurationStats configStats = configurations.get(config);
    108         if (configStats == null) {
    109             configStats = new ConfigurationStats();
    110             configStats.mBeginTimeStamp = beginTime;
    111             configStats.mEndTimeStamp = endTime;
    112             configStats.mConfiguration = config;
    113             configurations.put(config, configStats);
    114         }
    115         return configStats;
    116     }
    117 
    118     /**
    119      * Builds a UsageEvents.Event, but does not add it internally.
    120      */
    121     UsageEvents.Event buildEvent(String packageName, String className) {
    122         UsageEvents.Event event = new UsageEvents.Event();
    123         event.mPackage = getCachedStringRef(packageName);
    124         if (className != null) {
    125             event.mClass = getCachedStringRef(className);
    126         }
    127         return event;
    128     }
    129 
    130     private boolean isStatefulEvent(int eventType) {
    131         switch (eventType) {
    132             case UsageEvents.Event.MOVE_TO_FOREGROUND:
    133             case UsageEvents.Event.MOVE_TO_BACKGROUND:
    134             case UsageEvents.Event.END_OF_DAY:
    135             case UsageEvents.Event.CONTINUE_PREVIOUS_DAY:
    136                 return true;
    137         }
    138         return false;
    139     }
    140 
    141     /**
    142      * Returns whether the event type is one caused by user visible
    143      * interaction. Excludes those that are internally generated.
    144      * @param eventType
    145      * @return
    146      */
    147     private boolean isUserVisibleEvent(int eventType) {
    148         return eventType != UsageEvents.Event.SYSTEM_INTERACTION
    149                 && eventType != UsageEvents.Event.STANDBY_BUCKET_CHANGED;
    150     }
    151 
    152     void update(String packageName, long timeStamp, int eventType) {
    153         UsageStats usageStats = getOrCreateUsageStats(packageName);
    154 
    155         // TODO(adamlesinski): Ensure that we recover from incorrect event sequences
    156         // like double MOVE_TO_BACKGROUND, etc.
    157         if (eventType == UsageEvents.Event.MOVE_TO_BACKGROUND ||
    158                 eventType == UsageEvents.Event.END_OF_DAY) {
    159             if (usageStats.mLastEvent == UsageEvents.Event.MOVE_TO_FOREGROUND ||
    160                     usageStats.mLastEvent == UsageEvents.Event.CONTINUE_PREVIOUS_DAY) {
    161                 usageStats.mTotalTimeInForeground += timeStamp - usageStats.mLastTimeUsed;
    162             }
    163         }
    164 
    165         if (isStatefulEvent(eventType)) {
    166             usageStats.mLastEvent = eventType;
    167         }
    168 
    169         if (isUserVisibleEvent(eventType)) {
    170             usageStats.mLastTimeUsed = timeStamp;
    171         }
    172         usageStats.mEndTimeStamp = timeStamp;
    173 
    174         if (eventType == UsageEvents.Event.MOVE_TO_FOREGROUND) {
    175             usageStats.mLaunchCount += 1;
    176         }
    177 
    178         endTime = timeStamp;
    179     }
    180 
    181     void updateChooserCounts(String packageName, String category, String action) {
    182         UsageStats usageStats = getOrCreateUsageStats(packageName);
    183         if (usageStats.mChooserCounts == null) {
    184             usageStats.mChooserCounts = new ArrayMap<>();
    185         }
    186         ArrayMap<String, Integer> chooserCounts;
    187         final int idx = usageStats.mChooserCounts.indexOfKey(action);
    188         if (idx < 0) {
    189             chooserCounts = new ArrayMap<>();
    190             usageStats.mChooserCounts.put(action, chooserCounts);
    191         } else {
    192             chooserCounts = usageStats.mChooserCounts.valueAt(idx);
    193         }
    194         int currentCount = chooserCounts.getOrDefault(category, 0);
    195         chooserCounts.put(category, currentCount + 1);
    196     }
    197 
    198     void updateConfigurationStats(Configuration config, long timeStamp) {
    199         if (activeConfiguration != null) {
    200             ConfigurationStats activeStats = configurations.get(activeConfiguration);
    201             activeStats.mTotalTimeActive += timeStamp - activeStats.mLastTimeActive;
    202             activeStats.mLastTimeActive = timeStamp - 1;
    203         }
    204 
    205         if (config != null) {
    206             ConfigurationStats configStats = getOrCreateConfigurationStats(config);
    207             configStats.mLastTimeActive = timeStamp;
    208             configStats.mActivationCount += 1;
    209             activeConfiguration = configStats.mConfiguration;
    210         }
    211 
    212         endTime = timeStamp;
    213     }
    214 
    215     void incrementAppLaunchCount(String packageName) {
    216         UsageStats usageStats = getOrCreateUsageStats(packageName);
    217         usageStats.mAppLaunchCount += 1;
    218     }
    219 
    220     void commitTime(long timeStamp) {
    221         interactiveTracker.commitTime(timeStamp);
    222         nonInteractiveTracker.commitTime(timeStamp);
    223         keyguardShownTracker.commitTime(timeStamp);
    224         keyguardHiddenTracker.commitTime(timeStamp);
    225     }
    226 
    227     void updateScreenInteractive(long timeStamp) {
    228         interactiveTracker.update(timeStamp);
    229         nonInteractiveTracker.commitTime(timeStamp);
    230     }
    231 
    232     void updateScreenNonInteractive(long timeStamp) {
    233         nonInteractiveTracker.update(timeStamp);
    234         interactiveTracker.commitTime(timeStamp);
    235     }
    236 
    237     void updateKeyguardShown(long timeStamp) {
    238         keyguardShownTracker.update(timeStamp);
    239         keyguardHiddenTracker.commitTime(timeStamp);
    240     }
    241 
    242     void updateKeyguardHidden(long timeStamp) {
    243         keyguardHiddenTracker.update(timeStamp);
    244         keyguardShownTracker.commitTime(timeStamp);
    245     }
    246 
    247     void addEventStatsTo(List<EventStats> out) {
    248         interactiveTracker.addToEventStats(out, UsageEvents.Event.SCREEN_INTERACTIVE,
    249                 beginTime, endTime);
    250         nonInteractiveTracker.addToEventStats(out, UsageEvents.Event.SCREEN_NON_INTERACTIVE,
    251                 beginTime, endTime);
    252         keyguardShownTracker.addToEventStats(out, UsageEvents.Event.KEYGUARD_SHOWN,
    253                 beginTime, endTime);
    254         keyguardHiddenTracker.addToEventStats(out, UsageEvents.Event.KEYGUARD_HIDDEN,
    255                 beginTime, endTime);
    256     }
    257 
    258     private String getCachedStringRef(String str) {
    259         final int index = mStringCache.indexOf(str);
    260         if (index < 0) {
    261             mStringCache.add(str);
    262             return str;
    263         }
    264         return mStringCache.valueAt(index);
    265     }
    266 }
    267