Loading services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java +113 −0 Original line number Diff line number Diff line Loading @@ -60,6 +60,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.LocalServices; import com.android.server.pm.UserManagerInternal; import com.android.server.utils.EventLogger; import java.io.PrintWriter; import java.lang.ref.WeakReference; Loading Loading @@ -88,6 +89,8 @@ class MediaRouter2ServiceImpl { private static final long DUMMY_REQUEST_ID = -1; private static final int PACKAGE_IMPORTANCE_FOR_DISCOVERY = IMPORTANCE_FOREGROUND_SERVICE; private static final int DUMP_EVENTS_MAX_COUNT = 70; private final Context mContext; private final UserManagerInternal mUserManagerInternal; private final Object mLock = new Object(); Loading @@ -104,6 +107,9 @@ class MediaRouter2ServiceImpl { @GuardedBy("mLock") private int mCurrentActiveUserId = -1; private final EventLogger mEventLogger = new EventLogger(DUMP_EVENTS_MAX_COUNT, "MediaRouter2ServiceImpl"); private final ActivityManager.OnUidImportanceListener mOnUidImportanceListener = (uid, importance) -> { synchronized (mLock) { Loading @@ -125,6 +131,8 @@ class MediaRouter2ServiceImpl { UserHandler::updateDiscoveryPreferenceOnHandler, userHandler)); } } mEventLogger.log(new EventLogger.StringEvent("mScreenOnOffReceiver", null)); } }; Loading Loading @@ -628,6 +636,10 @@ class MediaRouter2ServiceImpl { /* package */ void updateRunningUserAndProfiles(int newActiveUserId) { synchronized (mLock) { if (mCurrentActiveUserId != newActiveUserId) { mEventLogger.log( EventLogger.StringEvent.from("switchUser", "userId: %d", newActiveUserId)); mCurrentActiveUserId = newActiveUserId; for (int i = 0; i < mUserRecords.size(); i++) { int userId = mUserRecords.keyAt(i); Loading Loading @@ -699,6 +711,10 @@ class MediaRouter2ServiceImpl { userRecord.mHandler.sendMessage( obtainMessage(UserHandler::notifyRouterRegistered, userRecord.mHandler, routerRecord)); mEventLogger.log(EventLogger.StringEvent.from("registerRouter2", "package: %s, uid: %d, pid: %d, router id: %d", packageName, uid, pid, routerRecord.mRouterId)); } @GuardedBy("mLock") Loading @@ -709,6 +725,9 @@ class MediaRouter2ServiceImpl { return; } mEventLogger.log(EventLogger.StringEvent.from("unregisterRouter2", "router id: %d", routerRecord.mRouterId)); UserRecord userRecord = routerRecord.mUserRecord; userRecord.mRouterRecords.remove(routerRecord); routerRecord.mUserRecord.mHandler.sendMessage( Loading @@ -727,6 +746,12 @@ class MediaRouter2ServiceImpl { if (routerRecord.mDiscoveryPreference.equals(discoveryRequest)) { return; } mEventLogger.log(EventLogger.StringEvent.from( "setDiscoveryRequestWithRouter2", "router id: %d, discovery request: %s", routerRecord.mRouterId, discoveryRequest.toString())); routerRecord.mDiscoveryPreference = discoveryRequest; routerRecord.mUserRecord.mHandler.sendMessage( obtainMessage(UserHandler::notifyDiscoveryPreferenceChangedToManagers, Loading @@ -744,6 +769,11 @@ class MediaRouter2ServiceImpl { RouterRecord routerRecord = mAllRouterRecords.get(binder); if (routerRecord != null) { mEventLogger.log(EventLogger.StringEvent.from( "setRouteVolumeWithRouter2", "router id: %d, volume: %d", routerRecord.mRouterId, volume)); routerRecord.mUserRecord.mHandler.sendMessage( obtainMessage(UserHandler::setRouteVolumeOnHandler, routerRecord.mUserRecord.mHandler, Loading Loading @@ -824,6 +854,11 @@ class MediaRouter2ServiceImpl { return; } mEventLogger.log(EventLogger.StringEvent.from( "selectRouteWithRouter2", "router id: %d, route: %s", routerRecord.mRouterId, route.getId())); routerRecord.mUserRecord.mHandler.sendMessage( obtainMessage(UserHandler::selectRouteOnHandler, routerRecord.mUserRecord.mHandler, Loading @@ -839,6 +874,11 @@ class MediaRouter2ServiceImpl { return; } mEventLogger.log(EventLogger.StringEvent.from( "deselectRouteWithRouter2", "router id: %d, route: %s", routerRecord.mRouterId, route.getId())); routerRecord.mUserRecord.mHandler.sendMessage( obtainMessage(UserHandler::deselectRouteOnHandler, routerRecord.mUserRecord.mHandler, Loading @@ -854,6 +894,11 @@ class MediaRouter2ServiceImpl { return; } mEventLogger.log(EventLogger.StringEvent.from( "transferToRouteWithRouter2", "router id: %d, route: %s", routerRecord.mRouterId, route.getId())); String defaultRouteId = routerRecord.mUserRecord.mHandler.mSystemProvider.getDefaultRoute().getId(); if (route.isSystemRoute() && !routerRecord.mHasModifyAudioRoutingPermission Loading @@ -879,6 +924,11 @@ class MediaRouter2ServiceImpl { return; } mEventLogger.log(EventLogger.StringEvent.from( "setSessionVolumeWithRouter2", "router id: %d, session: %s, volume: %d", routerRecord.mRouterId, uniqueSessionId, volume)); routerRecord.mUserRecord.mHandler.sendMessage( obtainMessage(UserHandler::setSessionVolumeOnHandler, routerRecord.mUserRecord.mHandler, Loading @@ -894,6 +944,11 @@ class MediaRouter2ServiceImpl { return; } mEventLogger.log(EventLogger.StringEvent.from( "releaseSessionWithRouter2", "router id: %d, session: %s", routerRecord.mRouterId, uniqueSessionId)); routerRecord.mUserRecord.mHandler.sendMessage( obtainMessage(UserHandler::releaseSessionOnHandler, routerRecord.mUserRecord.mHandler, Loading Loading @@ -936,6 +991,11 @@ class MediaRouter2ServiceImpl { return; } mEventLogger.log( EventLogger.StringEvent.from("registerManager", "uid: %d, pid: %d, package: %s, userId: %d", uid, pid, packageName, userId)); mContext.enforcePermission(Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid, "Must hold MEDIA_CONTENT_CONTROL permission."); Loading Loading @@ -972,6 +1032,12 @@ class MediaRouter2ServiceImpl { return; } UserRecord userRecord = managerRecord.mUserRecord; mEventLogger.log( EventLogger.StringEvent.from("unregisterManager", "userId: %d, managerId: %d", userRecord.mUserId, managerRecord.mManagerId)); userRecord.mManagerRecords.remove(managerRecord); managerRecord.dispose(); disposeUserIfNeededLocked(userRecord); // since manager removed from user Loading @@ -983,6 +1049,11 @@ class MediaRouter2ServiceImpl { if (managerRecord == null) { return; } mEventLogger.log( EventLogger.StringEvent.from("startScan", "manager: %d", managerRecord.mManagerId)); managerRecord.startScan(); } Loading @@ -992,6 +1063,11 @@ class MediaRouter2ServiceImpl { if (managerRecord == null) { return; } mEventLogger.log( EventLogger.StringEvent.from("stopScan", "manager: %d", managerRecord.mManagerId)); managerRecord.stopScan(); } Loading @@ -1005,6 +1081,11 @@ class MediaRouter2ServiceImpl { return; } mEventLogger.log( EventLogger.StringEvent.from("setRouteVolumeWithManager", "managerId: %d, routeId: %s, volume: %d", managerRecord.mManagerId, route.getId(), volume)); long uniqueRequestId = toUniqueRequestId(managerRecord.mManagerId, requestId); managerRecord.mUserRecord.mHandler.sendMessage( obtainMessage(UserHandler::setRouteVolumeOnHandler, Loading @@ -1020,6 +1101,11 @@ class MediaRouter2ServiceImpl { return; } mEventLogger.log( EventLogger.StringEvent.from("requestCreateSessionWithManager", "managerId: %d, routeId: %s", managerRecord.mManagerId, route.getId())); String packageName = oldSession.getClientPackageName(); RouterRecord routerRecord = managerRecord.mUserRecord.findRouterRecordLocked(packageName); Loading Loading @@ -1065,6 +1151,11 @@ class MediaRouter2ServiceImpl { return; } mEventLogger.log( EventLogger.StringEvent.from("selectRouteWithManager", "managerId: %d, session: %s, routeId: %s", managerRecord.mManagerId, uniqueSessionId, route.getId())); // Can be null if the session is system's or RCN. RouterRecord routerRecord = managerRecord.mUserRecord.mHandler .findRouterWithSessionLocked(uniqueSessionId); Loading @@ -1086,6 +1177,11 @@ class MediaRouter2ServiceImpl { return; } mEventLogger.log( EventLogger.StringEvent.from("deselectRouteWithManager", "managerId: %d, session: %s, routeId: %s", managerRecord.mManagerId, uniqueSessionId, route.getId())); // Can be null if the session is system's or RCN. RouterRecord routerRecord = managerRecord.mUserRecord.mHandler .findRouterWithSessionLocked(uniqueSessionId); Loading @@ -1107,6 +1203,11 @@ class MediaRouter2ServiceImpl { return; } mEventLogger.log( EventLogger.StringEvent.from("transferToRouteWithManager", "managerId: %d, session: %s, routeId: %s", managerRecord.mManagerId, uniqueSessionId, route.getId())); // Can be null if the session is system's or RCN. RouterRecord routerRecord = managerRecord.mUserRecord.mHandler .findRouterWithSessionLocked(uniqueSessionId); Loading @@ -1128,6 +1229,11 @@ class MediaRouter2ServiceImpl { return; } mEventLogger.log( EventLogger.StringEvent.from("setSessionVolumeWithManager", "managerId: %d, session: %s, volume: %d", managerRecord.mManagerId, uniqueSessionId, volume)); long uniqueRequestId = toUniqueRequestId(managerRecord.mManagerId, requestId); managerRecord.mUserRecord.mHandler.sendMessage( obtainMessage(UserHandler::setSessionVolumeOnHandler, Loading @@ -1145,6 +1251,11 @@ class MediaRouter2ServiceImpl { return; } mEventLogger.log( EventLogger.StringEvent.from("releaseSessionWithManager", "managerId: %d, session: %s", managerRecord.mManagerId, uniqueSessionId)); RouterRecord routerRecord = managerRecord.mUserRecord.mHandler .findRouterWithSessionLocked(uniqueSessionId); Loading Loading @@ -1264,6 +1375,8 @@ class MediaRouter2ServiceImpl { if (!mHandler.runWithScissors(() -> mHandler.dump(pw, indent), 1000)) { pw.println(indent + "<could not dump handler state>"); } mEventLogger.dump(pw, indent); } } Loading services/core/java/com/android/server/utils/EventLogger.java +36 −7 Original line number Diff line number Diff line Loading @@ -17,6 +17,8 @@ package com.android.server.utils; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.util.Log; import java.io.PrintWriter; Loading Loading @@ -87,12 +89,17 @@ public class EventLogger { log(event.printLog(logType, tag)); } /** Dumps events using {@link PrintWriter} */ /** Dumps events using {@link PrintWriter}. */ public synchronized void dump(PrintWriter pw) { pw.println("Events log: " + mTag); dump(pw, "" /* prefix */); } /** Dumps events using {@link PrintWriter} with a certain indent. */ public synchronized void dump(PrintWriter pw, String prefix) { pw.println(prefix + "Events log: " + mTag); String indent = prefix + " "; for (Event evt : mEvents) { pw.println(evt.toString()); pw.println(indent + evt.toString()); } } Loading Loading @@ -180,15 +187,37 @@ public class EventLogger { } public static class StringEvent extends Event { private final String mMsg; public StringEvent(String msg) { mMsg = msg; @Nullable private final String mSource; private final String mDescription; /** Creates event from {@code source} and formatted {@code description} with {@code args} */ public static StringEvent from(@NonNull String source, @NonNull String description, Object... args) { return new StringEvent(source, String.format(Locale.US, description, args)); } public StringEvent(String description) { this(null /* source */, description); } public StringEvent(String source, String description) { mSource = source; mDescription = description; } @Override public String eventToString() { return mMsg; if (mSource == null) { return mDescription; } // [source ] optional description return String.format("[%-40s] %s", mSource, (mDescription == null ? "" : mDescription)); } } } Loading
services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java +113 −0 Original line number Diff line number Diff line Loading @@ -60,6 +60,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.LocalServices; import com.android.server.pm.UserManagerInternal; import com.android.server.utils.EventLogger; import java.io.PrintWriter; import java.lang.ref.WeakReference; Loading Loading @@ -88,6 +89,8 @@ class MediaRouter2ServiceImpl { private static final long DUMMY_REQUEST_ID = -1; private static final int PACKAGE_IMPORTANCE_FOR_DISCOVERY = IMPORTANCE_FOREGROUND_SERVICE; private static final int DUMP_EVENTS_MAX_COUNT = 70; private final Context mContext; private final UserManagerInternal mUserManagerInternal; private final Object mLock = new Object(); Loading @@ -104,6 +107,9 @@ class MediaRouter2ServiceImpl { @GuardedBy("mLock") private int mCurrentActiveUserId = -1; private final EventLogger mEventLogger = new EventLogger(DUMP_EVENTS_MAX_COUNT, "MediaRouter2ServiceImpl"); private final ActivityManager.OnUidImportanceListener mOnUidImportanceListener = (uid, importance) -> { synchronized (mLock) { Loading @@ -125,6 +131,8 @@ class MediaRouter2ServiceImpl { UserHandler::updateDiscoveryPreferenceOnHandler, userHandler)); } } mEventLogger.log(new EventLogger.StringEvent("mScreenOnOffReceiver", null)); } }; Loading Loading @@ -628,6 +636,10 @@ class MediaRouter2ServiceImpl { /* package */ void updateRunningUserAndProfiles(int newActiveUserId) { synchronized (mLock) { if (mCurrentActiveUserId != newActiveUserId) { mEventLogger.log( EventLogger.StringEvent.from("switchUser", "userId: %d", newActiveUserId)); mCurrentActiveUserId = newActiveUserId; for (int i = 0; i < mUserRecords.size(); i++) { int userId = mUserRecords.keyAt(i); Loading Loading @@ -699,6 +711,10 @@ class MediaRouter2ServiceImpl { userRecord.mHandler.sendMessage( obtainMessage(UserHandler::notifyRouterRegistered, userRecord.mHandler, routerRecord)); mEventLogger.log(EventLogger.StringEvent.from("registerRouter2", "package: %s, uid: %d, pid: %d, router id: %d", packageName, uid, pid, routerRecord.mRouterId)); } @GuardedBy("mLock") Loading @@ -709,6 +725,9 @@ class MediaRouter2ServiceImpl { return; } mEventLogger.log(EventLogger.StringEvent.from("unregisterRouter2", "router id: %d", routerRecord.mRouterId)); UserRecord userRecord = routerRecord.mUserRecord; userRecord.mRouterRecords.remove(routerRecord); routerRecord.mUserRecord.mHandler.sendMessage( Loading @@ -727,6 +746,12 @@ class MediaRouter2ServiceImpl { if (routerRecord.mDiscoveryPreference.equals(discoveryRequest)) { return; } mEventLogger.log(EventLogger.StringEvent.from( "setDiscoveryRequestWithRouter2", "router id: %d, discovery request: %s", routerRecord.mRouterId, discoveryRequest.toString())); routerRecord.mDiscoveryPreference = discoveryRequest; routerRecord.mUserRecord.mHandler.sendMessage( obtainMessage(UserHandler::notifyDiscoveryPreferenceChangedToManagers, Loading @@ -744,6 +769,11 @@ class MediaRouter2ServiceImpl { RouterRecord routerRecord = mAllRouterRecords.get(binder); if (routerRecord != null) { mEventLogger.log(EventLogger.StringEvent.from( "setRouteVolumeWithRouter2", "router id: %d, volume: %d", routerRecord.mRouterId, volume)); routerRecord.mUserRecord.mHandler.sendMessage( obtainMessage(UserHandler::setRouteVolumeOnHandler, routerRecord.mUserRecord.mHandler, Loading Loading @@ -824,6 +854,11 @@ class MediaRouter2ServiceImpl { return; } mEventLogger.log(EventLogger.StringEvent.from( "selectRouteWithRouter2", "router id: %d, route: %s", routerRecord.mRouterId, route.getId())); routerRecord.mUserRecord.mHandler.sendMessage( obtainMessage(UserHandler::selectRouteOnHandler, routerRecord.mUserRecord.mHandler, Loading @@ -839,6 +874,11 @@ class MediaRouter2ServiceImpl { return; } mEventLogger.log(EventLogger.StringEvent.from( "deselectRouteWithRouter2", "router id: %d, route: %s", routerRecord.mRouterId, route.getId())); routerRecord.mUserRecord.mHandler.sendMessage( obtainMessage(UserHandler::deselectRouteOnHandler, routerRecord.mUserRecord.mHandler, Loading @@ -854,6 +894,11 @@ class MediaRouter2ServiceImpl { return; } mEventLogger.log(EventLogger.StringEvent.from( "transferToRouteWithRouter2", "router id: %d, route: %s", routerRecord.mRouterId, route.getId())); String defaultRouteId = routerRecord.mUserRecord.mHandler.mSystemProvider.getDefaultRoute().getId(); if (route.isSystemRoute() && !routerRecord.mHasModifyAudioRoutingPermission Loading @@ -879,6 +924,11 @@ class MediaRouter2ServiceImpl { return; } mEventLogger.log(EventLogger.StringEvent.from( "setSessionVolumeWithRouter2", "router id: %d, session: %s, volume: %d", routerRecord.mRouterId, uniqueSessionId, volume)); routerRecord.mUserRecord.mHandler.sendMessage( obtainMessage(UserHandler::setSessionVolumeOnHandler, routerRecord.mUserRecord.mHandler, Loading @@ -894,6 +944,11 @@ class MediaRouter2ServiceImpl { return; } mEventLogger.log(EventLogger.StringEvent.from( "releaseSessionWithRouter2", "router id: %d, session: %s", routerRecord.mRouterId, uniqueSessionId)); routerRecord.mUserRecord.mHandler.sendMessage( obtainMessage(UserHandler::releaseSessionOnHandler, routerRecord.mUserRecord.mHandler, Loading Loading @@ -936,6 +991,11 @@ class MediaRouter2ServiceImpl { return; } mEventLogger.log( EventLogger.StringEvent.from("registerManager", "uid: %d, pid: %d, package: %s, userId: %d", uid, pid, packageName, userId)); mContext.enforcePermission(Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid, "Must hold MEDIA_CONTENT_CONTROL permission."); Loading Loading @@ -972,6 +1032,12 @@ class MediaRouter2ServiceImpl { return; } UserRecord userRecord = managerRecord.mUserRecord; mEventLogger.log( EventLogger.StringEvent.from("unregisterManager", "userId: %d, managerId: %d", userRecord.mUserId, managerRecord.mManagerId)); userRecord.mManagerRecords.remove(managerRecord); managerRecord.dispose(); disposeUserIfNeededLocked(userRecord); // since manager removed from user Loading @@ -983,6 +1049,11 @@ class MediaRouter2ServiceImpl { if (managerRecord == null) { return; } mEventLogger.log( EventLogger.StringEvent.from("startScan", "manager: %d", managerRecord.mManagerId)); managerRecord.startScan(); } Loading @@ -992,6 +1063,11 @@ class MediaRouter2ServiceImpl { if (managerRecord == null) { return; } mEventLogger.log( EventLogger.StringEvent.from("stopScan", "manager: %d", managerRecord.mManagerId)); managerRecord.stopScan(); } Loading @@ -1005,6 +1081,11 @@ class MediaRouter2ServiceImpl { return; } mEventLogger.log( EventLogger.StringEvent.from("setRouteVolumeWithManager", "managerId: %d, routeId: %s, volume: %d", managerRecord.mManagerId, route.getId(), volume)); long uniqueRequestId = toUniqueRequestId(managerRecord.mManagerId, requestId); managerRecord.mUserRecord.mHandler.sendMessage( obtainMessage(UserHandler::setRouteVolumeOnHandler, Loading @@ -1020,6 +1101,11 @@ class MediaRouter2ServiceImpl { return; } mEventLogger.log( EventLogger.StringEvent.from("requestCreateSessionWithManager", "managerId: %d, routeId: %s", managerRecord.mManagerId, route.getId())); String packageName = oldSession.getClientPackageName(); RouterRecord routerRecord = managerRecord.mUserRecord.findRouterRecordLocked(packageName); Loading Loading @@ -1065,6 +1151,11 @@ class MediaRouter2ServiceImpl { return; } mEventLogger.log( EventLogger.StringEvent.from("selectRouteWithManager", "managerId: %d, session: %s, routeId: %s", managerRecord.mManagerId, uniqueSessionId, route.getId())); // Can be null if the session is system's or RCN. RouterRecord routerRecord = managerRecord.mUserRecord.mHandler .findRouterWithSessionLocked(uniqueSessionId); Loading @@ -1086,6 +1177,11 @@ class MediaRouter2ServiceImpl { return; } mEventLogger.log( EventLogger.StringEvent.from("deselectRouteWithManager", "managerId: %d, session: %s, routeId: %s", managerRecord.mManagerId, uniqueSessionId, route.getId())); // Can be null if the session is system's or RCN. RouterRecord routerRecord = managerRecord.mUserRecord.mHandler .findRouterWithSessionLocked(uniqueSessionId); Loading @@ -1107,6 +1203,11 @@ class MediaRouter2ServiceImpl { return; } mEventLogger.log( EventLogger.StringEvent.from("transferToRouteWithManager", "managerId: %d, session: %s, routeId: %s", managerRecord.mManagerId, uniqueSessionId, route.getId())); // Can be null if the session is system's or RCN. RouterRecord routerRecord = managerRecord.mUserRecord.mHandler .findRouterWithSessionLocked(uniqueSessionId); Loading @@ -1128,6 +1229,11 @@ class MediaRouter2ServiceImpl { return; } mEventLogger.log( EventLogger.StringEvent.from("setSessionVolumeWithManager", "managerId: %d, session: %s, volume: %d", managerRecord.mManagerId, uniqueSessionId, volume)); long uniqueRequestId = toUniqueRequestId(managerRecord.mManagerId, requestId); managerRecord.mUserRecord.mHandler.sendMessage( obtainMessage(UserHandler::setSessionVolumeOnHandler, Loading @@ -1145,6 +1251,11 @@ class MediaRouter2ServiceImpl { return; } mEventLogger.log( EventLogger.StringEvent.from("releaseSessionWithManager", "managerId: %d, session: %s", managerRecord.mManagerId, uniqueSessionId)); RouterRecord routerRecord = managerRecord.mUserRecord.mHandler .findRouterWithSessionLocked(uniqueSessionId); Loading Loading @@ -1264,6 +1375,8 @@ class MediaRouter2ServiceImpl { if (!mHandler.runWithScissors(() -> mHandler.dump(pw, indent), 1000)) { pw.println(indent + "<could not dump handler state>"); } mEventLogger.dump(pw, indent); } } Loading
services/core/java/com/android/server/utils/EventLogger.java +36 −7 Original line number Diff line number Diff line Loading @@ -17,6 +17,8 @@ package com.android.server.utils; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.util.Log; import java.io.PrintWriter; Loading Loading @@ -87,12 +89,17 @@ public class EventLogger { log(event.printLog(logType, tag)); } /** Dumps events using {@link PrintWriter} */ /** Dumps events using {@link PrintWriter}. */ public synchronized void dump(PrintWriter pw) { pw.println("Events log: " + mTag); dump(pw, "" /* prefix */); } /** Dumps events using {@link PrintWriter} with a certain indent. */ public synchronized void dump(PrintWriter pw, String prefix) { pw.println(prefix + "Events log: " + mTag); String indent = prefix + " "; for (Event evt : mEvents) { pw.println(evt.toString()); pw.println(indent + evt.toString()); } } Loading Loading @@ -180,15 +187,37 @@ public class EventLogger { } public static class StringEvent extends Event { private final String mMsg; public StringEvent(String msg) { mMsg = msg; @Nullable private final String mSource; private final String mDescription; /** Creates event from {@code source} and formatted {@code description} with {@code args} */ public static StringEvent from(@NonNull String source, @NonNull String description, Object... args) { return new StringEvent(source, String.format(Locale.US, description, args)); } public StringEvent(String description) { this(null /* source */, description); } public StringEvent(String source, String description) { mSource = source; mDescription = description; } @Override public String eventToString() { return mMsg; if (mSource == null) { return mDescription; } // [source ] optional description return String.format("[%-40s] %s", mSource, (mDescription == null ? "" : mDescription)); } } }