diff options
author | Sunny Goyal <sunnygoyal@google.com> | 2016-05-09 21:06:04 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2016-05-09 21:06:04 +0000 |
commit | 34a2d31f3208c64f6ebf403f8a0cb6ea96461747 (patch) | |
tree | bca629b65bf2277d429dbf21fd4e940c9f3a871d /src/com/android | |
parent | ac6d3a87ae9a31873a34f3eae01979910740ade1 (diff) | |
parent | 713edfce264db7edc409216d5c083f8dd6a7083f (diff) | |
download | android_packages_apps_Trebuchet-34a2d31f3208c64f6ebf403f8a0cb6ea96461747.tar.gz android_packages_apps_Trebuchet-34a2d31f3208c64f6ebf403f8a0cb6ea96461747.tar.bz2 android_packages_apps_Trebuchet-34a2d31f3208c64f6ebf403f8a0cb6ea96461747.zip |
Merge "Adding a utility class for persistant logging." into ub-launcher3-calgary
Diffstat (limited to 'src/com/android')
-rw-r--r-- | src/com/android/launcher3/Launcher.java | 28 | ||||
-rw-r--r-- | src/com/android/launcher3/LauncherAppState.java | 2 | ||||
-rw-r--r-- | src/com/android/launcher3/LauncherModel.java | 27 | ||||
-rw-r--r-- | src/com/android/launcher3/Utilities.java | 14 | ||||
-rw-r--r-- | src/com/android/launcher3/config/ProviderConfig.java | 2 | ||||
-rw-r--r-- | src/com/android/launcher3/logging/FileLog.java | 211 |
6 files changed, 245 insertions, 39 deletions
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index d2d1d02cc..21adcb7e1 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -114,6 +114,7 @@ import com.android.launcher3.logging.UserEventDispatcher; import com.android.launcher3.model.WidgetsModel; import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.util.ComponentKey; +import com.android.launcher3.logging.FileLog; import com.android.launcher3.util.TestingUtils; import com.android.launcher3.util.Thunk; import com.android.launcher3.util.ViewOnDrawExecutor; @@ -123,11 +124,9 @@ import com.android.launcher3.widget.WidgetsContainerView; import java.io.FileDescriptor; import java.io.PrintWriter; -import java.text.DateFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -307,11 +306,6 @@ public class Launcher extends Activity private final ArrayList<Integer> mSynchronouslyBoundPages = new ArrayList<Integer>(); private static final boolean DISABLE_SYNCHRONOUS_BINDING_CURRENT_PAGE = false; - private static final ArrayList<String> sDumpLogs = new ArrayList<String>(); - private static final Date sDateStamp = new Date(); - private static final DateFormat sDateFormat = - DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT); - // We only want to get the SharedPreferences once since it does an FS stat each time we get // it from the context. private SharedPreferences mSharedPrefs; @@ -3979,7 +3973,7 @@ public class Launcher extends Activity // Verify that we own the widget if (appWidgetInfo == null) { - Log.e(TAG, "Removing invalid widget: id=" + item.appWidgetId); + FileLog.e(TAG, "Removing invalid widget: id=" + item.appWidgetId); deleteWidgetInfo(item); return; } @@ -4652,12 +4646,10 @@ public class Launcher extends Activity } } - synchronized (sDumpLogs) { - writer.println(); - writer.println(prefix + "Debug logs"); - for (String log : sDumpLogs) { - writer.println(prefix + " " + log); - } + try { + FileLog.flushAll(writer); + } catch (Exception e) { + // Ignore } if (mLauncherCallbacks != null) { @@ -4665,14 +4657,6 @@ public class Launcher extends Activity } } - public static void addDumpLog(String tag, String log) { - Log.d(tag, log); - synchronized(sDumpLogs) { - sDateStamp.setTime(System.currentTimeMillis()); - sDumpLogs.add(sDateFormat.format(sDateStamp) + ": " + tag + ", " + log); - } - } - public static CustomAppWidget getCustomAppWidget(String name) { return sCustomAppWidgets.get(name); } diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java index f84e4b5b4..0fe639839 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -30,6 +30,7 @@ import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.dynamicui.ExtractionUtils; import com.android.launcher3.util.ConfigMonitor; +import com.android.launcher3.logging.FileLog; import com.android.launcher3.util.TestingUtils; import com.android.launcher3.util.Thunk; @@ -79,6 +80,7 @@ public class LauncherAppState { // is the first component to get created. Initializing application context here ensures // that LauncherAppState always exists in the main process. sContext = provider.getContext().getApplicationContext(); + FileLog.setDir(sContext.getFilesDir()); } private LauncherAppState() { diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 884685c8a..2fd12fd57 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -60,6 +60,7 @@ import com.android.launcher3.model.GridSizeMigrationTask; import com.android.launcher3.model.WidgetsModel; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.CursorIconInfo; +import com.android.launcher3.logging.FileLog; import com.android.launcher3.util.FlagOp; import com.android.launcher3.util.LongArrayMap; import com.android.launcher3.util.ManagedProfileHeuristic; @@ -1335,7 +1336,7 @@ public class LauncherModel extends BroadcastReceiver try { screenIds.add(sc.getLong(idIndex)); } catch (Exception e) { - addDumpLog("Invalid screen id: " + e); + FileLog.d(TAG, "Invalid screen id", e); } } } finally { @@ -1813,7 +1814,7 @@ public class LauncherModel extends BroadcastReceiver if (intent == null) { // The app is installed but the component is no // longer available. - addDumpLog("Invalid component removed: " + cn); + FileLog.d(TAG, "Invalid component removed: " + cn); itemsToRemove.add(id); continue; } else { @@ -1824,7 +1825,7 @@ public class LauncherModel extends BroadcastReceiver } else if (restored) { // Package is not yet available but might be // installed later. - addDumpLog("package not yet restored: " + cn); + FileLog.d(TAG, "package not yet restored: " + cn); if ((promiseType & ShortcutInfo.FLAG_RESTORE_STARTED) != 0) { // Restore has started once. @@ -1850,12 +1851,12 @@ public class LauncherModel extends BroadcastReceiver itemReplaced = true; } else if (REMOVE_UNRESTORED_ICONS) { - addDumpLog("Unrestored package removed: " + cn); + FileLog.d(TAG, "Unrestored package removed: " + cn); itemsToRemove.add(id); continue; } } else if (REMOVE_UNRESTORED_ICONS) { - addDumpLog("Unrestored package removed: " + cn); + FileLog.d(TAG, "Unrestored package removed: " + cn); itemsToRemove.add(id); continue; } @@ -1880,7 +1881,7 @@ public class LauncherModel extends BroadcastReceiver } else { // Do not wait for external media load anymore. // Log the invalid package, and remove it - addDumpLog("Invalid package removed: " + cn); + FileLog.d(TAG, "Invalid package removed: " + cn); itemsToRemove.add(id); continue; } @@ -1890,7 +1891,7 @@ public class LauncherModel extends BroadcastReceiver restored = false; } } catch (URISyntaxException e) { - addDumpLog("Invalid uri: " + intentDescription); + FileLog.d(TAG, "Invalid uri: " + intentDescription); itemsToRemove.add(id); continue; } @@ -2073,7 +2074,7 @@ public class LauncherModel extends BroadcastReceiver final boolean isProviderReady = isValidProvider(provider); if (!isSafeMode && !customWidget && wasProviderReady && !isProviderReady) { - addDumpLog("Deleting widget that isn't installed anymore: " + FileLog.d(TAG, "Deleting widget that isn't installed anymore: " + provider); itemsToRemove.add(id); } else { @@ -2115,7 +2116,7 @@ public class LauncherModel extends BroadcastReceiver appWidgetInfo.restoreStatus |= LauncherAppWidgetInfo.FLAG_RESTORE_STARTED; } else if (REMOVE_UNRESTORED_ICONS && !isSafeMode) { - addDumpLog("Unrestored widget removed: " + component); + FileLog.d(TAG, "Unrestored widget removed: " + component); itemsToRemove.add(id); continue; } @@ -2171,9 +2172,7 @@ public class LauncherModel extends BroadcastReceiver } } } finally { - if (c != null) { - c.close(); - } + Utilities.closeSilently(c); } // Break early if we've stopped loading @@ -3541,8 +3540,4 @@ public class LauncherModel extends BroadcastReceiver public static Looper getWorkerLooper() { return sWorkerThread.getLooper(); } - - @Thunk static final void addDumpLog(String log) { - Launcher.addDumpLog(TAG, log); - } } diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index 1acbfc12b..871f39045 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -63,9 +63,11 @@ import android.widget.Toast; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.config.ProviderConfig; import com.android.launcher3.util.IconNormalizer; import java.io.ByteArrayOutputStream; +import java.io.Closeable; import java.io.IOException; import java.lang.reflect.Method; import java.util.ArrayList; @@ -845,6 +847,18 @@ public final class Utilities { return true; } + public static void closeSilently(Closeable c) { + if (c != null) { + try { + c.close(); + } catch (IOException e) { + if (ProviderConfig.IS_DOGFOOD_BUILD) { + Log.d(TAG, "Error closing", e); + } + } + } + } + /** * An extension of {@link BitmapDrawable} which returns the bitmap pixel size as intrinsic size. * This allows the badging to be done based on the action bitmap size rather than diff --git a/src/com/android/launcher3/config/ProviderConfig.java b/src/com/android/launcher3/config/ProviderConfig.java index 825b43422..1d964b1b2 100644 --- a/src/com/android/launcher3/config/ProviderConfig.java +++ b/src/com/android/launcher3/config/ProviderConfig.java @@ -20,5 +20,5 @@ public class ProviderConfig { public static final String AUTHORITY = "com.android.launcher3.settings".intern(); - public static boolean IS_DOGFOOD_BUILD = false; + public static boolean IS_DOGFOOD_BUILD = true; } diff --git a/src/com/android/launcher3/logging/FileLog.java b/src/com/android/launcher3/logging/FileLog.java new file mode 100644 index 000000000..f82269538 --- /dev/null +++ b/src/com/android/launcher3/logging/FileLog.java @@ -0,0 +1,211 @@ +package com.android.launcher3.logging; + +import android.os.Handler; +import android.os.Message; +import android.util.Log; +import android.util.Pair; + +import com.android.launcher3.LauncherModel; +import com.android.launcher3.Utilities; +import com.android.launcher3.config.ProviderConfig; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.PrintWriter; +import java.text.DateFormat; +import java.util.Calendar; +import java.util.Date; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** + * Wrapper around {@link Log} to allow writing to a file. + * This class can safely be called from main thread. + */ +public final class FileLog { + + private static final String FILE_NAME_PREFIX = "log-"; + private static final DateFormat DATE_FORMAT = + DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT); + + private static final long MAX_LOG_FILE_SIZE = 4 << 20; // 4 mb + + private static Handler sHandler = null; + private static File sLogsDirectory = null; + + public static void setDir(File logsDir) { + sLogsDirectory = logsDir; + } + + public static void d(String tag, String msg, Exception e) { + Log.d(tag, msg, e); + print(tag, msg, e); + } + + public static void d(String tag, String msg) { + Log.d(tag, msg); + print(tag, msg); + } + + public static void e(String tag, String msg, Exception e) { + Log.e(tag, msg, e); + print(tag, msg, e); + } + + public static void e(String tag, String msg) { + Log.e(tag, msg); + print(tag, msg); + } + + public static void print(String tag, String msg) { + print(tag, msg, null); + } + + public static void print(String tag, String msg, Exception e) { + if (!ProviderConfig.IS_DOGFOOD_BUILD) { + return; + } + String out = String.format("%s %s %s", DATE_FORMAT.format(new Date()), tag, msg); + if (e != null) { + out += "\n" + Log.getStackTraceString(e); + } + Message.obtain(getHandler(), LogWriterCallback.MSG_WRITE, out).sendToTarget(); + } + + private static Handler getHandler() { + synchronized (DATE_FORMAT) { + if (sHandler == null) { + // We can use any non-ui looper, but why create another just for logging! + sHandler = new Handler(LauncherModel.getWorkerLooper(), new LogWriterCallback()); + } + } + return sHandler; + } + + /** + * Blocks until all the pending logs are written to the disk + * @param out if not null, all the persisted logs are copied to the writer. + */ + public static void flushAll(PrintWriter out) throws InterruptedException { + if (!ProviderConfig.IS_DOGFOOD_BUILD) { + return; + } + CountDownLatch latch = new CountDownLatch(1); + Message.obtain(getHandler(), LogWriterCallback.MSG_FLUSH, + Pair.create(out, latch)).sendToTarget(); + + latch.await(2, TimeUnit.SECONDS); + } + + /** + * Writes logs to the file. + * Log files are named log-0 for even days of the year and log-1 for odd days of the year. + * Logs older than 36 hours are purged. + */ + private static class LogWriterCallback implements Handler.Callback { + + private static final long CLOSE_DELAY = 5000; // 5 seconds + + private static final int MSG_WRITE = 1; + private static final int MSG_CLOSE = 2; + private static final int MSG_FLUSH = 3; + + private String mCurrentFileName = null; + private PrintWriter mCurrentWriter = null; + + private void closeWriter() { + Utilities.closeSilently(mCurrentWriter); + mCurrentWriter = null; + } + + @Override + public boolean handleMessage(Message msg) { + if (sLogsDirectory == null || !ProviderConfig.IS_DOGFOOD_BUILD) { + return true; + } + switch (msg.what) { + case MSG_WRITE: { + Calendar cal = Calendar.getInstance(); + // suffix with 0 or 1 based on the day of the year. + String fileName = FILE_NAME_PREFIX + (cal.get(Calendar.DAY_OF_YEAR) & 1); + + if (!fileName.equals(mCurrentFileName)) { + closeWriter(); + } + + try { + if (mCurrentWriter == null) { + mCurrentFileName = fileName; + + boolean append = false; + File logFile = new File(sLogsDirectory, fileName); + if (logFile.exists()) { + Calendar modifiedTime = Calendar.getInstance(); + modifiedTime.setTimeInMillis(logFile.lastModified()); + + // If the file was modified more that 36 hours ago, purge the file. + // We use instead of 24 to account for day-365 followed by day-1 + modifiedTime.add(Calendar.HOUR, 36); + append = cal.before(modifiedTime) + && logFile.length() < MAX_LOG_FILE_SIZE; + } + mCurrentWriter = new PrintWriter(new FileWriter(logFile, append)); + } + + mCurrentWriter.println((String) msg.obj); + mCurrentWriter.flush(); + + // Auto close file stream after some time. + sHandler.removeMessages(MSG_CLOSE); + sHandler.sendEmptyMessageDelayed(MSG_CLOSE, CLOSE_DELAY); + } catch (Exception e) { + Log.e("FileLog", "Error writing logs to file", e); + // Close stream, will try reopening during next log + closeWriter(); + } + return true; + } + case MSG_CLOSE: { + closeWriter(); + return true; + } + case MSG_FLUSH: { + closeWriter(); + Pair<PrintWriter, CountDownLatch> p = + (Pair<PrintWriter, CountDownLatch>) msg.obj; + + if (p.first != null) { + dumpFile(p.first, FILE_NAME_PREFIX + 0); + dumpFile(p.first, FILE_NAME_PREFIX + 1); + } + p.second.countDown(); + return true; + } + } + return true; + } + } + + private static void dumpFile(PrintWriter out, String fileName) { + File logFile = new File(sLogsDirectory, fileName); + if (logFile.exists()) { + + BufferedReader in = null; + try { + in = new BufferedReader(new FileReader(logFile)); + out.println(); + out.println("--- logfile: " + fileName + " ---"); + String line; + while ((line = in.readLine()) != null) { + out.println(line); + } + } catch (Exception e) { + // ignore + } finally { + Utilities.closeSilently(in); + } + } + } +} |