blob: a34094ce64520aba4e2547be229b0e321e1fd48a [file] [log] [blame]
Ihab Awad60ac30b2014-05-20 22:32:12 -07001/*
2 * Copyright 2014, 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
Tyler Gunnef9f6f92014-09-12 22:16:17 -070017package android.telecom;
Ihab Awad60ac30b2014-05-20 22:32:12 -070018
Tyler Gunnffbcd892020-05-04 15:01:59 -070019import android.annotation.NonNull;
Artur Satayev2ebb31c2020-01-08 12:24:36 +000020import android.compat.annotation.UnsupportedAppUsage;
Tyler Gunnffbcd892020-05-04 15:01:59 -070021import android.content.ComponentName;
Brad Ebinger4fb372f2016-10-05 15:47:28 -070022import android.content.Context;
Santos Cordon3c20d632016-02-25 16:12:35 -080023import android.net.Uri;
youhei.x.miyoshib3cd7b52016-12-12 21:10:54 +090024import android.os.Build;
Brad Ebinger51b98342016-09-22 16:30:46 -070025import android.telecom.Logging.EventManager;
26import android.telecom.Logging.Session;
27import android.telecom.Logging.SessionManager;
Santos Cordon3c20d632016-02-25 16:12:35 -080028import android.telephony.PhoneNumberUtils;
29import android.text.TextUtils;
Hall Liue362e502016-01-07 17:35:54 -080030
Brad Ebinger51b98342016-09-22 16:30:46 -070031import com.android.internal.annotations.VisibleForTesting;
32import com.android.internal.util.IndentingPrintWriter;
33
Tyler Gunnffbcd892020-05-04 15:01:59 -070034import java.util.Arrays;
Ihab Awad60ac30b2014-05-20 22:32:12 -070035import java.util.IllegalFormatException;
36import java.util.Locale;
Tyler Gunnffbcd892020-05-04 15:01:59 -070037import java.util.stream.Collectors;
Ihab Awad60ac30b2014-05-20 22:32:12 -070038
39/**
40 * Manages logging for the entire module.
41 *
42 * @hide
43 */
Brad Ebinger51b98342016-09-22 16:30:46 -070044public class Log {
Ihab Awad60ac30b2014-05-20 22:32:12 -070045
Brad Ebinger51b98342016-09-22 16:30:46 -070046 private static final long EXTENDED_LOGGING_DURATION_MILLIS = 60000 * 30; // 30 minutes
Ihab Awad60ac30b2014-05-20 22:32:12 -070047
Brad Ebinger51b98342016-09-22 16:30:46 -070048 private static final int EVENTS_TO_CACHE = 10;
49 private static final int EVENTS_TO_CACHE_DEBUG = 20;
50
Tyler Gunn9bc35112018-04-23 09:52:25 -070051 /**
52 * When generating a bug report, include the last X dialable digits when logging phone numbers.
53 */
54 private static final int NUM_DIALABLE_DIGITS_TO_LOG = Build.IS_USER ? 0 : 2;
55
Brad Ebinger51b98342016-09-22 16:30:46 -070056 // Generic tag for all Telecom logging
57 @VisibleForTesting
58 public static String TAG = "TelecomFramework";
Brad Ebinger0c3541b2016-11-01 14:11:38 -070059 public static boolean DEBUG = isLoggable(android.util.Log.DEBUG);
60 public static boolean INFO = isLoggable(android.util.Log.INFO);
61 public static boolean VERBOSE = isLoggable(android.util.Log.VERBOSE);
62 public static boolean WARN = isLoggable(android.util.Log.WARN);
63 public static boolean ERROR = isLoggable(android.util.Log.ERROR);
Brad Ebinger51b98342016-09-22 16:30:46 -070064
65 private static final boolean FORCE_LOGGING = false; /* STOP SHIP if true */
Jeff Sharkey5ab02432017-06-27 11:01:36 -060066 private static final boolean USER_BUILD = Build.IS_USER;
Ihab Awad60ac30b2014-05-20 22:32:12 -070067
Brad Ebinger51b98342016-09-22 16:30:46 -070068 // Used to synchronize singleton logging lazy initialization
69 private static final Object sSingletonSync = new Object();
70 private static EventManager sEventManager;
71 private static SessionManager sSessionManager;
Tyler Gunn4d54cca2023-03-16 23:00:04 +000072 private static Object sLock = null;
Brad Ebinger51b98342016-09-22 16:30:46 -070073
74 /**
75 * Tracks whether user-activated extended logging is enabled.
76 */
77 private static boolean sIsUserExtendedLoggingEnabled = false;
78
79 /**
Thomas Stuarta1ca66f2022-04-28 11:04:40 -070080 * Enabled in telecom testing to help gate log statements causing log spew.
81 */
82 private static boolean sIsUnitTestingEnabled = false;
83
84 /**
Brad Ebinger51b98342016-09-22 16:30:46 -070085 * The time when user-activated extended logging should be ended. Used to determine when
86 * extended logging should automatically be disabled.
87 */
88 private static long sUserExtendedLoggingStopTime = 0;
89
90 private Log() {
91 }
92
93 public static void d(String prefix, String format, Object... args) {
94 if (sIsUserExtendedLoggingEnabled) {
95 maybeDisableLogging();
96 android.util.Slog.i(TAG, buildMessage(prefix, format, args));
97 } else if (DEBUG) {
98 android.util.Slog.d(TAG, buildMessage(prefix, format, args));
99 }
100 }
101
102 public static void d(Object objectPrefix, String format, Object... args) {
103 if (sIsUserExtendedLoggingEnabled) {
104 maybeDisableLogging();
105 android.util.Slog.i(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args));
106 } else if (DEBUG) {
107 android.util.Slog.d(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args));
108 }
109 }
110
Mathew Inwood5d123b62020-11-04 09:29:36 +0000111 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
Brad Ebinger51b98342016-09-22 16:30:46 -0700112 public static void i(String prefix, String format, Object... args) {
113 if (INFO) {
114 android.util.Slog.i(TAG, buildMessage(prefix, format, args));
115 }
116 }
117
118 public static void i(Object objectPrefix, String format, Object... args) {
119 if (INFO) {
120 android.util.Slog.i(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args));
121 }
122 }
123
124 public static void v(String prefix, String format, Object... args) {
125 if (sIsUserExtendedLoggingEnabled) {
126 maybeDisableLogging();
127 android.util.Slog.i(TAG, buildMessage(prefix, format, args));
128 } else if (VERBOSE) {
129 android.util.Slog.v(TAG, buildMessage(prefix, format, args));
130 }
131 }
132
133 public static void v(Object objectPrefix, String format, Object... args) {
134 if (sIsUserExtendedLoggingEnabled) {
135 maybeDisableLogging();
136 android.util.Slog.i(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args));
137 } else if (VERBOSE) {
138 android.util.Slog.v(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args));
139 }
140 }
141
Mathew Inwood5d123b62020-11-04 09:29:36 +0000142 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
Brad Ebinger51b98342016-09-22 16:30:46 -0700143 public static void w(String prefix, String format, Object... args) {
144 if (WARN) {
145 android.util.Slog.w(TAG, buildMessage(prefix, format, args));
146 }
147 }
148
149 public static void w(Object objectPrefix, String format, Object... args) {
150 if (WARN) {
151 android.util.Slog.w(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args));
152 }
153 }
154
155 public static void e(String prefix, Throwable tr, String format, Object... args) {
156 if (ERROR) {
157 android.util.Slog.e(TAG, buildMessage(prefix, format, args), tr);
158 }
159 }
160
161 public static void e(Object objectPrefix, Throwable tr, String format, Object... args) {
162 if (ERROR) {
163 android.util.Slog.e(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args),
164 tr);
165 }
166 }
167
168 public static void wtf(String prefix, Throwable tr, String format, Object... args) {
169 android.util.Slog.wtf(TAG, buildMessage(prefix, format, args), tr);
170 }
171
172 public static void wtf(Object objectPrefix, Throwable tr, String format, Object... args) {
173 android.util.Slog.wtf(TAG, buildMessage(getPrefixFromObject(objectPrefix), format, args),
174 tr);
175 }
176
177 public static void wtf(String prefix, String format, Object... args) {
178 String msg = buildMessage(prefix, format, args);
179 android.util.Slog.wtf(TAG, msg, new IllegalStateException(msg));
180 }
181
182 public static void wtf(Object objectPrefix, String format, Object... args) {
183 String msg = buildMessage(getPrefixFromObject(objectPrefix), format, args);
184 android.util.Slog.wtf(TAG, msg, new IllegalStateException(msg));
185 }
186
187 /**
188 * The ease of use methods below only act mostly as proxies to the Session and Event Loggers.
189 * They also control the lazy loaders of the singleton instances, which will never be loaded if
190 * the proxy methods aren't used.
191 *
192 * Please see each method's documentation inside of their respective implementations in the
193 * loggers.
194 */
195
Brad Ebinger4fb372f2016-10-05 15:47:28 -0700196 public static void setSessionContext(Context context) {
197 getSessionManager().setContext(context);
198 }
199
Brad Ebinger51b98342016-09-22 16:30:46 -0700200 public static void startSession(String shortMethodName) {
201 getSessionManager().startSession(shortMethodName, null);
202 }
203
Brad Ebinger3445f822016-10-24 16:40:49 -0700204 public static void startSession(Session.Info info, String shortMethodName) {
205 getSessionManager().startSession(info, shortMethodName, null);
206 }
207
Brad Ebinger51b98342016-09-22 16:30:46 -0700208 public static void startSession(String shortMethodName, String callerIdentification) {
209 getSessionManager().startSession(shortMethodName, callerIdentification);
210 }
211
Brad Ebingera0dc9762016-10-21 09:41:29 -0700212 public static void startSession(Session.Info info, String shortMethodName,
213 String callerIdentification) {
214 getSessionManager().startSession(info, shortMethodName, callerIdentification);
215 }
216
Brad Ebinger51b98342016-09-22 16:30:46 -0700217 public static Session createSubsession() {
218 return getSessionManager().createSubsession();
219 }
220
Brad Ebinger3445f822016-10-24 16:40:49 -0700221 public static Session.Info getExternalSession() {
222 return getSessionManager().getExternalSession();
223 }
224
Tyler Gunnffbcd892020-05-04 15:01:59 -0700225 /**
226 * Retrieves external session information, providing a context for the recipient of the session
227 * info where the external session came from.
228 * @param ownerInfo The external owner info.
229 * @return New {@link Session.Info} instance with owner info set.
230 */
231 public static Session.Info getExternalSession(@NonNull String ownerInfo) {
232 return getSessionManager().getExternalSession(ownerInfo);
233 }
234
Brad Ebinger51b98342016-09-22 16:30:46 -0700235 public static void cancelSubsession(Session subsession) {
236 getSessionManager().cancelSubsession(subsession);
237 }
238
239 public static void continueSession(Session subsession, String shortMethodName) {
240 getSessionManager().continueSession(subsession, shortMethodName);
241 }
242
243 public static void endSession() {
244 getSessionManager().endSession();
245 }
246
Brad Ebinger836efad2016-10-18 13:48:17 -0700247 public static void registerSessionListener(SessionManager.ISessionListener l) {
248 getSessionManager().registerSessionListener(l);
249 }
250
Brad Ebinger51b98342016-09-22 16:30:46 -0700251 public static String getSessionId() {
252 // If the Session logger has not been initialized, then there have been no sessions logged.
253 // Don't load it now!
254 synchronized (sSingletonSync) {
255 if (sSessionManager != null) {
256 return getSessionManager().getSessionId();
257 } else {
258 return "";
259 }
260 }
261 }
262
263 public static void addEvent(EventManager.Loggable recordEntry, String event) {
264 getEventManager().event(recordEntry, event, null);
265 }
266
267 public static void addEvent(EventManager.Loggable recordEntry, String event, Object data) {
268 getEventManager().event(recordEntry, event, data);
269 }
270
271 public static void addEvent(EventManager.Loggable recordEntry, String event, String format,
272 Object... args) {
273 getEventManager().event(recordEntry, event, format, args);
274 }
275
276 public static void registerEventListener(EventManager.EventListener e) {
277 getEventManager().registerEventListener(e);
278 }
279
280 public static void addRequestResponsePair(EventManager.TimedEventPair p) {
281 getEventManager().addRequestResponsePair(p);
282 }
283
284 public static void dumpEvents(IndentingPrintWriter pw) {
285 // If the Events logger has not been initialized, then there have been no events logged.
286 // Don't load it now!
287 synchronized (sSingletonSync) {
288 if (sEventManager != null) {
289 getEventManager().dumpEvents(pw);
290 } else {
291 pw.println("No Historical Events Logged.");
292 }
293 }
294 }
295
296 /**
Tyler Gunn2db81b52017-05-19 10:10:23 -0700297 * Dumps the events in a timeline format.
298 * @param pw The {@link IndentingPrintWriter} to write to.
299 * @hide
300 */
301 public static void dumpEventsTimeline(IndentingPrintWriter pw) {
302 // If the Events logger has not been initialized, then there have been no events logged.
303 // Don't load it now!
304 synchronized (sSingletonSync) {
305 if (sEventManager != null) {
306 getEventManager().dumpEventsTimeline(pw);
307 } else {
308 pw.println("No Historical Events Logged.");
309 }
310 }
311 }
312
313 /**
Brad Ebinger51b98342016-09-22 16:30:46 -0700314 * Enable or disable extended telecom logging.
315 *
316 * @param isExtendedLoggingEnabled {@code true} if extended logging should be enabled,
317 * {@code false} if it should be disabled.
318 */
319 public static void setIsExtendedLoggingEnabled(boolean isExtendedLoggingEnabled) {
320 // If the state hasn't changed, bail early.
321 if (sIsUserExtendedLoggingEnabled == isExtendedLoggingEnabled) {
322 return;
323 }
324
325 if (sEventManager != null) {
326 sEventManager.changeEventCacheSize(isExtendedLoggingEnabled ?
327 EVENTS_TO_CACHE_DEBUG : EVENTS_TO_CACHE);
328 }
329
330 sIsUserExtendedLoggingEnabled = isExtendedLoggingEnabled;
331 if (sIsUserExtendedLoggingEnabled) {
332 sUserExtendedLoggingStopTime = System.currentTimeMillis()
333 + EXTENDED_LOGGING_DURATION_MILLIS;
334 } else {
335 sUserExtendedLoggingStopTime = 0;
336 }
337 }
338
Thomas Stuarta1ca66f2022-04-28 11:04:40 -0700339 /**
340 * Enabled when tests are running to help gate log statements causing log spew.
341 *
342 * @param isEnabled {@code true} if running unit tests. false otherwise.
343 *
344 */
345 public static void setUnitTestingEnabled(boolean isEnabled) {
346 sIsUnitTestingEnabled = isEnabled;
347 }
348
349 public static boolean isUnitTestingEnabled() {
350 return sIsUnitTestingEnabled;
351 }
352
Brad Ebinger51b98342016-09-22 16:30:46 -0700353 private static EventManager getEventManager() {
354 // Checking for null again outside of synchronization because we only need to synchronize
355 // during the lazy loading of the events logger. We don't need to synchronize elsewhere.
356 if (sEventManager == null) {
357 synchronized (sSingletonSync) {
358 if (sEventManager == null) {
359 sEventManager = new EventManager(Log::getSessionId);
360 return sEventManager;
361 }
362 }
363 }
364 return sEventManager;
365 }
366
Brad Ebinger8adafe72017-06-08 15:44:40 -0700367 @VisibleForTesting
368 public static SessionManager getSessionManager() {
Brad Ebinger51b98342016-09-22 16:30:46 -0700369 // Checking for null again outside of synchronization because we only need to synchronize
370 // during the lazy loading of the session logger. We don't need to synchronize elsewhere.
371 if (sSessionManager == null) {
372 synchronized (sSingletonSync) {
373 if (sSessionManager == null) {
374 sSessionManager = new SessionManager();
375 return sSessionManager;
376 }
377 }
378 }
379 return sSessionManager;
380 }
381
Brad Ebinger51b98342016-09-22 16:30:46 -0700382 public static void setTag(String tag) {
383 TAG = tag;
Brad Ebinger0c3541b2016-11-01 14:11:38 -0700384 DEBUG = isLoggable(android.util.Log.DEBUG);
385 INFO = isLoggable(android.util.Log.INFO);
386 VERBOSE = isLoggable(android.util.Log.VERBOSE);
387 WARN = isLoggable(android.util.Log.WARN);
388 ERROR = isLoggable(android.util.Log.ERROR);
Brad Ebinger51b98342016-09-22 16:30:46 -0700389 }
390
391 /**
Tyler Gunn4d54cca2023-03-16 23:00:04 +0000392 * Sets the main telecom sync lock used within Telecom. This is used when building log messages
393 * so that we can identify places in the code where we are doing something outside of the
394 * Telecom lock.
395 * @param lock The lock.
396 */
397 public static void setLock(Object lock) {
398 // Don't do lock monitoring on user builds.
399 if (!Build.IS_USER) {
400 sLock = lock;
401 }
402 }
403
404 /**
Brad Ebinger51b98342016-09-22 16:30:46 -0700405 * If user enabled extended logging is enabled and the time limit has passed, disables the
406 * extended logging.
407 */
408 private static void maybeDisableLogging() {
409 if (!sIsUserExtendedLoggingEnabled) {
410 return;
411 }
412
413 if (sUserExtendedLoggingStopTime < System.currentTimeMillis()) {
414 sUserExtendedLoggingStopTime = 0;
415 sIsUserExtendedLoggingEnabled = false;
416 }
417 }
418
Ihab Awad60ac30b2014-05-20 22:32:12 -0700419 public static boolean isLoggable(int level) {
420 return FORCE_LOGGING || android.util.Log.isLoggable(TAG, level);
421 }
422
Tyler Gunnfd74d53f2019-02-13 13:15:56 -0800423 /**
424 * Generates an obfuscated string for a calling handle in {@link Uri} format, or a raw phone
425 * phone number in {@link String} format.
426 * @param pii The information to obfuscate.
427 * @return The obfuscated string.
428 */
Brad Ebinger51b98342016-09-22 16:30:46 -0700429 public static String piiHandle(Object pii) {
430 if (pii == null || VERBOSE) {
431 return String.valueOf(pii);
Ihab Awad60ac30b2014-05-20 22:32:12 -0700432 }
Ihab Awad60ac30b2014-05-20 22:32:12 -0700433
Brad Ebinger51b98342016-09-22 16:30:46 -0700434 StringBuilder sb = new StringBuilder();
435 if (pii instanceof Uri) {
436 Uri uri = (Uri) pii;
437 String scheme = uri.getScheme();
438
439 if (!TextUtils.isEmpty(scheme)) {
440 sb.append(scheme).append(":");
441 }
442
443 String textToObfuscate = uri.getSchemeSpecificPart();
444 if (PhoneAccount.SCHEME_TEL.equals(scheme)) {
Tyler Gunnfd74d53f2019-02-13 13:15:56 -0800445 obfuscatePhoneNumber(sb, textToObfuscate);
Brad Ebinger51b98342016-09-22 16:30:46 -0700446 } else if (PhoneAccount.SCHEME_SIP.equals(scheme)) {
447 for (int i = 0; i < textToObfuscate.length(); i++) {
448 char c = textToObfuscate.charAt(i);
449 if (c != '@' && c != '.') {
450 c = '*';
451 }
452 sb.append(c);
453 }
454 } else {
455 sb.append(pii(pii));
456 }
Tyler Gunnfd74d53f2019-02-13 13:15:56 -0800457 } else if (pii instanceof String) {
458 String number = (String) pii;
459 obfuscatePhoneNumber(sb, number);
Ihab Awad60ac30b2014-05-20 22:32:12 -0700460 }
Ihab Awad60ac30b2014-05-20 22:32:12 -0700461
Brad Ebinger51b98342016-09-22 16:30:46 -0700462 return sb.toString();
Ihab Awad60ac30b2014-05-20 22:32:12 -0700463 }
464
465 /**
Tyler Gunnfd74d53f2019-02-13 13:15:56 -0800466 * Obfuscates a phone number, allowing NUM_DIALABLE_DIGITS_TO_LOG digits to be exposed for the
467 * phone number.
468 * @param sb String buffer to write obfuscated number to.
469 * @param phoneNumber The number to obfuscate.
470 */
471 private static void obfuscatePhoneNumber(StringBuilder sb, String phoneNumber) {
472 int numDigitsToObfuscate = getDialableCount(phoneNumber)
473 - NUM_DIALABLE_DIGITS_TO_LOG;
474 for (int i = 0; i < phoneNumber.length(); i++) {
475 char c = phoneNumber.charAt(i);
476 boolean isDialable = PhoneNumberUtils.isDialable(c);
477 if (isDialable) {
478 numDigitsToObfuscate--;
479 }
480 sb.append(isDialable && numDigitsToObfuscate >= 0 ? "*" : c);
481 }
482 }
483
484 /**
Tyler Gunn9bc35112018-04-23 09:52:25 -0700485 * Determines the number of dialable characters in a string.
486 * @param toCount The string to count dialable characters in.
487 * @return The count of dialable characters.
488 */
489 private static int getDialableCount(String toCount) {
490 int numDialable = 0;
491 for (char c : toCount.toCharArray()) {
492 if (PhoneNumberUtils.isDialable(c)) {
493 numDialable++;
494 }
495 }
496 return numDialable;
497 }
498
499 /**
Ihab Awad60ac30b2014-05-20 22:32:12 -0700500 * Redact personally identifiable information for production users.
youhei.x.miyoshib3cd7b52016-12-12 21:10:54 +0900501 * If we are running in verbose mode, return the original string,
Brad Ebingerf784b292017-12-22 13:45:27 -0800502 * and return "***" otherwise.
Ihab Awad60ac30b2014-05-20 22:32:12 -0700503 */
504 public static String pii(Object pii) {
505 if (pii == null || VERBOSE) {
506 return String.valueOf(pii);
507 }
Brad Ebingerf784b292017-12-22 13:45:27 -0800508 return "***";
Ihab Awad60ac30b2014-05-20 22:32:12 -0700509 }
510
511 private static String getPrefixFromObject(Object obj) {
512 return obj == null ? "<null>" : obj.getClass().getSimpleName();
513 }
514
515 private static String buildMessage(String prefix, String format, Object... args) {
Brad Ebinger51b98342016-09-22 16:30:46 -0700516 // Incorporate thread ID and calling method into prefix
517 String sessionName = getSessionId();
518 String sessionPostfix = TextUtils.isEmpty(sessionName) ? "" : ": " + sessionName;
519
Ihab Awad60ac30b2014-05-20 22:32:12 -0700520 String msg;
521 try {
522 msg = (args == null || args.length == 0) ? format
523 : String.format(Locale.US, format, args);
524 } catch (IllegalFormatException ife) {
Brad Ebinger4fb372f2016-10-05 15:47:28 -0700525 e(TAG, ife, "Log: IllegalFormatException: formatString='%s' numArgs=%d", format,
Ihab Awad60ac30b2014-05-20 22:32:12 -0700526 args.length);
527 msg = format + " (An error occurred while formatting the message.)";
528 }
Tyler Gunn4d54cca2023-03-16 23:00:04 +0000529 // If a lock was set, check if this thread holds that lock and output an emoji that lets
530 // the developer know whether a log message came from within the Telecom lock or not.
531 String isLocked = sLock != null ? (Thread.holdsLock(sLock) ? "\uD83D\uDD12" : "❗") : "";
532 return String.format(Locale.US, "%s: %s%s%s", prefix, msg, sessionPostfix, isLocked);
Ihab Awad60ac30b2014-05-20 22:32:12 -0700533 }
Tyler Gunnffbcd892020-05-04 15:01:59 -0700534
535 /**
536 * Generates an abbreviated version of the package name from a component.
537 * E.g. com.android.phone becomes cap
538 * @param componentName The component name to abbreviate.
539 * @return Abbreviation of empty string if component is null.
540 * @hide
541 */
542 public static String getPackageAbbreviation(ComponentName componentName) {
543 if (componentName == null) {
544 return "";
545 }
546 return getPackageAbbreviation(componentName.getPackageName());
547 }
548
549 /**
550 * Generates an abbreviated version of the package name.
551 * E.g. com.android.phone becomes cap
552 * @param packageName The packageName name to abbreviate.
553 * @return Abbreviation of empty string if package is null.
554 * @hide
555 */
556 public static String getPackageAbbreviation(String packageName) {
557 if (packageName == null) {
558 return "";
559 }
560 return Arrays.stream(packageName.split("\\."))
Tyler Gunnd5821842021-02-05 11:12:57 -0800561 .map(s -> s.length() == 0 ? "" : s.substring(0, 1))
Tyler Gunnffbcd892020-05-04 15:01:59 -0700562 .collect(Collectors.joining(""));
563 }
Ihab Awad60ac30b2014-05-20 22:32:12 -0700564}