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 17package com.android.server.usage; 18 19import android.util.AtomicFile; 20import android.util.Slog; 21import android.util.Xml; 22import com.android.internal.util.FastXmlSerializer; 23import com.android.internal.util.XmlUtils; 24import org.xmlpull.v1.XmlPullParser; 25import org.xmlpull.v1.XmlPullParserException; 26 27import java.io.*; 28 29public class UsageStatsXml { 30 private static final String TAG = "UsageStatsXml"; 31 private static final int CURRENT_VERSION = 1; 32 private static final String USAGESTATS_TAG = "usagestats"; 33 private static final String VERSION_ATTR = "version"; 34 static final String CHECKED_IN_SUFFIX = "-c"; 35 36 public static long parseBeginTime(AtomicFile file) { 37 return parseBeginTime(file.getBaseFile()); 38 } 39 40 public static long parseBeginTime(File file) { 41 String name = file.getName(); 42 43 // Eat as many occurrences of -c as possible. This is due to a bug where -c 44 // would be appended more than once to a checked-in file, causing a crash 45 // on boot when indexing files since Long.parseLong() will puke on anything but 46 // a number. 47 while (name.endsWith(CHECKED_IN_SUFFIX)) { 48 name = name.substring(0, name.length() - CHECKED_IN_SUFFIX.length()); 49 } 50 return Long.parseLong(name); 51 } 52 53 public static void read(AtomicFile file, IntervalStats statsOut) throws IOException { 54 try { 55 FileInputStream in = file.openRead(); 56 try { 57 statsOut.beginTime = parseBeginTime(file); 58 read(in, statsOut); 59 statsOut.lastTimeSaved = file.getLastModifiedTime(); 60 } finally { 61 try { 62 in.close(); 63 } catch (IOException e) { 64 // Empty 65 } 66 } 67 } catch (FileNotFoundException e) { 68 Slog.e(TAG, "UsageStats Xml", e); 69 throw e; 70 } 71 } 72 73 public static void write(AtomicFile file, IntervalStats stats) throws IOException { 74 FileOutputStream fos = file.startWrite(); 75 try { 76 write(fos, stats); 77 file.finishWrite(fos); 78 fos = null; 79 } finally { 80 // When fos is null (successful write), this will no-op 81 file.failWrite(fos); 82 } 83 } 84 85 private static void read(InputStream in, IntervalStats statsOut) throws IOException { 86 XmlPullParser parser = Xml.newPullParser(); 87 try { 88 parser.setInput(in, "utf-8"); 89 XmlUtils.beginDocument(parser, USAGESTATS_TAG); 90 String versionStr = parser.getAttributeValue(null, VERSION_ATTR); 91 try { 92 switch (Integer.parseInt(versionStr)) { 93 case 1: 94 UsageStatsXmlV1.read(parser, statsOut); 95 break; 96 97 default: 98 Slog.e(TAG, "Unrecognized version " + versionStr); 99 throw new IOException("Unrecognized version " + versionStr); 100 } 101 } catch (NumberFormatException e) { 102 Slog.e(TAG, "Bad version"); 103 throw new IOException(e); 104 } 105 } catch (XmlPullParserException e) { 106 Slog.e(TAG, "Failed to parse Xml", e); 107 throw new IOException(e); 108 } 109 } 110 111 private static void write(OutputStream out, IntervalStats stats) throws IOException { 112 FastXmlSerializer xml = new FastXmlSerializer(); 113 xml.setOutput(out, "utf-8"); 114 xml.startDocument("utf-8", true); 115 xml.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); 116 xml.startTag(null, USAGESTATS_TAG); 117 xml.attribute(null, VERSION_ATTR, Integer.toString(CURRENT_VERSION)); 118 119 UsageStatsXmlV1.write(xml, stats); 120 121 xml.endTag(null, USAGESTATS_TAG); 122 xml.endDocument(); 123 } 124} 125