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    // STOPSHIP: ship with DEBUG set
68    private static final int MAX_ENABLED_LOG_LEVEL = VERBOSE;
69
70    private static Boolean sDebugLoggingEnabledForTests = null;
71
72    /**
73     * Enable debug logging for unit tests.
74     */
75    @VisibleForTesting
76    public static void setDebugLoggingEnabledForTests(boolean enabled) {
77        setDebugLoggingEnabledForTestsInternal(enabled);
78    }
79
80    protected static void setDebugLoggingEnabledForTestsInternal(boolean enabled) {
81        sDebugLoggingEnabledForTests = Boolean.valueOf(enabled);
82    }
83
84    /**
85     * Returns true if the build configuration prevents debug logging.
86     */
87    @VisibleForTesting
88    public static boolean buildPreventsDebugLogging() {
89        return MAX_ENABLED_LOG_LEVEL > VERBOSE;
90    }
91
92    /**
93     * Returns a boolean indicating whether debug logging is enabled.
94     */
95    protected static boolean isDebugLoggingEnabled(String tag) {
96        if (buildPreventsDebugLogging()) {
97            return false;
98        }
99        if (sDebugLoggingEnabledForTests != null) {
100            return sDebugLoggingEnabledForTests.booleanValue();
101        }
102        return Log.isLoggable(tag, Log.DEBUG) || Log.isLoggable(TAG, Log.DEBUG);
103    }
104
105    /**
106     * Returns a String for the specified content provider uri.  This will do
107     * sanitation of the uri to remove PII if debug logging is not enabled.
108     */
109    public static String contentUriToString(final Uri uri) {
110        return contentUriToString(TAG, uri);
111    }
112
113    /**
114     * Returns a String for the specified content provider uri.  This will do
115     * sanitation of the uri to remove PII if debug logging is not enabled.
116     */
117    public static String contentUriToString(String tag, Uri uri) {
118        if (isDebugLoggingEnabled(tag)) {
119            // Debug logging has been enabled, so log the uri as is
120            return uri.toString();
121        } else {
122            // Debug logging is not enabled, we want to remove the email address from the uri.
123            List<String> pathSegments = uri.getPathSegments();
124
125            Uri.Builder builder = new Uri.Builder()
126                    .scheme(uri.getScheme())
127                    .authority(uri.getAuthority())
128                    .query(uri.getQuery())
129                    .fragment(uri.getFragment());
130
131            // This assumes that the first path segment is the account
132            final String account = pathSegments.get(0);
133
134            builder = builder.appendPath(sanitizeAccountName(account));
135            for (int i = 1; i < pathSegments.size(); i++) {
136                builder.appendPath(pathSegments.get(i));
137            }
138            return builder.toString();
139        }
140    }
141
142    /**
143     * Sanitizes an account name.  If debug logging is not enabled, a sanitized name
144     * is returned.
145     */
146    public static String sanitizeAccountName(String accountName) {
147        if (TextUtils.isEmpty(accountName)) {
148            return "";
149        }
150
151        return ACCOUNT_PREFIX + sanitizeName(TAG, accountName);
152    }
153
154    public static String sanitizeName(final String tag, final String name) {
155        if (TextUtils.isEmpty(name)) {
156            return "";
157        }
158
159        if (isDebugLoggingEnabled(tag)) {
160            return name;
161        }
162
163        return String.valueOf(name.hashCode());
164    }
165
166    /**
167     * Checks to see whether or not a log for the specified tag is loggable at the specified level.
168     */
169    public static boolean isLoggable(String tag, int level) {
170        if (MAX_ENABLED_LOG_LEVEL > level) {
171            return false;
172        }
173        return Log.isLoggable(tag, level) || Log.isLoggable(TAG, level);
174    }
175
176    /**
177     * Send a {@link #VERBOSE} log message.
178     * @param tag Used to identify the source of a log message.  It usually identifies
179     *        the class or activity where the log call occurs.
180     * @param format the format string (see {@link java.util.Formatter#format})
181     * @param args
182     *            the list of arguments passed to the formatter. If there are
183     *            more arguments than required by {@code format},
184     *            additional arguments are ignored.
185     */
186    public static int v(String tag, String format, Object... args) {
187        if (isLoggable(tag, VERBOSE)) {
188            return Log.v(tag, String.format(format, args));
189        }
190        return 0;
191    }
192
193    /**
194     * Send a {@link #VERBOSE} log message.
195     * @param tag Used to identify the source of a log message.  It usually identifies
196     *        the class or activity where the log call occurs.
197     * @param tr An exception to log
198     * @param format the format string (see {@link java.util.Formatter#format})
199     * @param args
200     *            the list of arguments passed to the formatter. If there are
201     *            more arguments than required by {@code format},
202     *            additional arguments are ignored.
203     */
204    public static int v(String tag, Throwable tr, String format, Object... args) {
205        if (isLoggable(tag, VERBOSE)) {
206            return Log.v(tag, String.format(format, args), tr);
207        }
208        return 0;
209    }
210
211    /**
212     * Send a {@link #DEBUG} log message.
213     * @param tag Used to identify the source of a log message.  It usually identifies
214     *        the class or activity where the log call occurs.
215     * @param format the format string (see {@link java.util.Formatter#format})
216     * @param args
217     *            the list of arguments passed to the formatter. If there are
218     *            more arguments than required by {@code format},
219     *            additional arguments are ignored.
220     */
221    public static int d(String tag, String format, Object... args) {
222        if (isLoggable(tag, DEBUG)) {
223            return Log.d(tag, String.format(format, args));
224        }
225        return 0;
226    }
227
228    /**
229     * Send a {@link #DEBUG} log message.
230     * @param tag Used to identify the source of a log message.  It usually identifies
231     *        the class or activity where the log call occurs.
232     * @param tr An exception to log
233     * @param format the format string (see {@link java.util.Formatter#format})
234     * @param args
235     *            the list of arguments passed to the formatter. If there are
236     *            more arguments than required by {@code format},
237     *            additional arguments are ignored.
238     */
239    public static int d(String tag, Throwable tr, String format, Object... args) {
240        if (isLoggable(tag, DEBUG)) {
241            return Log.d(tag, String.format(format, args), tr);
242        }
243        return 0;
244    }
245
246    /**
247     * Send a {@link #INFO} log message.
248     * @param tag Used to identify the source of a log message.  It usually identifies
249     *        the class or activity where the log call occurs.
250     * @param format the format string (see {@link java.util.Formatter#format})
251     * @param args
252     *            the list of arguments passed to the formatter. If there are
253     *            more arguments than required by {@code format},
254     *            additional arguments are ignored.
255     */
256    public static int i(String tag, String format, Object... args) {
257        if (isLoggable(tag, INFO)) {
258            return Log.i(tag, String.format(format, args));
259        }
260        return 0;
261    }
262
263    /**
264     * Send a {@link #INFO} log message.
265     * @param tag Used to identify the source of a log message.  It usually identifies
266     *        the class or activity where the log call occurs.
267     * @param tr An exception to log
268     * @param format the format string (see {@link java.util.Formatter#format})
269     * @param args
270     *            the list of arguments passed to the formatter. If there are
271     *            more arguments than required by {@code format},
272     *            additional arguments are ignored.
273     */
274    public static int i(String tag, Throwable tr, String format, Object... args) {
275        if (isLoggable(tag, INFO)) {
276            return Log.i(tag, String.format(format, args), tr);
277        }
278        return 0;
279    }
280
281    /**
282     * Send a {@link #WARN} log message.
283     * @param tag Used to identify the source of a log message.  It usually identifies
284     *        the class or activity where the log call occurs.
285     * @param format the format string (see {@link java.util.Formatter#format})
286     * @param args
287     *            the list of arguments passed to the formatter. If there are
288     *            more arguments than required by {@code format},
289     *            additional arguments are ignored.
290     */
291    public static int w(String tag, String format, Object... args) {
292        if (isLoggable(tag, WARN)) {
293            return Log.w(tag, String.format(format, args));
294        }
295        return 0;
296    }
297
298    /**
299     * Send a {@link #WARN} log message.
300     * @param tag Used to identify the source of a log message.  It usually identifies
301     *        the class or activity where the log call occurs.
302     * @param tr An exception to log
303     * @param format the format string (see {@link java.util.Formatter#format})
304     * @param args
305     *            the list of arguments passed to the formatter. If there are
306     *            more arguments than required by {@code format},
307     *            additional arguments are ignored.
308     */
309    public static int w(String tag, Throwable tr, String format, Object... args) {
310        if (isLoggable(tag, WARN)) {
311            return Log.w(tag, String.format(format, args), tr);
312        }
313        return 0;
314    }
315
316    /**
317     * Send a {@link #ERROR} log message.
318     * @param tag Used to identify the source of a log message.  It usually identifies
319     *        the class or activity where the log call occurs.
320     * @param format the format string (see {@link java.util.Formatter#format})
321     * @param args
322     *            the list of arguments passed to the formatter. If there are
323     *            more arguments than required by {@code format},
324     *            additional arguments are ignored.
325     */
326    public static int e(String tag, String format, Object... args) {
327        if (isLoggable(tag, ERROR)) {
328            return Log.e(tag, String.format(format, args));
329        }
330        return 0;
331    }
332
333    /**
334     * Send a {@link #ERROR} log message.
335     * @param tag Used to identify the source of a log message.  It usually identifies
336     *        the class or activity where the log call occurs.
337     * @param tr An exception to log
338     * @param format the format string (see {@link java.util.Formatter#format})
339     * @param args
340     *            the list of arguments passed to the formatter. If there are
341     *            more arguments than required by {@code format},
342     *            additional arguments are ignored.
343     */
344    public static int e(String tag, Throwable tr, String format, Object... args) {
345        if (isLoggable(tag, ERROR)) {
346            return Log.e(tag, String.format(format, args), tr);
347        }
348        return 0;
349    }
350
351    /**
352     * What a Terrible Failure: Report a condition that should never happen.
353     * The error will always be logged at level ASSERT with the call stack.
354     * Depending on system configuration, a report may be added to the
355     * {@link android.os.DropBoxManager} and/or the process may be terminated
356     * immediately with an error dialog.
357     * @param tag Used to identify the source of a log message.  It usually identifies
358     *        the class or activity where the log call occurs.
359     * @param format the format string (see {@link java.util.Formatter#format})
360     * @param args
361     *            the list of arguments passed to the formatter. If there are
362     *            more arguments than required by {@code format},
363     *            additional arguments are ignored.
364     */
365    public static int wtf(String tag, String format, Object... args) {
366        return Log.wtf(tag, String.format(format, args), new Error());
367    }
368
369    /**
370     * What a Terrible Failure: Report a condition that should never happen.
371     * The error will always be logged at level ASSERT with the call stack.
372     * Depending on system configuration, a report may be added to the
373     * {@link android.os.DropBoxManager} and/or the process may be terminated
374     * immediately with an error dialog.
375     * @param tag Used to identify the source of a log message.  It usually identifies
376     *        the class or activity where the log call occurs.
377     * @param tr An exception to log
378     * @param format the format string (see {@link java.util.Formatter#format})
379     * @param args
380     *            the list of arguments passed to the formatter. If there are
381     *            more arguments than required by {@code format},
382     *            additional arguments are ignored.
383     */
384    public static int wtf(String tag, Throwable tr, String format, Object... args) {
385        return Log.wtf(tag, String.format(format, args), tr);
386    }
387
388
389    /**
390     * Try to make a date MIME(RFC 2822/5322)-compliant.
391     *
392     * It fixes:
393     * - "Thu, 10 Dec 09 15:08:08 GMT-0700" to "Thu, 10 Dec 09 15:08:08 -0700"
394     *   (4 digit zone value can't be preceded by "GMT")
395     *   We got a report saying eBay sends a date in this format
396     */
397    public static String cleanUpMimeDate(String date) {
398        if (TextUtils.isEmpty(date)) {
399            return date;
400        }
401        date = DATE_CLEANUP_PATTERN_WRONG_TIMEZONE.matcher(date).replaceFirst("$1");
402        return date;
403    }
404
405
406    public static String byteToHex(int b) {
407        return byteToHex(new StringBuilder(), b).toString();
408    }
409
410    public static StringBuilder byteToHex(StringBuilder sb, int b) {
411        b &= 0xFF;
412        sb.append("0123456789ABCDEF".charAt(b >> 4));
413        sb.append("0123456789ABCDEF".charAt(b & 0xF));
414        return sb;
415    }
416
417}
418