diff options
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() { |