summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/java/com/android/internal/util/FileRotator.java63
-rw-r--r--core/tests/coretests/src/com/android/internal/util/FileRotatorTest.java4
-rw-r--r--services/java/com/android/server/net/NetworkStatsRecorder.java37
-rw-r--r--services/java/com/android/server/net/NetworkStatsService.java4
4 files changed, 91 insertions, 17 deletions
diff --git a/core/java/com/android/internal/util/FileRotator.java b/core/java/com/android/internal/util/FileRotator.java
index 6cfb97d..26235f1 100644
--- a/core/java/com/android/internal/util/FileRotator.java
+++ b/core/java/com/android/internal/util/FileRotator.java
@@ -19,8 +19,6 @@ package com.android.internal.util;
import android.os.FileUtils;
import android.util.Slog;
-import com.android.internal.util.FileRotator.Rewriter;
-
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
@@ -29,8 +27,11 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
import libcore.io.IoUtils;
+import libcore.io.Streams;
/**
* Utility that rotates files over time, similar to {@code logrotate}. There is
@@ -137,10 +138,38 @@ public class FileRotator {
public void deleteAll() {
final FileInfo info = new FileInfo(mPrefix);
for (String name : mBasePath.list()) {
- if (!info.parse(name)) continue;
+ if (info.parse(name)) {
+ // delete each file that matches parser
+ new File(mBasePath, name).delete();
+ }
+ }
+ }
+
+ /**
+ * Dump all files managed by this rotator for debugging purposes.
+ */
+ public void dumpAll(OutputStream os) throws IOException {
+ final ZipOutputStream zos = new ZipOutputStream(os);
+ try {
+ final FileInfo info = new FileInfo(mPrefix);
+ for (String name : mBasePath.list()) {
+ if (info.parse(name)) {
+ final ZipEntry entry = new ZipEntry(name);
+ zos.putNextEntry(entry);
- // delete each file that matches parser
- new File(mBasePath, name).delete();
+ final File file = new File(mBasePath, name);
+ final FileInputStream is = new FileInputStream(file);
+ try {
+ Streams.copy(is, zos);
+ } finally {
+ IoUtils.closeQuietly(is);
+ }
+
+ zos.closeEntry();
+ }
+ }
+ } finally {
+ IoUtils.closeQuietly(zos);
}
}
@@ -159,22 +188,22 @@ public class FileRotator {
public void combineActive(final Reader reader, final Writer writer, long currentTimeMillis)
throws IOException {
rewriteActive(new Rewriter() {
- /** {@inheritDoc} */
+ @Override
public void reset() {
// ignored
}
- /** {@inheritDoc} */
+ @Override
public void read(InputStream in) throws IOException {
reader.read(in);
}
- /** {@inheritDoc} */
+ @Override
public boolean shouldWrite() {
return true;
}
- /** {@inheritDoc} */
+ @Override
public void write(OutputStream out) throws IOException {
writer.write(out);
}
@@ -224,11 +253,11 @@ public class FileRotator {
// write success, delete backup
backupFile.delete();
- } catch (IOException e) {
+ } catch (Throwable t) {
// write failed, delete file and restore backup
file.delete();
backupFile.renameTo(file);
- throw e;
+ throw rethrowAsIoException(t);
}
} else {
@@ -241,11 +270,11 @@ public class FileRotator {
// write success, delete empty backup
backupFile.delete();
- } catch (IOException e) {
+ } catch (Throwable t) {
// write failed, delete file and empty backup
file.delete();
backupFile.delete();
- throw e;
+ throw rethrowAsIoException(t);
}
}
}
@@ -353,6 +382,14 @@ public class FileRotator {
}
}
+ private static IOException rethrowAsIoException(Throwable t) throws IOException {
+ if (t instanceof IOException) {
+ throw (IOException) t;
+ } else {
+ throw new IOException(t.getMessage(), t);
+ }
+ }
+
/**
* Details for a rotated file, either parsed from an existing filename, or
* ready to be built into a new filename.
diff --git a/core/tests/coretests/src/com/android/internal/util/FileRotatorTest.java b/core/tests/coretests/src/com/android/internal/util/FileRotatorTest.java
index 94d1cb6..95f0e67 100644
--- a/core/tests/coretests/src/com/android/internal/util/FileRotatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/util/FileRotatorTest.java
@@ -187,12 +187,12 @@ public class FileRotatorTest extends AndroidTestCase {
rotate.combineActive(reader, new Writer() {
public void write(OutputStream out) throws IOException {
new DataOutputStream(out).writeUTF("bar");
- throw new ProtocolException("yikes");
+ throw new NullPointerException("yikes");
}
}, currentTime);
fail("woah, somehow able to write exception");
- } catch (ProtocolException e) {
+ } catch (IOException e) {
// expected from above
}
diff --git a/services/java/com/android/server/net/NetworkStatsRecorder.java b/services/java/com/android/server/net/NetworkStatsRecorder.java
index 2ce7771..c3ecf54 100644
--- a/services/java/com/android/server/net/NetworkStatsRecorder.java
+++ b/services/java/com/android/server/net/NetworkStatsRecorder.java
@@ -26,6 +26,7 @@ import android.net.NetworkStats.NonMonotonicObserver;
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
import android.net.TrafficStats;
+import android.os.DropBoxManager;
import android.util.Log;
import android.util.MathUtils;
import android.util.Slog;
@@ -34,6 +35,7 @@ import com.android.internal.util.FileRotator;
import com.android.internal.util.IndentingPrintWriter;
import com.google.android.collect.Sets;
+import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
@@ -43,6 +45,8 @@ import java.lang.ref.WeakReference;
import java.util.HashSet;
import java.util.Map;
+import libcore.io.IoUtils;
+
/**
* Logic to record deltas between periodic {@link NetworkStats} snapshots into
* {@link NetworkStatsHistory} that belong to {@link NetworkStatsCollection}.
@@ -56,8 +60,14 @@ public class NetworkStatsRecorder {
private static final boolean LOGD = false;
private static final boolean LOGV = false;
+ private static final String TAG_NETSTATS_DUMP = "netstats_dump";
+
+ /** Dump before deleting in {@link #recoverFromWtf()}. */
+ private static final boolean DUMP_BEFORE_DELETE = true;
+
private final FileRotator mRotator;
private final NonMonotonicObserver<String> mObserver;
+ private final DropBoxManager mDropBox;
private final String mCookie;
private final long mBucketDuration;
@@ -74,9 +84,10 @@ public class NetworkStatsRecorder {
private WeakReference<NetworkStatsCollection> mComplete;
public NetworkStatsRecorder(FileRotator rotator, NonMonotonicObserver<String> observer,
- String cookie, long bucketDuration, boolean onlyTags) {
+ DropBoxManager dropBox, String cookie, long bucketDuration, boolean onlyTags) {
mRotator = checkNotNull(rotator, "missing FileRotator");
mObserver = checkNotNull(observer, "missing NonMonotonicObserver");
+ mDropBox = checkNotNull(dropBox, "missing DropBoxManager");
mCookie = cookie;
mBucketDuration = bucketDuration;
@@ -122,6 +133,7 @@ public class NetworkStatsRecorder {
mComplete = new WeakReference<NetworkStatsCollection>(complete);
} catch (IOException e) {
Log.wtf(TAG, "problem completely reading network stats", e);
+ recoverFromWtf();
}
}
return complete;
@@ -212,6 +224,7 @@ public class NetworkStatsRecorder {
mPending.reset();
} catch (IOException e) {
Log.wtf(TAG, "problem persisting pending stats", e);
+ recoverFromWtf();
}
}
}
@@ -226,6 +239,7 @@ public class NetworkStatsRecorder {
mRotator.rewriteAll(new RemoveUidRewriter(mBucketDuration, uid));
} catch (IOException e) {
Log.wtf(TAG, "problem removing UID " + uid, e);
+ recoverFromWtf();
}
// clear UID from current stats snapshot
@@ -355,4 +369,25 @@ public class NetworkStatsRecorder {
mSinceBoot.dump(pw);
}
}
+
+ /**
+ * Recover from {@link FileRotator} failure by dumping state to
+ * {@link DropBoxManager} and deleting contents.
+ */
+ private void recoverFromWtf() {
+ if (DUMP_BEFORE_DELETE) {
+ final ByteArrayOutputStream os = new ByteArrayOutputStream();
+ try {
+ mRotator.dumpAll(os);
+ } catch (IOException e) {
+ // ignore partial contents
+ os.reset();
+ } finally {
+ IoUtils.closeQuietly(os);
+ }
+ mDropBox.addData(TAG_NETSTATS_DUMP, os.toByteArray(), 0);
+ }
+
+ mRotator.deleteAll();
+ }
}
diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java
index 1a56b80..e710b33 100644
--- a/services/java/com/android/server/net/NetworkStatsService.java
+++ b/services/java/com/android/server/net/NetworkStatsService.java
@@ -338,9 +338,11 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
private NetworkStatsRecorder buildRecorder(
String prefix, NetworkStatsSettings.Config config, boolean includeTags) {
+ final DropBoxManager dropBox = (DropBoxManager) mContext.getSystemService(
+ Context.DROPBOX_SERVICE);
return new NetworkStatsRecorder(new FileRotator(
mBaseDir, prefix, config.rotateAgeMillis, config.deleteAgeMillis),
- mNonMonotonicObserver, prefix, config.bucketDuration, includeTags);
+ mNonMonotonicObserver, dropBox, prefix, config.bucketDuration, includeTags);
}
private void shutdownLocked() {