• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 package com.android.telephony;
17 
18 import android.net.Uri;
19 import android.os.Build;
20 import android.telecom.PhoneAccount;
21 import android.telephony.PhoneNumberUtils;
22 import android.text.TextUtils;
23 import android.util.Base64;
24 import android.util.Log;
25 
26 import com.android.internal.telephony.util.TelephonyUtils;
27 
28 import java.security.MessageDigest;
29 import java.security.NoSuchAlgorithmException;
30 
31 /**
32  * A copy of {@link android.telephony.Rlog} to be used within the telephony mainline module.
33  *
34  * @hide
35  */
36 public final class Rlog {
37 
38     private static final boolean USER_BUILD = TelephonyUtils.IS_USER;
39 
Rlog()40     private Rlog() {
41     }
42 
log(int priority, String tag, String msg)43     private static int log(int priority, String tag, String msg) {
44         return Log.logToRadioBuffer(priority, tag, msg);
45     }
46 
v(String tag, String msg)47     public static int v(String tag, String msg) {
48         return log(Log.VERBOSE, tag, msg);
49     }
50 
v(String tag, String msg, Throwable tr)51     public static int v(String tag, String msg, Throwable tr) {
52         return log(Log.VERBOSE, tag,
53                 msg + '\n' + Log.getStackTraceString(tr));
54     }
55 
d(String tag, String msg)56     public static int d(String tag, String msg) {
57         return log(Log.DEBUG, tag, msg);
58     }
59 
d(String tag, String msg, Throwable tr)60     public static int d(String tag, String msg, Throwable tr) {
61         return log(Log.DEBUG, tag,
62                 msg + '\n' + Log.getStackTraceString(tr));
63     }
64 
i(String tag, String msg)65     public static int i(String tag, String msg) {
66         return log(Log.INFO, tag, msg);
67     }
68 
i(String tag, String msg, Throwable tr)69     public static int i(String tag, String msg, Throwable tr) {
70         return log(Log.INFO, tag,
71                 msg + '\n' + Log.getStackTraceString(tr));
72     }
73 
w(String tag, String msg)74     public static int w(String tag, String msg) {
75         return log(Log.WARN, tag, msg);
76     }
77 
w(String tag, String msg, Throwable tr)78     public static int w(String tag, String msg, Throwable tr) {
79         return log(Log.WARN, tag,
80                 msg + '\n' + Log.getStackTraceString(tr));
81     }
82 
w(String tag, Throwable tr)83     public static int w(String tag, Throwable tr) {
84         return log(Log.WARN, tag, Log.getStackTraceString(tr));
85     }
86 
e(String tag, String msg)87     public static int e(String tag, String msg) {
88         return log(Log.ERROR, tag, msg);
89     }
90 
e(String tag, String msg, Throwable tr)91     public static int e(String tag, String msg, Throwable tr) {
92         return log(Log.ERROR, tag,
93                 msg + '\n' + Log.getStackTraceString(tr));
94     }
95 
println(int priority, String tag, String msg)96     public static int println(int priority, String tag, String msg) {
97         return log(priority, tag, msg);
98     }
99 
isLoggable(String tag, int level)100     public static boolean isLoggable(String tag, int level) {
101         return Log.isLoggable(tag, level);
102     }
103 
104     /**
105      * Redact personally identifiable information for production users.
106      * @param tag used to identify the source of a log message
107      * @param pii the personally identifiable information we want to apply secure hash on.
108      * @return If tag is loggable in verbose mode or pii is null, return the original input.
109      * otherwise return a secure Hash of input pii
110      */
pii(String tag, Object pii)111     public static String pii(String tag, Object pii) {
112         String val = String.valueOf(pii);
113         if (pii == null || TextUtils.isEmpty(val) || isLoggable(tag, Log.VERBOSE)) {
114             return val;
115         }
116         return "[" + secureHash(val.getBytes()) + "]";
117     }
118 
119     /**
120      * Redact personally identifiable information for production users.
121      * @param enablePiiLogging set when caller explicitly want to enable sensitive logging.
122      * @param pii the personally identifiable information we want to apply secure hash on.
123      * @return If enablePiiLogging is set to true or pii is null, return the original input.
124      * otherwise return a secure Hash of input pii
125      */
pii(boolean enablePiiLogging, Object pii)126     public static String pii(boolean enablePiiLogging, Object pii) {
127         String val = String.valueOf(pii);
128         if (pii == null || TextUtils.isEmpty(val) || enablePiiLogging) {
129             return val;
130         }
131         return "[" + secureHash(val.getBytes()) + "]";
132     }
133 
134     /**
135      * Generates an obfuscated string for a calling handle in {@link Uri} format, or a raw phone
136      * phone number in {@link String} format.
137      * @param pii The information to obfuscate.
138      * @return The obfuscated string.
139      */
piiHandle(Object pii)140     public static String piiHandle(Object pii) {
141         StringBuilder sb = new StringBuilder();
142         if (pii instanceof Uri) {
143             Uri uri = (Uri) pii;
144             String scheme = uri.getScheme();
145 
146             if (!TextUtils.isEmpty(scheme)) {
147                 sb.append(scheme).append(":");
148             }
149 
150             String textToObfuscate = uri.getSchemeSpecificPart();
151             if (PhoneAccount.SCHEME_TEL.equals(scheme)) {
152                 obfuscatePhoneNumber(sb, textToObfuscate);
153             } else if (PhoneAccount.SCHEME_SIP.equals(scheme)) {
154                 for (int i = 0; i < textToObfuscate.length(); i++) {
155                     char c = textToObfuscate.charAt(i);
156                     if (c != '@' && c != '.') {
157                         c = '*';
158                     }
159                     sb.append(c);
160                 }
161             } else {
162                 sb.append("***");
163             }
164         } else if (pii instanceof String) {
165             String number = (String) pii;
166             obfuscatePhoneNumber(sb, number);
167         }
168 
169         return sb.toString();
170     }
171 
172     /**
173      * Obfuscates a phone number, allowing NUM_DIALABLE_DIGITS_TO_LOG digits to be exposed for the
174      * phone number.
175      * @param sb String buffer to write obfuscated number to.
176      * @param phoneNumber The number to obfuscate.
177      */
obfuscatePhoneNumber(StringBuilder sb, String phoneNumber)178     private static void obfuscatePhoneNumber(StringBuilder sb, String phoneNumber) {
179         int numDigitsToLog = USER_BUILD ? 0 : 2;
180         int numDigitsToObfuscate = getDialableCount(phoneNumber) - numDigitsToLog;
181         for (int i = 0; i < phoneNumber.length(); i++) {
182             char c = phoneNumber.charAt(i);
183             boolean isDialable = PhoneNumberUtils.isDialable(c);
184             if (isDialable) {
185                 numDigitsToObfuscate--;
186             }
187             sb.append(isDialable && numDigitsToObfuscate >= 0 ? "*" : c);
188         }
189     }
190 
191     /**
192      * Determines the number of dialable characters in a string.
193      * @param toCount The string to count dialable characters in.
194      * @return The count of dialable characters.
195      */
getDialableCount(String toCount)196     private static int getDialableCount(String toCount) {
197         int numDialable = 0;
198         for (char c : toCount.toCharArray()) {
199             if (PhoneNumberUtils.isDialable(c)) {
200                 numDialable++;
201             }
202         }
203         return numDialable;
204     }
205 
206     /**
207      * Returns a secure hash (using the SHA1 algorithm) of the provided input.
208      *
209      * @return "****" if the build type is user, otherwise the hash
210      * @param input the bytes for which the secure hash should be computed.
211      */
secureHash(byte[] input)212     private static String secureHash(byte[] input) {
213         // Refrain from logging user personal information in user build.
214         if (USER_BUILD) {
215             return "****";
216         }
217 
218         MessageDigest messageDigest;
219 
220         try {
221             messageDigest = MessageDigest.getInstance("SHA-1");
222         } catch (NoSuchAlgorithmException e) {
223             return "####";
224         }
225 
226         byte[] result = messageDigest.digest(input);
227         return Base64.encodeToString(
228                 result, Base64.URL_SAFE | Base64.NO_PADDING | Base64.NO_WRAP);
229     }
230 }
231