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