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