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.internal.protolog;
18 
19 import android.annotation.Nullable;
20 import android.util.Slog;
21 
22 import org.json.JSONException;
23 import org.json.JSONObject;
24 
25 import java.io.BufferedReader;
26 import java.io.FileInputStream;
27 import java.io.FileNotFoundException;
28 import java.io.IOException;
29 import java.io.InputStream;
30 import java.io.InputStreamReader;
31 import java.io.PrintWriter;
32 import java.util.Iterator;
33 import java.util.Map;
34 import java.util.TreeMap;
35 import java.util.zip.GZIPInputStream;
36 
37 /**
38  * Handles loading and parsing of ProtoLog viewer configuration.
39  */
40 public class ProtoLogViewerConfigReader {
41     private static final String TAG = "ProtoLogViewerConfigReader";
42     private Map<Integer, String> mLogMessageMap = null;
43 
44     /** Returns message format string for its hash or null if unavailable. */
getViewerString(int messageHash)45     public synchronized String getViewerString(int messageHash) {
46         if (mLogMessageMap != null) {
47             return mLogMessageMap.get(messageHash);
48         } else {
49             return null;
50         }
51     }
52 
53     /**
54      * Reads the specified viewer configuration file. Does nothing if the config is already loaded.
55      */
loadViewerConfig(PrintWriter pw, String viewerConfigFilename)56     public synchronized void loadViewerConfig(PrintWriter pw, String viewerConfigFilename) {
57         try {
58             loadViewerConfig(new GZIPInputStream(new FileInputStream(viewerConfigFilename)));
59             logAndPrintln(pw, "Loaded " + mLogMessageMap.size()
60                     + " log definitions from " + viewerConfigFilename);
61         } catch (FileNotFoundException e) {
62             logAndPrintln(pw, "Unable to load log definitions: File "
63                     + viewerConfigFilename + " not found." + e);
64         } catch (IOException e) {
65             logAndPrintln(pw, "Unable to load log definitions: IOException while reading "
66                     + viewerConfigFilename + ". " + e);
67         } catch (JSONException e) {
68             logAndPrintln(pw, "Unable to load log definitions: JSON parsing exception while reading "
69                     + viewerConfigFilename + ". " + e);
70         }
71     }
72 
73     /**
74      * Reads the specified viewer configuration input stream.
75      * Does nothing if the config is already loaded.
76      */
loadViewerConfig(InputStream viewerConfigInputStream)77     public synchronized void loadViewerConfig(InputStream viewerConfigInputStream)
78             throws IOException, JSONException {
79         if (mLogMessageMap != null) {
80             return;
81         }
82         InputStreamReader config = new InputStreamReader(viewerConfigInputStream);
83         BufferedReader reader = new BufferedReader(config);
84         StringBuilder builder = new StringBuilder();
85         String line;
86         while ((line = reader.readLine()) != null) {
87             builder.append(line).append('\n');
88         }
89         reader.close();
90         JSONObject json = new JSONObject(builder.toString());
91         JSONObject messages = json.getJSONObject("messages");
92 
93         mLogMessageMap = new TreeMap<>();
94         Iterator it = messages.keys();
95         while (it.hasNext()) {
96             String key = (String) it.next();
97             try {
98                 int hash = Integer.parseInt(key);
99                 JSONObject val = messages.getJSONObject(key);
100                 String msg = val.getString("message");
101                 mLogMessageMap.put(hash, msg);
102             } catch (NumberFormatException expected) {
103                 // Not a messageHash - skip it
104             }
105         }
106     }
107 
108     /**
109      * Returns the number of loaded log definitions kept in memory.
110      */
knownViewerStringsNumber()111     public synchronized int knownViewerStringsNumber() {
112         if (mLogMessageMap != null) {
113             return mLogMessageMap.size();
114         }
115         return 0;
116     }
117 
logAndPrintln(@ullable PrintWriter pw, String msg)118     static void logAndPrintln(@Nullable PrintWriter pw, String msg) {
119         Slog.i(TAG, msg);
120         if (pw != null) {
121             pw.println(msg);
122             pw.flush();
123         }
124     }
125 }
126