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 55 // Time attributes stored as an offset of the beginTime. 56 private static final String LAST_TIME_ACTIVE_ATTR = "lastTimeActive"; 57 private static final String END_TIME_ATTR = "endTime"; 58 private static final String TIME_ATTR = "time"; 59 60 private static void loadUsageStats(XmlPullParser parser, IntervalStats statsOut) 61 throws XmlPullParserException, IOException { 62 final String pkg = parser.getAttributeValue(null, PACKAGE_ATTR); 63 if (pkg == null) { 64 throw new ProtocolException("no " + PACKAGE_ATTR + " attribute present"); 65 } 66 67 final UsageStats stats = statsOut.getOrCreateUsageStats(pkg); 68 69 // Apply the offset to the beginTime to find the absolute time. 70 stats.mLastTimeUsed = statsOut.beginTime + XmlUtils.readLongAttribute( 71 parser, LAST_TIME_ACTIVE_ATTR); 72 73 stats.mTotalTimeInForeground = XmlUtils.readLongAttribute(parser, TOTAL_TIME_ACTIVE_ATTR); 74 stats.mLastEvent = XmlUtils.readIntAttribute(parser, LAST_EVENT_ATTR); 75 } 76 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 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 if (event.mEventType == UsageEvents.Event.CONFIGURATION_CHANGE) { 111 event.mConfiguration = new Configuration(); 112 Configuration.readXmlAttrs(parser, event.mConfiguration); 113 } 114 115 if (statsOut.events == null) { 116 statsOut.events = new TimeSparseArray<>(); 117 } 118 statsOut.events.put(event.mTimeStamp, event); 119 } 120 121 private static void writeUsageStats(XmlSerializer xml, final IntervalStats stats, 122 final UsageStats usageStats) throws IOException { 123 xml.startTag(null, PACKAGE_TAG); 124 125 // Write the time offset. 126 XmlUtils.writeLongAttribute(xml, LAST_TIME_ACTIVE_ATTR, 127 usageStats.mLastTimeUsed - stats.beginTime); 128 129 XmlUtils.writeStringAttribute(xml, PACKAGE_ATTR, usageStats.mPackageName); 130 XmlUtils.writeLongAttribute(xml, TOTAL_TIME_ACTIVE_ATTR, usageStats.mTotalTimeInForeground); 131 XmlUtils.writeIntAttribute(xml, LAST_EVENT_ATTR, usageStats.mLastEvent); 132 133 xml.endTag(null, PACKAGE_TAG); 134 } 135 136 private static void writeConfigStats(XmlSerializer xml, final IntervalStats stats, 137 final ConfigurationStats configStats, boolean isActive) throws IOException { 138 xml.startTag(null, CONFIG_TAG); 139 140 // Write the time offset. 141 XmlUtils.writeLongAttribute(xml, LAST_TIME_ACTIVE_ATTR, 142 configStats.mLastTimeActive - stats.beginTime); 143 144 XmlUtils.writeLongAttribute(xml, TOTAL_TIME_ACTIVE_ATTR, configStats.mTotalTimeActive); 145 XmlUtils.writeIntAttribute(xml, COUNT_ATTR, configStats.mActivationCount); 146 if (isActive) { 147 XmlUtils.writeBooleanAttribute(xml, ACTIVE_ATTR, true); 148 } 149 150 // Now write the attributes representing the configuration object. 151 Configuration.writeXmlAttrs(xml, configStats.mConfiguration); 152 153 xml.endTag(null, CONFIG_TAG); 154 } 155 156 private static void writeEvent(XmlSerializer xml, final IntervalStats stats, 157 final UsageEvents.Event event) throws IOException { 158 xml.startTag(null, EVENT_TAG); 159 160 // Store the time offset. 161 XmlUtils.writeLongAttribute(xml, TIME_ATTR, event.mTimeStamp - stats.beginTime); 162 163 XmlUtils.writeStringAttribute(xml, PACKAGE_ATTR, event.mPackage); 164 if (event.mClass != null) { 165 XmlUtils.writeStringAttribute(xml, CLASS_ATTR, event.mClass); 166 } 167 XmlUtils.writeIntAttribute(xml, TYPE_ATTR, event.mEventType); 168 169 if (event.mEventType == UsageEvents.Event.CONFIGURATION_CHANGE 170 && event.mConfiguration != null) { 171 Configuration.writeXmlAttrs(xml, event.mConfiguration); 172 } 173 174 xml.endTag(null, EVENT_TAG); 175 } 176 177 /** 178 * Reads from the {@link XmlPullParser}, assuming that it is already on the 179 * <code><usagestats></code> tag. 180 * 181 * @param parser The parser from which to read events. 182 * @param statsOut The stats object to populate with the data from the XML file. 183 */ 184 public static void read(XmlPullParser parser, IntervalStats statsOut) 185 throws XmlPullParserException, IOException { 186 statsOut.packageStats.clear(); 187 statsOut.configurations.clear(); 188 statsOut.activeConfiguration = null; 189 190 if (statsOut.events != null) { 191 statsOut.events.clear(); 192 } 193 194 statsOut.endTime = XmlUtils.readLongAttribute(parser, END_TIME_ATTR); 195 196 int eventCode; 197 int outerDepth = parser.getDepth(); 198 while ((eventCode = parser.next()) != XmlPullParser.END_DOCUMENT 199 && (eventCode != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { 200 if (eventCode != XmlPullParser.START_TAG) { 201 continue; 202 } 203 204 final String tag = parser.getName(); 205 switch (tag) { 206 case PACKAGE_TAG: 207 loadUsageStats(parser, statsOut); 208 break; 209 210 case CONFIG_TAG: 211 loadConfigStats(parser, statsOut); 212 break; 213 214 case EVENT_TAG: 215 loadEvent(parser, statsOut); 216 break; 217 } 218 } 219 } 220 221 /** 222 * Writes the stats object to an XML file. The {@link XmlSerializer} 223 * has already written the <code><usagestats></code> tag, but attributes may still 224 * be added. 225 * 226 * @param xml The serializer to which to write the packageStats data. 227 * @param stats The stats object to write to the XML file. 228 */ 229 public static void write(XmlSerializer xml, IntervalStats stats) throws IOException { 230 XmlUtils.writeLongAttribute(xml, END_TIME_ATTR, stats.endTime - stats.beginTime); 231 232 xml.startTag(null, PACKAGES_TAG); 233 final int statsCount = stats.packageStats.size(); 234 for (int i = 0; i < statsCount; i++) { 235 writeUsageStats(xml, stats, stats.packageStats.valueAt(i)); 236 } 237 xml.endTag(null, PACKAGES_TAG); 238 239 240 xml.startTag(null, CONFIGURATIONS_TAG); 241 final int configCount = stats.configurations.size(); 242 for (int i = 0; i < configCount; i++) { 243 boolean active = stats.activeConfiguration.equals(stats.configurations.keyAt(i)); 244 writeConfigStats(xml, stats, stats.configurations.valueAt(i), active); 245 } 246 xml.endTag(null, CONFIGURATIONS_TAG); 247 248 xml.startTag(null, EVENT_LOG_TAG); 249 final int eventCount = stats.events != null ? stats.events.size() : 0; 250 for (int i = 0; i < eventCount; i++) { 251 writeEvent(xml, stats, stats.events.valueAt(i)); 252 } 253 xml.endTag(null, EVENT_LOG_TAG); 254 } 255 256 private UsageStatsXmlV1() { 257 } 258 } 259