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;
21
22import com.google.common.annotations.VisibleForTesting;
23
24import java.util.List;
25import java.util.regex.Pattern;
26
27public class LogUtils {
28
29    public static final String TAG = LogTag.getLogTag();
30
31    // "GMT" + "+" or "-" + 4 digits
32    private static final Pattern DATE_CLEANUP_PATTERN_WRONG_TIMEZONE =
33            Pattern.compile("GMT([-+]\\d{4})$");
34
35    private static final String ACCOUNT_PREFIX = "account:";
36
37    /**
38     * Priority constant for the println method; use LogUtils.v.
39     */
40    public static final int VERBOSE = Log.VERBOSE;
41
42    /**
43     * Priority constant for the println method; use LogUtils.d.
44     */
45    public static final int DEBUG = Log.DEBUG;
46
47    /**
48     * Priority constant for the println method; use LogUtils.i.
49     */
50    public static final int INFO = Log.INFO;
51
52    /**
53     * Priority constant for the println method; use LogUtils.w.
54     */
55    public static final int WARN = Log.WARN;
56
57    /**
58     * Priority constant for the println method; use LogUtils.e.
59     */
60    public static final int ERROR = Log.ERROR;
61
62    /**
63     * Used to enable/disable logging that we don't want included in
64     * production releases.  This should be set to DEBUG for production releases, and VERBOSE for
65     * internal builds.
66     */
67    private static final int MAX_ENABLED_LOG_LEVEL = DEBUG;
68
69    private static Boolean sDebugLoggingEnabledForTests = null;
70
71    /**
72     * Enable debug logging for unit tests.
73     */
74    @VisibleForTesting
75    public static void setDebugLoggingEnabledForTests(boolean enabled) {
76        setDebugLoggingEnabledForTestsInternal(enabled);
77    }
78
79    protected static void setDebugLoggingEnabledForTestsInternal(boolean enabled) {
80        sDebugLoggingEnabledForTests = Boolean.valueOf(enabled);
81    }
82
83    /**
84     * Returns true if the build configuration prevents debug logging.
85     */
86    @VisibleForTesting
87    public static boolean buildPreventsDebugLogging() {
88        return MAX_ENABLED_LOG_LEVEL > VERBOSE;
89    }
90
91    /**
92     * Returns a boolean indicating whether debug logging is enabled.
93     */
94    protected static boolean isDebugLoggingEnabled(String tag) {
95        if (buildPreventsDebugLogging()) {
96            return false;
97        }
98        if (sDebugLoggingEnabledForTests != null) {
99            return sDebugLoggingEnabledForTests.booleanValue();
100        }
101        return Log.isLoggable(tag, Log.DEBUG) || Log.isLoggable(TAG, Log.DEBUG);
102    }
103
104    /**
105     * Returns a String for the specified content provider uri.  This will do
106     * sanitation of the uri to remove PII if debug logging is not enabled.
107     */
108    public static String contentUriToString(final Uri uri) {
109        return contentUriToString(TAG, uri);
110    }
111
112    /**
113     * Returns a String for the specified content provider uri.  This will do
114     * sanitation of the uri to remove PII if debug logging is not enabled.
115     */
116    public static String contentUriToString(String tag, Uri uri) {
117        if (isDebugLoggingEnabled(tag)) {
118            // Debug logging has been enabled, so log the uri as is
119            return uri.toString();
120        } else {
121            // Debug logging is not enabled, we want to remove the email address from the uri.
122            List<String> pathSegments = uri.getPathSegments();
123
124            Uri.Builder builder = new Uri.Builder()
125                    .scheme(uri.getScheme())
126                    .authority(uri.getAuthority())
127                    .query(uri.getQuery())
128                    .fragment(uri.getFragment());
129
130            // This assumes that the first path segment is the account
131            final String account = pathSegments.get(0);
132
133            builder = builder.appendPath(sanitizeAccountName(account));
134            for (int i = 1; i < pathSegments.size(); i++) {
135                builder.appendPath(pathSegments.get(i));
136            }
137            return builder.toString();
138        }
139    }
140
141    /**
142     * Sanitizes an account name.  If debug logging is not enabled, a sanitized name
143     * is returned.
144     */
145    public static String sanitizeAccountName(String accountName) {
146        if (TextUtils.isEmpty(accountName)) {
147            return "";
148        }
149
150        return ACCOUNT_PREFIX + sanitizeName(TAG, accountName);
151    }
152
153    public static String sanitizeName(final String tag, final String name) {
154        if (TextUtils.isEmpty(name)) {
155            return "";
156        }
157
158        if (isDebugLoggingEnabled(tag)) {
159            return name;
160        }
161
162        return String.valueOf(name.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) || 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