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; 29 30import java.io.IOException; 31import java.net.ProtocolException; 32 33/** 34 * UsageStats reader/writer for version 1 of the XML format. 35 */ 36final 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 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 stats.mTotalTimeInForeground = XmlUtils.readLongAttribute(parser, TOTAL_TIME_ACTIVE_ATTR); 73 stats.mLastEvent = XmlUtils.readIntAttribute(parser, LAST_EVENT_ATTR); 74 } 75 76 private static void loadConfigStats(XmlPullParser parser, IntervalStats statsOut) 77 throws XmlPullParserException, IOException { 78 final Configuration config = new Configuration(); 79 Configuration.readXmlAttrs(parser, config); 80 81 final ConfigurationStats configStats = statsOut.getOrCreateConfigurationStats(config); 82 83 // Apply the offset to the beginTime to find the absolute time. 84 configStats.mLastTimeActive = statsOut.beginTime + XmlUtils.readLongAttribute( 85 parser, LAST_TIME_ACTIVE_ATTR); 86 87 configStats.mTotalTimeActive = XmlUtils.readLongAttribute(parser, TOTAL_TIME_ACTIVE_ATTR); 88 configStats.mActivationCount = XmlUtils.readIntAttribute(parser, COUNT_ATTR); 89 if (XmlUtils.readBooleanAttribute(parser, ACTIVE_ATTR)) { 90 statsOut.activeConfiguration = configStats.mConfiguration; 91 } 92 } 93 94 private static void loadEvent(XmlPullParser parser, IntervalStats statsOut) 95 throws XmlPullParserException, IOException { 96 final String packageName = XmlUtils.readStringAttribute(parser, PACKAGE_ATTR); 97 if (packageName == null) { 98 throw new ProtocolException("no " + PACKAGE_ATTR + " attribute present"); 99 } 100 101 final String className = XmlUtils.readStringAttribute(parser, CLASS_ATTR); 102 103 final UsageEvents.Event event = statsOut.buildEvent(packageName, className); 104 105 // Apply the offset to the beginTime to find the absolute time of this event. 106 event.mTimeStamp = statsOut.beginTime + XmlUtils.readLongAttribute(parser, TIME_ATTR); 107 108 event.mEventType = XmlUtils.readIntAttribute(parser, TYPE_ATTR); 109 if (event.mEventType == UsageEvents.Event.CONFIGURATION_CHANGE) { 110 event.mConfiguration = new Configuration(); 111 Configuration.readXmlAttrs(parser, event.mConfiguration); 112 } 113 114 if (statsOut.events == null) { 115 statsOut.events = new TimeSparseArray<>(); 116 } 117 statsOut.events.put(event.mTimeStamp, event); 118 } 119 120 private static void writeUsageStats(XmlSerializer xml, final IntervalStats stats, 121 final UsageStats usageStats) throws IOException { 122 xml.startTag(null, PACKAGE_TAG); 123 124 // Write the time offset. 125 XmlUtils.writeLongAttribute(xml, LAST_TIME_ACTIVE_ATTR, 126 usageStats.mLastTimeUsed - stats.beginTime); 127 128 XmlUtils.writeStringAttribute(xml, PACKAGE_ATTR, usageStats.mPackageName); 129 XmlUtils.writeLongAttribute(xml, TOTAL_TIME_ACTIVE_ATTR, usageStats.mTotalTimeInForeground); 130 XmlUtils.writeIntAttribute(xml, LAST_EVENT_ATTR, usageStats.mLastEvent); 131 132 xml.endTag(null, PACKAGE_TAG); 133 } 134 135 private static void writeConfigStats(XmlSerializer xml, final IntervalStats stats, 136 final ConfigurationStats configStats, boolean isActive) throws IOException { 137 xml.startTag(null, CONFIG_TAG); 138 139 // Write the time offset. 140 XmlUtils.writeLongAttribute(xml, LAST_TIME_ACTIVE_ATTR, 141 configStats.mLastTimeActive - stats.beginTime); 142 143 XmlUtils.writeLongAttribute(xml, TOTAL_TIME_ACTIVE_ATTR, configStats.mTotalTimeActive); 144 XmlUtils.writeIntAttribute(xml, COUNT_ATTR, configStats.mActivationCount); 145 if (isActive) { 146 XmlUtils.writeBooleanAttribute(xml, ACTIVE_ATTR, true); 147 } 148 149 // Now write the attributes representing the configuration object. 150 Configuration.writeXmlAttrs(xml, configStats.mConfiguration); 151 152 xml.endTag(null, CONFIG_TAG); 153 } 154 155 private static void writeEvent(XmlSerializer xml, final IntervalStats stats, 156 final UsageEvents.Event event) throws IOException { 157 xml.startTag(null, EVENT_TAG); 158 159 // Store the time offset. 160 XmlUtils.writeLongAttribute(xml, TIME_ATTR, event.mTimeStamp - stats.beginTime); 161 162 XmlUtils.writeStringAttribute(xml, PACKAGE_ATTR, event.mPackage); 163 if (event.mClass != null) { 164 XmlUtils.writeStringAttribute(xml, CLASS_ATTR, event.mClass); 165 } 166 XmlUtils.writeIntAttribute(xml, TYPE_ATTR, event.mEventType); 167 168 if (event.mEventType == UsageEvents.Event.CONFIGURATION_CHANGE 169 && event.mConfiguration != null) { 170 Configuration.writeXmlAttrs(xml, event.mConfiguration); 171 } 172 173 xml.endTag(null, EVENT_TAG); 174 } 175 176 /** 177 * Reads from the {@link XmlPullParser}, assuming that it is already on the 178 * <code><usagestats></code> tag. 179 * 180 * @param parser The parser from which to read events. 181 * @param statsOut The stats object to populate with the data from the XML file. 182 */ 183 public static void read(XmlPullParser parser, IntervalStats statsOut) 184 throws XmlPullParserException, IOException { 185 statsOut.packageStats.clear(); 186 statsOut.configurations.clear(); 187 statsOut.activeConfiguration = null; 188 189 if (statsOut.events != null) { 190 statsOut.events.clear(); 191 } 192 193 statsOut.endTime = statsOut.beginTime + XmlUtils.readLongAttribute(parser, END_TIME_ATTR); 194 195 int eventCode; 196 int outerDepth = parser.getDepth(); 197 while ((eventCode = parser.next()) != XmlPullParser.END_DOCUMENT 198 && (eventCode != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { 199 if (eventCode != XmlPullParser.START_TAG) { 200 continue; 201 } 202 203 final String tag = parser.getName(); 204 switch (tag) { 205 case PACKAGE_TAG: 206 loadUsageStats(parser, statsOut); 207 break; 208 209 case CONFIG_TAG: 210 loadConfigStats(parser, statsOut); 211 break; 212 213 case EVENT_TAG: 214 loadEvent(parser, statsOut); 215 break; 216 } 217 } 218 } 219 220 /** 221 * Writes the stats object to an XML file. The {@link XmlSerializer} 222 * has already written the <code><usagestats></code> tag, but attributes may still 223 * be added. 224 * 225 * @param xml The serializer to which to write the packageStats data. 226 * @param stats The stats object to write to the XML file. 227 */ 228 public static void write(XmlSerializer xml, IntervalStats stats) throws IOException { 229 XmlUtils.writeLongAttribute(xml, END_TIME_ATTR, stats.endTime - stats.beginTime); 230 231 xml.startTag(null, PACKAGES_TAG); 232 final int statsCount = stats.packageStats.size(); 233 for (int i = 0; i < statsCount; i++) { 234 writeUsageStats(xml, stats, stats.packageStats.valueAt(i)); 235 } 236 xml.endTag(null, PACKAGES_TAG); 237 238 xml.startTag(null, CONFIGURATIONS_TAG); 239 final int configCount = stats.configurations.size(); 240 for (int i = 0; i < configCount; i++) { 241 boolean active = stats.activeConfiguration.equals(stats.configurations.keyAt(i)); 242 writeConfigStats(xml, stats, stats.configurations.valueAt(i), active); 243 } 244 xml.endTag(null, CONFIGURATIONS_TAG); 245 246 xml.startTag(null, EVENT_LOG_TAG); 247 final int eventCount = stats.events != null ? stats.events.size() : 0; 248 for (int i = 0; i < eventCount; i++) { 249 writeEvent(xml, stats, stats.events.valueAt(i)); 250 } 251 xml.endTag(null, EVENT_LOG_TAG); 252 } 253 254 private UsageStatsXmlV1() { 255 } 256} 257