Loading core/java/com/android/internal/util/FileRotator.java +50 −13 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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 Loading Loading @@ -137,12 +138,40 @@ 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); 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); } } /** * Process currently active file, first reading any existing data, then Loading @@ -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); } Loading Loading @@ -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 { Loading @@ -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); } } } Loading Loading @@ -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. Loading core/tests/coretests/src/com/android/internal/util/FileRotatorTest.java +2 −2 Original line number Diff line number Diff line Loading @@ -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 } Loading services/java/com/android/server/net/NetworkStatsRecorder.java +36 −1 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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}. Loading @@ -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; Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -212,6 +224,7 @@ public class NetworkStatsRecorder { mPending.reset(); } catch (IOException e) { Log.wtf(TAG, "problem persisting pending stats", e); recoverFromWtf(); } } } Loading @@ -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 Loading Loading @@ -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(); } } services/java/com/android/server/net/NetworkStatsService.java +3 −1 Original line number Diff line number Diff line Loading @@ -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() { Loading Loading
core/java/com/android/internal/util/FileRotator.java +50 −13 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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 Loading Loading @@ -137,12 +138,40 @@ 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); 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); } } /** * Process currently active file, first reading any existing data, then Loading @@ -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); } Loading Loading @@ -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 { Loading @@ -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); } } } Loading Loading @@ -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. Loading
core/tests/coretests/src/com/android/internal/util/FileRotatorTest.java +2 −2 Original line number Diff line number Diff line Loading @@ -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 } Loading
services/java/com/android/server/net/NetworkStatsRecorder.java +36 −1 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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}. Loading @@ -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; Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -212,6 +224,7 @@ public class NetworkStatsRecorder { mPending.reset(); } catch (IOException e) { Log.wtf(TAG, "problem persisting pending stats", e); recoverFromWtf(); } } } Loading @@ -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 Loading Loading @@ -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(); } }
services/java/com/android/server/net/NetworkStatsService.java +3 −1 Original line number Diff line number Diff line Loading @@ -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() { Loading