summaryrefslogtreecommitdiffstats
path: root/src/com/android
diff options
context:
space:
mode:
authorSunny Goyal <sunnygoyal@google.com>2016-05-09 21:06:04 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2016-05-09 21:06:04 +0000
commit34a2d31f3208c64f6ebf403f8a0cb6ea96461747 (patch)
treebca629b65bf2277d429dbf21fd4e940c9f3a871d /src/com/android
parentac6d3a87ae9a31873a34f3eae01979910740ade1 (diff)
parent713edfce264db7edc409216d5c083f8dd6a7083f (diff)
downloadandroid_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.java28
-rw-r--r--src/com/android/launcher3/LauncherAppState.java2
-rw-r--r--src/com/android/launcher3/LauncherModel.java27
-rw-r--r--src/com/android/launcher3/Utilities.java14
-rw-r--r--src/com/android/launcher3/config/ProviderConfig.java2
-rw-r--r--src/com/android/launcher3/logging/FileLog.java211
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);
+ }
+ }
+ }
+}