1 /*
2  * Copyright (C) 2021 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 package com.android.internal.util.dump;
17 
18 import android.util.ArrayMap;
19 import android.util.Dumpable;
20 import android.util.DumpableContainer;
21 import android.util.IndentingPrintWriter;
22 import android.util.Log;
23 
24 import java.io.PrintWriter;
25 import java.util.Objects;
26 
27 /**
28  * Helper class for {@link DumpableContainer} implementations - they can "implement it by
29  * association", i.e., by delegating the interface methods to a {@code DumpableContainerImpl}.
30  *
31  * <p>This class is not thread safe.
32  *
33  * @hide
34  */
35 @android.ravenwood.annotation.RavenwoodKeepWholeClass
36 public final class DumpableContainerImpl implements DumpableContainer {
37 
38     private static final String TAG = DumpableContainerImpl.class.getSimpleName();
39 
40     private static final boolean DEBUG = false;
41 
42     private final ArrayMap<String, Dumpable> mDumpables = new ArrayMap<>();
43 
44     @Override
addDumpable(Dumpable dumpable)45     public boolean addDumpable(Dumpable dumpable) {
46         Objects.requireNonNull(dumpable, "dumpable");
47         String name = dumpable.getDumpableName();
48         Objects.requireNonNull(name, () -> "name of" + dumpable);
49 
50         if (mDumpables.containsKey(name)) {
51             if (DEBUG) {
52                 Log.d(TAG, "addDumpable(): ignoring " + dumpable + " as there is already a dumpable"
53                         + " with that name (" + name + "): " + mDumpables.get(name));
54             }
55             return false;
56         }
57 
58         if (DEBUG) {
59             Log.d(TAG, "Adding " + name + " -> " + dumpable);
60         }
61         mDumpables.put(name,  dumpable);
62         return true;
63     }
64 
65     @Override
removeDumpable(Dumpable dumpable)66     public boolean removeDumpable(Dumpable dumpable) {
67         Objects.requireNonNull(dumpable, "dumpable");
68         String name = dumpable.getDumpableName();
69         if (name == null) {
70             if (DEBUG) {
71                 Log.d(TAG, "Tried to remove nameless dumpable: " + dumpable);
72             }
73             return false;
74         }
75 
76         Dumpable candidate = mDumpables.get(name);
77         if (candidate == null) {
78             if (DEBUG) {
79                 Log.d(TAG, "Dumpable with name " + name + " not found");
80             }
81             return false;
82         }
83 
84         // Make sure it's the right one
85         if (candidate != dumpable) {
86             Log.w(TAG, "removeDumpable(): passed dumpable (" + dumpable + ") named " + name
87                     + ", but internal dumpable with that name is " + candidate);
88             return false;
89         }
90         if (DEBUG) {
91             Log.d(TAG, "Removing dumpable named " + name);
92         }
93         mDumpables.remove(name);
94         return true;
95     }
96 
97     /**
98      * Dumps the number of dumpable, without a newline.
99      */
dumpNumberDumpables(IndentingPrintWriter writer)100     private int dumpNumberDumpables(IndentingPrintWriter writer) {
101         int size = mDumpables.size();
102         if (size == 0) {
103             writer.print("No dumpables");
104         } else {
105             writer.print(size); writer.print(" dumpables");
106         }
107         return size;
108     }
109 
110     /**
111      * Lists the name of all dumpables to the given {@code writer}.
112      */
listDumpables(String prefix, PrintWriter writer)113     public void listDumpables(String prefix, PrintWriter writer) {
114         IndentingPrintWriter ipw = new IndentingPrintWriter(writer, prefix, prefix);
115 
116         int size = dumpNumberDumpables(ipw);
117         if (size == 0) {
118             ipw.println();
119             return;
120         }
121         ipw.print(": ");
122         for (int i = 0; i < size; i++) {
123             ipw.print(mDumpables.keyAt(i));
124             if (i < size - 1) ipw.print(' ');
125         }
126         ipw.println();
127     }
128 
129     /**
130      * Dumps the content of all dumpables to the given {@code writer}.
131      */
dumpAllDumpables(String prefix, PrintWriter writer, String[] args)132     public void dumpAllDumpables(String prefix, PrintWriter writer, String[] args) {
133         IndentingPrintWriter ipw = new IndentingPrintWriter(writer, prefix, prefix);
134         int size = dumpNumberDumpables(ipw);
135         if (size == 0) {
136             ipw.println();
137             return;
138         }
139         ipw.println(":");
140 
141         for (int i = 0; i < size; i++) {
142             String dumpableName = mDumpables.keyAt(i);
143             ipw.print('#'); ipw.print(i); ipw.print(": "); ipw.println(dumpableName);
144             Dumpable dumpable = mDumpables.valueAt(i);
145             indentAndDump(ipw, dumpable, args);
146         }
147     }
148 
indentAndDump(IndentingPrintWriter writer, Dumpable dumpable, String[] args)149     private void indentAndDump(IndentingPrintWriter writer, Dumpable dumpable, String[] args) {
150         writer.increaseIndent();
151         try {
152             dumpable.dump(writer, args);
153         } finally {
154             writer.decreaseIndent();
155         }
156     }
157 
158     /**
159      * Dumps the content of a specific dumpable to the given {@code writer}.
160      */
161     @SuppressWarnings("resource") // cannot close ipw as it would close writer
dumpOneDumpable(String prefix, PrintWriter writer, String dumpableName, String[] args)162     public void dumpOneDumpable(String prefix, PrintWriter writer, String dumpableName,
163             String[] args) {
164         IndentingPrintWriter ipw = new IndentingPrintWriter(writer, prefix, prefix);
165         Dumpable dumpable = mDumpables.get(dumpableName);
166         if (dumpable == null) {
167             ipw.print("No "); ipw.println(dumpableName);
168             return;
169         }
170         ipw.print(dumpableName); ipw.println(':');
171         indentAndDump(ipw, dumpable, args);
172     }
173 }
174