1 /*
2  * Copyright (C) 2006 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 android.util;
18 
19 import android.compat.annotation.UnsupportedAppUsage;
20 import android.os.Build;
21 import android.os.SystemClock;
22 
23 import java.io.FileDescriptor;
24 import java.io.PrintWriter;
25 import java.time.Duration;
26 import java.time.Instant;
27 import java.time.LocalDateTime;
28 import java.util.ArrayDeque;
29 import java.util.Deque;
30 import java.util.Iterator;
31 
32 /**
33  * @hide
34  */
35 public final class LocalLog {
36 
37     private final Deque<String> mLog;
38     private final int mMaxLines;
39 
40     /**
41      * {@code true} to use log timestamps expressed in local date/time, {@code false} to use log
42      * timestamped expressed with the elapsed realtime clock and UTC system clock. {@code false} is
43      * useful when logging behavior that modifies device time zone or system clock.
44      */
45     private final boolean mUseLocalTimestamps;
46 
47     @UnsupportedAppUsage
LocalLog(int maxLines)48     public LocalLog(int maxLines) {
49         this(maxLines, true /* useLocalTimestamps */);
50     }
51 
LocalLog(int maxLines, boolean useLocalTimestamps)52     public LocalLog(int maxLines, boolean useLocalTimestamps) {
53         mMaxLines = Math.max(0, maxLines);
54         mLog = new ArrayDeque<>(mMaxLines);
55         mUseLocalTimestamps = useLocalTimestamps;
56     }
57 
58     @UnsupportedAppUsage
log(String msg)59     public void log(String msg) {
60         if (mMaxLines <= 0) {
61             return;
62         }
63         final String logLine;
64         if (mUseLocalTimestamps) {
65             logLine = LocalDateTime.now() + " - " + msg;
66         } else {
67             logLine = Duration.ofMillis(SystemClock.elapsedRealtime())
68                     + " / " + Instant.now() + " - " + msg;
69         }
70         append(logLine);
71     }
72 
append(String logLine)73     private synchronized void append(String logLine) {
74         while (mLog.size() >= mMaxLines) {
75             mLog.remove();
76         }
77         mLog.add(logLine);
78     }
79 
80     @UnsupportedAppUsage
dump(FileDescriptor fd, PrintWriter pw, String[] args)81     public synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
82         dump(pw);
83     }
84 
dump(PrintWriter pw)85     public synchronized void dump(PrintWriter pw) {
86         dump("", pw);
87     }
88 
89     /**
90      * Dumps the content of local log to print writer with each log entry predeced with indent
91      *
92      * @param indent indent that precedes each log entry
93      * @param pw printer writer to write into
94      */
dump(String indent, PrintWriter pw)95     public synchronized void dump(String indent, PrintWriter pw) {
96         Iterator<String> itr = mLog.iterator();
97         while (itr.hasNext()) {
98             pw.printf("%s%s\n", indent, itr.next());
99         }
100     }
101 
reverseDump(FileDescriptor fd, PrintWriter pw, String[] args)102     public synchronized void reverseDump(FileDescriptor fd, PrintWriter pw, String[] args) {
103         reverseDump(pw);
104     }
105 
reverseDump(PrintWriter pw)106     public synchronized void reverseDump(PrintWriter pw) {
107         Iterator<String> itr = mLog.descendingIterator();
108         while (itr.hasNext()) {
109             pw.println(itr.next());
110         }
111     }
112 
113     public static class ReadOnlyLocalLog {
114         private final LocalLog mLog;
ReadOnlyLocalLog(LocalLog log)115         ReadOnlyLocalLog(LocalLog log) {
116             mLog = log;
117         }
118         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
dump(FileDescriptor fd, PrintWriter pw, String[] args)119         public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
120             mLog.dump(pw);
121         }
dump(PrintWriter pw)122         public void dump(PrintWriter pw) {
123             mLog.dump(pw);
124         }
reverseDump(FileDescriptor fd, PrintWriter pw, String[] args)125         public void reverseDump(FileDescriptor fd, PrintWriter pw, String[] args) {
126             mLog.reverseDump(pw);
127         }
reverseDump(PrintWriter pw)128         public void reverseDump(PrintWriter pw) {
129             mLog.reverseDump(pw);
130         }
131     }
132 
133     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
readOnlyLocalLog()134     public ReadOnlyLocalLog readOnlyLocalLog() {
135         return new ReadOnlyLocalLog(this);
136     }
137 }
138