1/**
2 * Copyright (c) 2011, Google Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package com.android.mail.utils;
17
18import android.net.Uri;
19import android.text.TextUtils;
20import android.util.Log;
21import com.google.common.annotations.VisibleForTesting;
22
23import java.util.List;
24import java.util.regex.Pattern;
25
26public class LogUtils {
27
28    // "GMT" + "+" or "-" + 4 digits
29    private static final Pattern DATE_CLEANUP_PATTERN_WRONG_TIMEZONE =
30            Pattern.compile("GMT([-+]\\d{4})$");
31    private static String LOG_TAG = "UnifiedEmail";
32
33    /**
34     * Get the log tag to apply to logging.
35     */
36    public String getLogTag() {
37        return LOG_TAG;
38    }
39
40    /**
41     * Priority constant for the println method; use LogUtils.v.
42     */
43    public static final int VERBOSE = Log.VERBOSE;
44
45    /**
46     * Priority constant for the println method; use LogUtils.d.
47     */
48    public static final int DEBUG = Log.DEBUG;
49
50    /**
51     * Priority constant for the println method; use LogUtils.i.
52     */
53    public static final int INFO = Log.INFO;
54
55    /**
56     * Priority constant for the println method; use LogUtils.w.
57     */
58    public static final int WARN = Log.WARN;
59
60    /**
61     * Priority constant for the println method; use LogUtils.e.
62     */
63    public static final int ERROR = Log.ERROR;
64
65    /**
66     * Used to enable/disable logging that we don't want included in
67     * production releases.
68     */
69    private static final int MAX_ENABLED_LOG_LEVEL = VERBOSE;
70
71    private static Boolean sDebugLoggingEnabledForTests = null;
72
73    /**
74     * Enable debug logging for unit tests.
75     */
76    @VisibleForTesting
77    static void setDebugLoggingEnabledForTests(boolean enabled) {
78        sDebugLoggingEnabledForTests = Boolean.valueOf(enabled);
79    }
80
81    /**
82     * Returns true if the build configuration prevents debug logging.
83     */
84    @VisibleForTesting
85    public static boolean buildPreventsDebugLogging() {
86        return MAX_ENABLED_LOG_LEVEL > VERBOSE;
87    }
88
89    /**
90     * Returns a boolean indicating whether debug logging is enabled.
91     */
92    protected static boolean isDebugLoggingEnabled() {
93        if (buildPreventsDebugLogging()) {
94            return false;
95        }
96        if (sDebugLoggingEnabledForTests != null) {
97            return sDebugLoggingEnabledForTests.booleanValue();
98        }
99        return Log.isLoggable(LOG_TAG, Log.DEBUG);
100    }
101
102    /**
103     * Returns a String for the specified content provider uri.  This will do
104     * sanitation of the uri to remove PII if debug logging is not enabled.
105     */
106    public static String contentUriToString(Uri uri) {
107
108        if (isDebugLoggingEnabled()) {
109            // Debug logging has been enabled, so log the uri as is
110            return uri.toString();
111        } else {
112            // Debug logging is not enabled, we want to remove the email address from the uri.
113            List<String> pathSegments = uri.getPathSegments();
114
115            Uri.Builder builder = new Uri.Builder()
116                    .scheme(uri.getScheme())
117                    .authority(uri.getAuthority())
118                    .query(uri.getQuery())
119                    .fragment(uri.getFragment());
120
121            // This assumes that the first path segment is the account
122            final String account = pathSegments.get(0);
123
124            builder = builder.appendPath(String.valueOf(account.hashCode()));
125            for (int i = 1; i < pathSegments.size(); i++) {
126                builder.appendPath(pathSegments.get(i));
127            }
128            return builder.toString();
129        }
130    }
131
132   /* TODO: what is the correct behavior for base case and the Gmail case? Seems like this
133    * belongs in override code in UnifiedGmail.
134    *Converts the specified set of labels to a string, and removes any PII as necessary
135    * public static String labelSetToString(Set<String> labelSet) {
136        if (isDebugLoggingEnabled() || labelSet == null) {
137            return labelSet != null ? labelSet.toString() : "";
138        } else {
139            final StringBuilder builder = new StringBuilder("[");
140            int i = 0;
141            for(String label : labelSet) {
142                if (i > 0) {
143                    builder.append(", ");
144                }
145                builder.append(sanitizeLabelName(label));
146                i++;
147            }
148            builder.append(']');
149            return builder.toString();
150        }
151    }
152
153    private static String sanitizeLabelName(String canonicalName) {
154        if (TextUtils.isEmpty(canonicalName)) {
155            return "";
156        }
157
158        if (Gmail.isSystemLabel(canonicalName)) {
159            return canonicalName;
160        }
161
162        return USER_LABEL_PREFIX + String.valueOf(canonicalName.hashCode());
163    }*/
164
165    /**
166     * Checks to see whether or not a log for the specified tag is loggable at the specified level.
167     */
168    public static boolean isLoggable(String tag, int level) {
169        if (MAX_ENABLED_LOG_LEVEL > level) {
170            return false;
171        }
172        return Log.isLoggable(tag, level);
173    }
174
175    /**
176     * Send a {@link #VERBOSE} log message.
177     * @param tag Used to identify the source of a log message.  It usually identifies
178     *        the class or activity where the log call occurs.
179     * @param format the format string (see {@link java.util.Formatter#format})
180     * @param args
181     *            the list of arguments passed to the formatter. If there are
182     *            more arguments than required by {@code format},
183     *            additional arguments are ignored.
184     */
185    public static int v(String tag, String format, Object... args) {
186        if (isLoggable(tag, VERBOSE)) {
187            return Log.v(tag, String.format(format, args));
188        }
189        return 0;
190    }
191
192    /**
193     * Send a {@link #VERBOSE} log message.
194     * @param tag Used to identify the source of a log message.  It usually identifies
195     *        the class or activity where the log call occurs.
196     * @param tr An exception to log
197     * @param format the format string (see {@link java.util.Formatter#format})
198     * @param args
199     *            the list of arguments passed to the formatter. If there are
200     *            more arguments than required by {@code format},
201     *            additional arguments are ignored.
202     */
203    public static int v(String tag, Throwable tr, String format, Object... args) {
204        if (isLoggable(tag, VERBOSE)) {
205            return Log.v(tag, String.format(format, args), tr);
206        }
207        return 0;
208    }
209
210    /**
211     * Send a {@link #DEBUG} log message.
212     * @param tag Used to identify the source of a log message.  It usually identifies
213     *        the class or activity where the log call occurs.
214     * @param format the format string (see {@link java.util.Formatter#format})
215     * @param args
216     *            the list of arguments passed to the formatter. If there are
217     *            more arguments than required by {@code format},
218     *            additional arguments are ignored.
219     */
220    public static int d(String tag, String format, Object... args) {
221        if (isLoggable(tag, DEBUG)) {
222            return Log.d(tag, String.format(format, args));
223        }
224        return 0;
225    }
226
227    /**
228     * Send a {@link #DEBUG} log message.
229     * @param tag Used to identify the source of a log message.  It usually identifies
230     *        the class or activity where the log call occurs.
231     * @param tr An exception to log
232     * @param format the format string (see {@link java.util.Formatter#format})
233     * @param args
234     *            the list of arguments passed to the formatter. If there are
235     *            more arguments than required by {@code format},
236     *            additional arguments are ignored.
237     */
238    public static int d(String tag, Throwable tr, String format, Object... args) {
239        if (isLoggable(tag, DEBUG)) {
240            return Log.d(tag, String.format(format, args), tr);
241        }
242        return 0;
243    }
244
245    /**
246     * Send a {@link #INFO} log message.
247     * @param tag Used to identify the source of a log message.  It usually identifies
248     *        the class or activity where the log call occurs.
249     * @param format the format string (see {@link java.util.Formatter#format})
250     * @param args
251     *            the list of arguments passed to the formatter. If there are
252     *            more arguments than required by {@code format},
253     *            additional arguments are ignored.
254     */
255    public static int i(String tag, String format, Object... args) {
256        if (isLoggable(tag, INFO)) {
257            return Log.i(tag, String.format(format, args));
258        }
259        return 0;
260    }
261
262    /**
263     * Send a {@link #INFO} log message.
264     * @param tag Used to identify the source of a log message.  It usually identifies
265     *        the class or activity where the log call occurs.
266     * @param tr An exception to log
267     * @param format the format string (see {@link java.util.Formatter#format})
268     * @param args
269     *            the list of arguments passed to the formatter. If there are
270     *            more arguments than required by {@code format},
271     *            additional arguments are ignored.
272     */
273    public static int i(String tag, Throwable tr, String format, Object... args) {
274        if (isLoggable(tag, INFO)) {
275            return Log.i(tag, String.format(format, args), tr);
276        }
277        return 0;
278    }
279
280    /**
281     * Send a {@link #WARN} log message.
282     * @param tag Used to identify the source of a log message.  It usually identifies
283     *        the class or activity where the log call occurs.
284     * @param format the format string (see {@link java.util.Formatter#format})
285     * @param args
286     *            the list of arguments passed to the formatter. If there are
287     *            more arguments than required by {@code format},
288     *            additional arguments are ignored.
289     */
290    public static int w(String tag, String format, Object... args) {
291        if (isLoggable(tag, WARN)) {
292            return Log.w(tag, String.format(format, args));
293        }
294        return 0;
295    }
296
297    /**
298     * Send a {@link #WARN} log message.
299     * @param tag Used to identify the source of a log message.  It usually identifies
300     *        the class or activity where the log call occurs.
301     * @param tr An exception to log
302     * @param format the format string (see {@link java.util.Formatter#format})
303     * @param args
304     *            the list of arguments passed to the formatter. If there are
305     *            more arguments than required by {@code format},
306     *            additional arguments are ignored.
307     */
308    public static int w(String tag, Throwable tr, String format, Object... args) {
309        if (isLoggable(tag, WARN)) {
310            return Log.w(tag, String.format(format, args), tr);
311        }
312        return 0;
313    }
314
315    /**
316     * Send a {@link #ERROR} log message.
317     * @param tag Used to identify the source of a log message.  It usually identifies
318     *        the class or activity where the log call occurs.
319     * @param format the format string (see {@link java.util.Formatter#format})
320     * @param args
321     *            the list of arguments passed to the formatter. If there are
322     *            more arguments than required by {@code format},
323     *            additional arguments are ignored.
324     */
325    public static int e(String tag, String format, Object... args) {
326        if (isLoggable(tag, ERROR)) {
327            return Log.e(tag, String.format(format, args));
328        }
329        return 0;
330    }
331
332    /**
333     * Send a {@link #ERROR} log message.
334     * @param tag Used to identify the source of a log message.  It usually identifies
335     *        the class or activity where the log call occurs.
336     * @param tr An exception to log
337     * @param format the format string (see {@link java.util.Formatter#format})
338     * @param args
339     *            the list of arguments passed to the formatter. If there are
340     *            more arguments than required by {@code format},
341     *            additional arguments are ignored.
342     */
343    public static int e(String tag, Throwable tr, String format, Object... args) {
344        if (isLoggable(tag, ERROR)) {
345            return Log.e(tag, String.format(format, args), tr);
346        }
347        return 0;
348    }
349
350    /**
351     * What a Terrible Failure: Report a condition that should never happen.
352     * The error will always be logged at level ASSERT with the call stack.
353     * Depending on system configuration, a report may be added to the
354     * {@link android.os.DropBoxManager} and/or the process may be terminated
355     * immediately with an error dialog.
356     * @param tag Used to identify the source of a log message.  It usually identifies
357     *        the class or activity where the log call occurs.
358     * @param format the format string (see {@link java.util.Formatter#format})
359     * @param args
360     *            the list of arguments passed to the formatter. If there are
361     *            more arguments than required by {@code format},
362     *            additional arguments are ignored.
363     */
364    public static int wtf(String tag, String format, Object... args) {
365        return Log.wtf(tag, String.format(format, args), new Error());
366    }
367
368    /**
369     * What a Terrible Failure: Report a condition that should never happen.
370     * The error will always be logged at level ASSERT with the call stack.
371     * Depending on system configuration, a report may be added to the
372     * {@link android.os.DropBoxManager} and/or the process may be terminated
373     * immediately with an error dialog.
374     * @param tag Used to identify the source of a log message.  It usually identifies
375     *        the class or activity where the log call occurs.
376     * @param tr An exception to log
377     * @param format the format string (see {@link java.util.Formatter#format})
378     * @param args
379     *            the list of arguments passed to the formatter. If there are
380     *            more arguments than required by {@code format},
381     *            additional arguments are ignored.
382     */
383    public static int wtf(String tag, Throwable tr, String format, Object... args) {
384        return Log.wtf(tag, String.format(format, args), tr);
385    }
386
387
388    /**
389     * Try to make a date MIME(RFC 2822/5322)-compliant.
390     *
391     * It fixes:
392     * - "Thu, 10 Dec 09 15:08:08 GMT-0700" to "Thu, 10 Dec 09 15:08:08 -0700"
393     *   (4 digit zone value can't be preceded by "GMT")
394     *   We got a report saying eBay sends a date in this format
395     */
396    public static String cleanUpMimeDate(String date) {
397        if (TextUtils.isEmpty(date)) {
398            return date;
399        }
400        date = DATE_CLEANUP_PATTERN_WRONG_TIMEZONE.matcher(date).replaceFirst("$1");
401        return date;
402    }
403
404
405    public static String byteToHex(int b) {
406        return byteToHex(new StringBuilder(), b).toString();
407    }
408
409    public static StringBuilder byteToHex(StringBuilder sb, int b) {
410        b &= 0xFF;
411        sb.append("0123456789ABCDEF".charAt(b >> 4));
412        sb.append("0123456789ABCDEF".charAt(b & 0xF));
413        return sb;
414    }
415
416}
417