1 /*
2  * Copyright (C) 2020 The Android Open Source Project
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  */
16 
17 package com.android.permissioncontroller
18 
19 import android.util.Log
20 import com.android.permissioncontroller.Constants.LOGS_TO_DUMP_FILE
21 import java.io.File
22 
23 /** Like {@link Log} but stores the logs in a file which can later be dumped via {@link #dump} */
24 object DumpableLog {
25     private const val MAX_FILE_SIZE = 64 * 1024
26 
27     private val lock = Any()
28     private val file = File(PermissionControllerApplication.get().filesDir, LOGS_TO_DUMP_FILE)
29 
30     init {
31         file.createNewFile()
32     }
33 
34     /** Equivalent to {@link Log.v} */
vnull35     fun v(tag: String, message: String, exception: Throwable? = null) {
36         Log.v(tag, message, exception)
37         addLogToDump("v", tag, message, exception)
38     }
39 
40     /** Equivalent to {@link Log.d} */
dnull41     fun d(tag: String, message: String, exception: Throwable? = null) {
42         Log.d(tag, message, exception)
43         addLogToDump("d", tag, message, exception)
44     }
45 
46     /** Equivalent to {@link Log.i} */
inull47     fun i(tag: String, message: String, exception: Throwable? = null) {
48         Log.i(tag, message, exception)
49         addLogToDump("i", tag, message, exception)
50     }
51 
52     /** Equivalent to {@link Log.w} */
wnull53     fun w(tag: String, message: String, exception: Throwable? = null) {
54         Log.w(tag, message, exception)
55         addLogToDump("w", tag, message, exception)
56     }
57 
58     /** Equivalent to {@link Log.e} */
enull59     fun e(tag: String, message: String, exception: Throwable? = null) {
60         Log.e(tag, message, exception)
61         addLogToDump("e", tag, message, exception)
62     }
63 
addLogToDumpnull64     private fun addLogToDump(level: String, tag: String, message: String, exception: Throwable?) {
65         synchronized(lock) {
66             // TODO: Needs to be replaced by proper log rotation
67             if (file.length() > MAX_FILE_SIZE) {
68                 val dump = file.readLines()
69 
70                 file.writeText("truncated at ${System.currentTimeMillis()}\n")
71                 dump.subList(dump.size / 2, dump.size).forEach { file.appendText(it + "\n") }
72             }
73 
74             file.appendText(
75                 "${System.currentTimeMillis()} $tag:$level $message " +
76                     "${exception?.let { it.message + Log.getStackTraceString(it) } ?: ""}\n"
77             )
78         }
79     }
80 
81     /** @return the previously logged entries */
getnull82     suspend fun get(): List<String> {
83         synchronized(lock) {
84             return file.readLines()
85         }
86     }
87 }
88