• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 com.android.internal.util.XmlUtils;
19 
20 import org.xmlpull.v1.XmlPullParser;
21 import org.xmlpull.v1.XmlPullParserException;
22 import org.xmlpull.v1.XmlSerializer;
23 
24 import android.app.usage.ConfigurationStats;
25 import android.app.usage.TimeSparseArray;
26 import android.app.usage.UsageEvents;
27 import android.app.usage.UsageStats;
28 import android.content.res.Configuration;
29 
30 import java.io.IOException;
31 import java.net.ProtocolException;
32 
33 /**
34  * UsageStats reader/writer for version 1 of the XML format.
35  */
36 final class UsageStatsXmlV1 {
37     private static final String PACKAGES_TAG = "packages";
38     private static final String PACKAGE_TAG = "package";
39 
40     private static final String CONFIGURATIONS_TAG = "configurations";
41     private static final String CONFIG_TAG = "config";
42 
43     private static final String EVENT_LOG_TAG = "event-log";
44     private static final String EVENT_TAG = "event";
45 
46     // Attributes
47     private static final String PACKAGE_ATTR = "package";
48     private static final String CLASS_ATTR = "class";
49     private static final String TOTAL_TIME_ACTIVE_ATTR = "timeActive";
50     private static final String COUNT_ATTR = "count";
51     private static final String ACTIVE_ATTR = "active";
52     private static final String LAST_EVENT_ATTR = "lastEvent";
53     private static final String TYPE_ATTR = "type";
54     private static final String SHORTCUT_ID_ATTR = "shortcutId";
55 
56     // Time attributes stored as an offset of the beginTime.
57     private static final String LAST_TIME_ACTIVE_ATTR = "lastTimeActive";
58     private static final String END_TIME_ATTR = "endTime";
59     private static final String TIME_ATTR = "time";
60 
loadUsageStats(XmlPullParser parser, IntervalStats statsOut)61     private static void loadUsageStats(XmlPullParser parser, IntervalStats statsOut)
62             throws IOException {
63         final String pkg = parser.getAttributeValue(null, PACKAGE_ATTR);
64         if (pkg == null) {
65             throw new ProtocolException("no " + PACKAGE_ATTR + " attribute present");
66         }
67 
68         final UsageStats stats = statsOut.getOrCreateUsageStats(pkg);
69 
70         // Apply the offset to the beginTime to find the absolute time.
71         stats.mLastTimeUsed = statsOut.beginTime + XmlUtils.readLongAttribute(
72                 parser, LAST_TIME_ACTIVE_ATTR);
73         stats.mTotalTimeInForeground = XmlUtils.readLongAttribute(parser, TOTAL_TIME_ACTIVE_ATTR);
74         stats.mLastEvent = XmlUtils.readIntAttribute(parser, LAST_EVENT_ATTR);
75     }
76 
loadConfigStats(XmlPullParser parser, IntervalStats statsOut)77     private static void loadConfigStats(XmlPullParser parser, IntervalStats statsOut)
78             throws XmlPullParserException, IOException {
79         final Configuration config = new Configuration();
80         Configuration.readXmlAttrs(parser, config);
81 
82         final ConfigurationStats configStats = statsOut.getOrCreateConfigurationStats(config);
83 
84         // Apply the offset to the beginTime to find the absolute time.
85         configStats.mLastTimeActive = statsOut.beginTime + XmlUtils.readLongAttribute(
86                 parser, LAST_TIME_ACTIVE_ATTR);
87 
88         configStats.mTotalTimeActive = XmlUtils.readLongAttribute(parser, TOTAL_TIME_ACTIVE_ATTR);
89         configStats.mActivationCount = XmlUtils.readIntAttribute(parser, COUNT_ATTR);
90         if (XmlUtils.readBooleanAttribute(parser, ACTIVE_ATTR)) {
91             statsOut.activeConfiguration = configStats.mConfiguration;
92         }
93     }
94 
loadEvent(XmlPullParser parser, IntervalStats statsOut)95     private static void loadEvent(XmlPullParser parser, IntervalStats statsOut)
96             throws XmlPullParserException, IOException {
97         final String packageName = XmlUtils.readStringAttribute(parser, PACKAGE_ATTR);
98         if (packageName == null) {
99             throw new ProtocolException("no " + PACKAGE_ATTR + " attribute present");
100         }
101 
102         final String className = XmlUtils.readStringAttribute(parser, CLASS_ATTR);
103 
104         final UsageEvents.Event event = statsOut.buildEvent(packageName, className);
105 
106         // Apply the offset to the beginTime to find the absolute time of this event.
107         event.mTimeStamp = statsOut.beginTime + XmlUtils.readLongAttribute(parser, TIME_ATTR);
108 
109         event.mEventType = XmlUtils.readIntAttribute(parser, TYPE_ATTR);
110         switch (event.mEventType) {
111             case UsageEvents.Event.CONFIGURATION_CHANGE:
112                 event.mConfiguration = new Configuration();
113                 Configuration.readXmlAttrs(parser, event.mConfiguration);
114                 break;
115             case UsageEvents.Event.SHORTCUT_INVOCATION:
116                 final String id = XmlUtils.readStringAttribute(parser, SHORTCUT_ID_ATTR);
117                 event.mShortcutId = (id != null) ? id.intern() : null;
118                 break;
119         }
120 
121         if (statsOut.events == null) {
122             statsOut.events = new TimeSparseArray<>();
123         }
124         statsOut.events.put(event.mTimeStamp, event);
125     }
126 
writeUsageStats(XmlSerializer xml, final IntervalStats stats, final UsageStats usageStats)127     private static void writeUsageStats(XmlSerializer xml, final IntervalStats stats,
128             final UsageStats usageStats) throws IOException {
129         xml.startTag(null, PACKAGE_TAG);
130 
131         // Write the time offset.
132         XmlUtils.writeLongAttribute(xml, LAST_TIME_ACTIVE_ATTR,
133                 usageStats.mLastTimeUsed - stats.beginTime);
134 
135         XmlUtils.writeStringAttribute(xml, PACKAGE_ATTR, usageStats.mPackageName);
136         XmlUtils.writeLongAttribute(xml, TOTAL_TIME_ACTIVE_ATTR, usageStats.mTotalTimeInForeground);
137         XmlUtils.writeIntAttribute(xml, LAST_EVENT_ATTR, usageStats.mLastEvent);
138 
139         xml.endTag(null, PACKAGE_TAG);
140     }
141 
writeConfigStats(XmlSerializer xml, final IntervalStats stats, final ConfigurationStats configStats, boolean isActive)142     private static void writeConfigStats(XmlSerializer xml, final IntervalStats stats,
143             final ConfigurationStats configStats, boolean isActive) throws IOException {
144         xml.startTag(null, CONFIG_TAG);
145 
146         // Write the time offset.
147         XmlUtils.writeLongAttribute(xml, LAST_TIME_ACTIVE_ATTR,
148                 configStats.mLastTimeActive - stats.beginTime);
149 
150         XmlUtils.writeLongAttribute(xml, TOTAL_TIME_ACTIVE_ATTR, configStats.mTotalTimeActive);
151         XmlUtils.writeIntAttribute(xml, COUNT_ATTR, configStats.mActivationCount);
152         if (isActive) {
153             XmlUtils.writeBooleanAttribute(xml, ACTIVE_ATTR, true);
154         }
155 
156         // Now write the attributes representing the configuration object.
157         Configuration.writeXmlAttrs(xml, configStats.mConfiguration);
158 
159         xml.endTag(null, CONFIG_TAG);
160     }
161 
writeEvent(XmlSerializer xml, final IntervalStats stats, final UsageEvents.Event event)162     private static void writeEvent(XmlSerializer xml, final IntervalStats stats,
163             final UsageEvents.Event event) throws IOException {
164         xml.startTag(null, EVENT_TAG);
165 
166         // Store the time offset.
167         XmlUtils.writeLongAttribute(xml, TIME_ATTR, event.mTimeStamp - stats.beginTime);
168 
169         XmlUtils.writeStringAttribute(xml, PACKAGE_ATTR, event.mPackage);
170         if (event.mClass != null) {
171             XmlUtils.writeStringAttribute(xml, CLASS_ATTR, event.mClass);
172         }
173         XmlUtils.writeIntAttribute(xml, TYPE_ATTR, event.mEventType);
174 
175         switch (event.mEventType) {
176             case UsageEvents.Event.CONFIGURATION_CHANGE:
177                 if (event.mConfiguration != null) {
178                     Configuration.writeXmlAttrs(xml, event.mConfiguration);
179                 }
180                 break;
181             case UsageEvents.Event.SHORTCUT_INVOCATION:
182                 if (event.mShortcutId != null) {
183                     XmlUtils.writeStringAttribute(xml, SHORTCUT_ID_ATTR, event.mShortcutId);
184                 }
185                 break;
186         }
187 
188         xml.endTag(null, EVENT_TAG);
189     }
190 
191     /**
192      * Reads from the {@link XmlPullParser}, assuming that it is already on the
193      * <code><usagestats></code> tag.
194      *
195      * @param parser The parser from which to read events.
196      * @param statsOut The stats object to populate with the data from the XML file.
197      */
read(XmlPullParser parser, IntervalStats statsOut)198     public static void read(XmlPullParser parser, IntervalStats statsOut)
199             throws XmlPullParserException, IOException {
200         statsOut.packageStats.clear();
201         statsOut.configurations.clear();
202         statsOut.activeConfiguration = null;
203 
204         if (statsOut.events != null) {
205             statsOut.events.clear();
206         }
207 
208         statsOut.endTime = statsOut.beginTime + XmlUtils.readLongAttribute(parser, END_TIME_ATTR);
209 
210         int eventCode;
211         int outerDepth = parser.getDepth();
212         while ((eventCode = parser.next()) != XmlPullParser.END_DOCUMENT
213                 && (eventCode != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
214             if (eventCode != XmlPullParser.START_TAG) {
215                 continue;
216             }
217 
218             final String tag = parser.getName();
219             switch (tag) {
220                 case PACKAGE_TAG:
221                     loadUsageStats(parser, statsOut);
222                     break;
223 
224                 case CONFIG_TAG:
225                     loadConfigStats(parser, statsOut);
226                     break;
227 
228                 case EVENT_TAG:
229                     loadEvent(parser, statsOut);
230                     break;
231             }
232         }
233     }
234 
235     /**
236      * Writes the stats object to an XML file. The {@link XmlSerializer}
237      * has already written the <code><usagestats></code> tag, but attributes may still
238      * be added.
239      *
240      * @param xml The serializer to which to write the packageStats data.
241      * @param stats The stats object to write to the XML file.
242      */
write(XmlSerializer xml, IntervalStats stats)243     public static void write(XmlSerializer xml, IntervalStats stats) throws IOException {
244         XmlUtils.writeLongAttribute(xml, END_TIME_ATTR, stats.endTime - stats.beginTime);
245 
246         xml.startTag(null, PACKAGES_TAG);
247         final int statsCount = stats.packageStats.size();
248         for (int i = 0; i < statsCount; i++) {
249             writeUsageStats(xml, stats, stats.packageStats.valueAt(i));
250         }
251         xml.endTag(null, PACKAGES_TAG);
252 
253         xml.startTag(null, CONFIGURATIONS_TAG);
254         final int configCount = stats.configurations.size();
255         for (int i = 0; i < configCount; i++) {
256             boolean active = stats.activeConfiguration.equals(stats.configurations.keyAt(i));
257             writeConfigStats(xml, stats, stats.configurations.valueAt(i), active);
258         }
259         xml.endTag(null, CONFIGURATIONS_TAG);
260 
261         xml.startTag(null, EVENT_LOG_TAG);
262         final int eventCount = stats.events != null ? stats.events.size() : 0;
263         for (int i = 0; i < eventCount; i++) {
264             writeEvent(xml, stats, stats.events.valueAt(i));
265         }
266         xml.endTag(null, EVENT_LOG_TAG);
267     }
268 
UsageStatsXmlV1()269     private UsageStatsXmlV1() {
270     }
271 }
272