159f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal/******************************************************************************* 259f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal * Copyright (C) 2013 Google Inc. 359f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal * Licensed to The Android Open Source Project. 459f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal * 559f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal * Licensed under the Apache License, Version 2.0 (the "License"); 659f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal * you may not use this file except in compliance with the License. 759f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal * You may obtain a copy of the License at 859f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal * 959f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal * http://www.apache.org/licenses/LICENSE-2.0 1059f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal * 1159f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal * Unless required by applicable law or agreed to in writing, software 1259f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal * distributed under the License is distributed on an "AS IS" BASIS, 1359f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1459f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal * See the License for the specific language governing permissions and 1559f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal * limitations under the License. 1659f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal *******************************************************************************/ 1759f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal 1859f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwalpackage com.android.mail; 1959f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal 2059f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwalimport com.android.mail.utils.LogTag; 2159f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwalimport com.android.mail.utils.LogUtils; 2259f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal 2359f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwalimport android.app.Service; 2459f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwalimport android.content.Intent; 2559f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwalimport android.os.IBinder; 2659f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwalimport android.util.Pair; 2759f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal 2859f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwalimport java.io.FileDescriptor; 2959f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwalimport java.io.PrintWriter; 3059f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwalimport java.util.Date; 3159f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwalimport java.util.HashMap; 3259f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwalimport java.util.LinkedList; 3359f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwalimport java.util.Map; 3459f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwalimport java.util.Queue; 3559f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal 3659f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal/** 3759f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal * A write-only device for sensitive logs. Turned on only during debugging. 3859f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal * 3959f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal * Dump valuable system state by sending a local broadcast to the associated activity. 4059f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal * Broadcast receivers are responsible for dumping state as they see fit. 4159f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal * This service is only started when the log level is high, so there is no risk of user 4259f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal * data being logged by mistake. 4359f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal * 4459f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal * To add logging to this service, call {@link #log(String, String, Object...)} with a tag name, 4559f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal * which is a class name, like "AbstractActivityController", which is a unique ID. Then, add to the 4659f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal * resulting buffer any information of interest at logging time. This is kept in a ring buffer, 4759f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal * which is overwritten with new information. 4859f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal */ 4959f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwalpublic class MailLogService extends Service { 5059f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal /** 5159f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal * This is the top level flag that enables this service. 5259f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal */ 531fc5b00641a9a506439f3efc0f1063d0bd6167a3Vikram Aggarwal public static boolean DEBUG_ENABLED = false; 5459f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal 5559f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal /** The tag which needs to be turned to DEBUG to get logging going. */ 5659f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal protected static final String LOG_TAG = LogTag.getLogTag(); 5759f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal 5859f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal /** 5959f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal * A circular buffer of {@value #SIZE} lines. To insert into this buffer, 6059f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal * call the {@link #put(String)} method. To retrieve the most recent logs, 6159f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal * call the {@link #toString()} method. 6259f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal */ 6359f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal private static class CircularBuffer { 6459f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal // We accept fifty lines of input. 6559f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal public static final int SIZE = 50; 6659f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal /** The actual list of strings to be printed. */ 6759f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal final Queue<Pair<Long, String>> mList = new LinkedList<Pair<Long, String>>(); 6859f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal /** The current size of the buffer */ 6959f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal int mCurrentSize = 0; 7059f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal 7159f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal /** Create an empty log buffer. */ 7259f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal private CircularBuffer() { 7359f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal // Do nothing 7459f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal } 7559f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal 7659f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal /** Get the current timestamp */ 7759f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal private static String dateToString(long timestamp) { 7859f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal final Date d = new Date(timestamp); 7959f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal return String.format("%d-%d %d:%d:%d: ", d.getDay(), d.getMonth(), d.getHours(), 8059f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal d.getMinutes(), d.getSeconds()); 8159f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal } 8259f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal 8359f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal /** 8459f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal * Insert a log message into the buffer. This might evict the oldest message if the log 8559f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal * is at capacity. 8659f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal * @param message a log message for this buffer. 8759f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal */ 8859f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal private synchronized void put(String message) { 8959f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal if (mCurrentSize == SIZE) { 9059f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal // At capacity, we'll remove the head, and add to the tail. Size is unchanged. 9159f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal mList.remove(); 9259f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal } else { 9359f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal // Less than capacity. Adding a new element at the end. 9459f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal mCurrentSize++; 9559f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal } 9659f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal // Add the current timestamp along with the message. 9759f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal mList.add(new Pair<Long, String>(System.currentTimeMillis(), message)); 9859f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal } 9959f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal 10059f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal @Override 10159f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal public String toString() { 10259f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal final StringBuilder builder = new StringBuilder(); 10359f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal for (final Pair<Long, String> s : mList) { 10459f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal // Print the timestamp as an actual date, and then the message. 10559f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal builder.append(dateToString(s.first)); 10659f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal builder.append(s.second); 10759f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal // Put a newline at the end of each log line. 10859f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal builder.append("\n"); 10959f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal } 11059f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal return builder.toString(); 11159f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal } 11259f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal } 11359f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal 11459f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal /** Header printed at the start of the dump. */ 11559f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal private static final String HEADER = "**** MailLogService ***\n"; 11659f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal /** Map of current tag -> log. */ 11759f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal private static final Map<String, CircularBuffer> sLogs = new HashMap<String, CircularBuffer>(); 11859f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal 1199e2d407fdafeb874e640eb84017feaf784309075Scott Kennedy @Override 12059f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal public IBinder onBind(Intent intent) { 12159f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal return null; 12259f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal } 12359f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal 12459f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal /** 12559f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal * Return the circular buffer associated with this tag, or create a new buffer if none is 12659f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal * currently associated. 12759f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal * @param tag a string to identify a unique tag. 12859f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal * @return a circular buffer associated with a string tag. 12959f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal */ 13059f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal private static CircularBuffer getOrCreate(String tag) { 13159f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal if (sLogs.containsKey(tag)) { 13259f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal return sLogs.get(tag); 13359f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal } 13459f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal // Create a new CircularBuffer with this tag 13559f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal final CircularBuffer buffer = new CircularBuffer(); 13659f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal sLogs.put(tag, buffer); 13759f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal return buffer; 13859f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal } 13959f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal 14059f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal /** 14159f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal * Return true if the logging level is high enough for this service to function. 14259f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal * @return true if this service is functioning at the current log level. False otherwise. 14359f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal */ 14459f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal public static boolean isLoggingLevelHighEnough() { 145b184bfe96fa3512af88260fce4f3cee3066fb28dScott Kennedy return LogUtils.isLoggable(LOG_TAG, LogUtils.DEBUG); 14659f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal } 14759f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal 14859f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal /** 14959f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal * Add to the log for the tag given. 15059f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal * @param tag a unique tag to add the message to 15159f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal * @param format a string format for the message 15259f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal * @param args optional list of arguments for the format. 15359f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal */ 15459f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal public static void log(String tag, String format, Object... args) { 15559f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal if (!DEBUG_ENABLED || !isLoggingLevelHighEnough()) { 15659f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal return; 15759f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal } 15859f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal // The message we are printing. 15959f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal final String logMessage = String.format(format, args); 16059f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal // Find the circular buffer to go with this tag, or create a new one. 16159f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal getOrCreate(tag).put(logMessage); 16259f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal } 16359f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal 16459f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal @Override 16559f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) { 16659f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal if (!DEBUG_ENABLED) { 16759f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal return; 16859f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal } 16959f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal writer.print(HEADER); 17059f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal // Go through all the tags, and write them all out sequentially. 17159f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal for (final String tag : sLogs.keySet()) { 17259f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal // Write out a sub-header: Logging for tag "MyModuleName" 17359f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal writer.append("Logging for tag: \""); 17459f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal writer.append(tag); 17559f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal writer.append("\"\n"); 17659f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal 17759f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal writer.append(sLogs.get(tag).toString()); 17859f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal } 17959f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal // Go through all the buffers. 18059f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal super.dump(fd, writer,args); 18159f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal } 18259f741f60e389818a9a56b1fba46eea1c372690dVikram Aggarwal} 183