Merge "Add support for starting external Sessions"
am: 4ce60695f9
Change-Id: I6957c8e2846d216f77f5f6337388e1fc296511de
This commit is contained in:
@ -188,6 +188,15 @@ public class Log {
|
|||||||
getSessionManager().startSession(shortMethodName, callerIdentification);
|
getSessionManager().startSession(shortMethodName, callerIdentification);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void startSession(Session.Info info, String shortMethodName,
|
||||||
|
String callerIdentification) {
|
||||||
|
getSessionManager().startSession(info, shortMethodName, callerIdentification);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void startExternalSession(Session.Info sessionInfo, String shortMethodName) {
|
||||||
|
getSessionManager().startExternalSession(sessionInfo, shortMethodName);
|
||||||
|
}
|
||||||
|
|
||||||
public static Session createSubsession() {
|
public static Session createSubsession() {
|
||||||
return getSessionManager().createSubsession();
|
return getSessionManager().createSubsession();
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,11 @@
|
|||||||
package android.telecom.Logging;
|
package android.telecom.Logging;
|
||||||
|
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
|
import android.os.Parcel;
|
||||||
|
import android.os.Parcelable;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
@ -33,12 +38,58 @@ public class Session {
|
|||||||
public static final String END_SUBSESSION = "END_SUBSESSION";
|
public static final String END_SUBSESSION = "END_SUBSESSION";
|
||||||
public static final String END_SESSION = "END_SESSION";
|
public static final String END_SESSION = "END_SESSION";
|
||||||
|
|
||||||
|
public static final String SUBSESSION_SEPARATION_CHAR = "->";
|
||||||
|
public static final String EXTERNAL_INDICATOR = "E-";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initial value of mExecutionEndTimeMs and the final value of {@link #getLocalExecutionTime()}
|
* Initial value of mExecutionEndTimeMs and the final value of {@link #getLocalExecutionTime()}
|
||||||
* if the Session is canceled.
|
* if the Session is canceled.
|
||||||
*/
|
*/
|
||||||
public static final int UNDEFINED = -1;
|
public static final int UNDEFINED = -1;
|
||||||
|
|
||||||
|
public static class Info implements Parcelable {
|
||||||
|
public final String sessionId;
|
||||||
|
public final String shortMethodName;
|
||||||
|
|
||||||
|
private Info(String id, String methodName) {
|
||||||
|
sessionId = id;
|
||||||
|
shortMethodName = methodName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Info getInfo (Session s) {
|
||||||
|
return new Info(s.getFullSessionId(), s.getShortMethodName());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Responsible for creating Info objects for deserialized Parcels. */
|
||||||
|
public static final Parcelable.Creator<Info> CREATOR =
|
||||||
|
new Parcelable.Creator<Info> () {
|
||||||
|
@Override
|
||||||
|
public Info createFromParcel(Parcel source) {
|
||||||
|
String id = source.readString();
|
||||||
|
String methodName = source.readString();
|
||||||
|
return new Info(id, methodName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Info[] newArray(int size) {
|
||||||
|
return new Info[size];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** {@inheritDoc} */
|
||||||
|
@Override
|
||||||
|
public int describeContents() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Writes Info object into a Parcel. */
|
||||||
|
@Override
|
||||||
|
public void writeToParcel(Parcel destination, int flags) {
|
||||||
|
destination.writeString(sessionId);
|
||||||
|
destination.writeString(shortMethodName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private String mSessionId;
|
private String mSessionId;
|
||||||
private String mShortMethodName;
|
private String mShortMethodName;
|
||||||
private long mExecutionStartTimeMs;
|
private long mExecutionStartTimeMs;
|
||||||
@ -46,6 +97,7 @@ public class Session {
|
|||||||
private Session mParentSession;
|
private Session mParentSession;
|
||||||
private ArrayList<Session> mChildSessions;
|
private ArrayList<Session> mChildSessions;
|
||||||
private boolean mIsCompleted = false;
|
private boolean mIsCompleted = false;
|
||||||
|
private boolean mIsExternal = false;
|
||||||
private int mChildCounter = 0;
|
private int mChildCounter = 0;
|
||||||
// True if this is a subsession that has been started from the same thread as the parent
|
// True if this is a subsession that has been started from the same thread as the parent
|
||||||
// session. This can happen if Log.startSession(...) is called multiple times on the same
|
// session. This can happen if Log.startSession(...) is called multiple times on the same
|
||||||
@ -56,8 +108,11 @@ public class Session {
|
|||||||
// Optionally provided info about the method/class/component that started the session in order
|
// Optionally provided info about the method/class/component that started the session in order
|
||||||
// to make Logging easier. This info will be provided in parentheses along with the session.
|
// to make Logging easier. This info will be provided in parentheses along with the session.
|
||||||
private String mOwnerInfo;
|
private String mOwnerInfo;
|
||||||
|
// Cache Full Method path so that recursive population of the full method path only needs to
|
||||||
|
// be calculated once.
|
||||||
|
private String mFullMethodPathCache;
|
||||||
|
|
||||||
public Session(String sessionId, String shortMethodName, long startTimeMs, long threadID,
|
public Session(String sessionId, String shortMethodName, long startTimeMs,
|
||||||
boolean isStartedFromActiveSession, String ownerInfo) {
|
boolean isStartedFromActiveSession, String ownerInfo) {
|
||||||
setSessionId(sessionId);
|
setSessionId(sessionId);
|
||||||
setShortMethodName(shortMethodName);
|
setShortMethodName(shortMethodName);
|
||||||
@ -86,6 +141,14 @@ public class Session {
|
|||||||
mShortMethodName = shortMethodName;
|
mShortMethodName = shortMethodName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setIsExternal(boolean isExternal) {
|
||||||
|
mIsExternal = isExternal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isExternal() {
|
||||||
|
return mIsExternal;
|
||||||
|
}
|
||||||
|
|
||||||
public void setParentSession(Session parentSession) {
|
public void setParentSession(Session parentSession) {
|
||||||
mParentSession = parentSession;
|
mParentSession = parentSession;
|
||||||
}
|
}
|
||||||
@ -126,6 +189,15 @@ public class Session {
|
|||||||
return mIsStartedFromActiveSession;
|
return mIsStartedFromActiveSession;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Info getInfo() {
|
||||||
|
return Info.getInfo(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public String getSessionId() {
|
||||||
|
return mSessionId;
|
||||||
|
}
|
||||||
|
|
||||||
// Mark this session complete. This will be deleted by Log when all subsessions are complete
|
// Mark this session complete. This will be deleted by Log when all subsessions are complete
|
||||||
// as well.
|
// as well.
|
||||||
public void markSessionCompleted(long executionEndTimeMs) {
|
public void markSessionCompleted(long executionEndTimeMs) {
|
||||||
@ -186,6 +258,38 @@ public class Session {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Recursively concatenate mShortMethodName with the parent Sessions to create full method
|
||||||
|
// path. Caches this string so that multiple calls for the path will be quick.
|
||||||
|
public String getFullMethodPath() {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
getFullMethodPath(sb);
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private synchronized void getFullMethodPath(StringBuilder sb) {
|
||||||
|
// Don't calculate if we have already figured it out!
|
||||||
|
if (!TextUtils.isEmpty(mFullMethodPathCache)) {
|
||||||
|
sb.append(mFullMethodPathCache);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Session parentSession = getParentSession();
|
||||||
|
boolean isSessionStarted = false;
|
||||||
|
if (parentSession != null) {
|
||||||
|
// Check to see if the session has been renamed yet. If it has not, then the session
|
||||||
|
// has not been continued.
|
||||||
|
isSessionStarted = !mShortMethodName.equals(parentSession.mShortMethodName);
|
||||||
|
parentSession.getFullMethodPath(sb);
|
||||||
|
sb.append(SUBSESSION_SEPARATION_CHAR);
|
||||||
|
}
|
||||||
|
sb.append(mShortMethodName);
|
||||||
|
|
||||||
|
if(isSessionStarted) {
|
||||||
|
// Cache this value so that we do not have to do this work next time!
|
||||||
|
// We do not cache the value if the session being evaluated hasn't been continued yet.
|
||||||
|
mFullMethodPathCache = sb.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
int result = mSessionId != null ? mSessionId.hashCode() : 0;
|
int result = mSessionId != null ? mSessionId.hashCode() : 0;
|
||||||
@ -238,7 +342,7 @@ public class Session {
|
|||||||
return mParentSession.toString();
|
return mParentSession.toString();
|
||||||
} else {
|
} else {
|
||||||
StringBuilder methodName = new StringBuilder();
|
StringBuilder methodName = new StringBuilder();
|
||||||
methodName.append(mShortMethodName);
|
methodName.append(getFullMethodPath());
|
||||||
if (mOwnerInfo != null && !mOwnerInfo.isEmpty()) {
|
if (mOwnerInfo != null && !mOwnerInfo.isEmpty()) {
|
||||||
methodName.append("(InCall package: ");
|
methodName.append("(InCall package: ");
|
||||||
methodName.append(mOwnerInfo);
|
methodName.append(mOwnerInfo);
|
||||||
|
@ -21,6 +21,7 @@ import android.os.Handler;
|
|||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.os.Process;
|
import android.os.Process;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
|
import android.telecom.Log;
|
||||||
import android.util.Base64;
|
import android.util.Base64;
|
||||||
|
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
@ -41,12 +42,9 @@ public class SessionManager {
|
|||||||
|
|
||||||
// Currently using 3 letters, So don't exceed 64^3
|
// Currently using 3 letters, So don't exceed 64^3
|
||||||
private static final long SESSION_ID_ROLLOVER_THRESHOLD = 262144;
|
private static final long SESSION_ID_ROLLOVER_THRESHOLD = 262144;
|
||||||
|
|
||||||
// This parameter can be overridden in Telecom's Timeouts class.
|
// This parameter can be overridden in Telecom's Timeouts class.
|
||||||
public static final long DEFAULT_SESSION_TIMEOUT_MS = 30000L; // 30 seconds
|
private static final long DEFAULT_SESSION_TIMEOUT_MS = 30000L; // 30 seconds
|
||||||
|
private static final String LOGGING_TAG = "Logging";
|
||||||
private static String LOGGING_TAG = "Logging";
|
|
||||||
|
|
||||||
private static final String TIMEOUTS_PREFIX = "telecom.";
|
private static final String TIMEOUTS_PREFIX = "telecom.";
|
||||||
|
|
||||||
// Synchronized in all method calls
|
// Synchronized in all method calls
|
||||||
@ -56,10 +54,9 @@ public class SessionManager {
|
|||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public ConcurrentHashMap<Integer, Session> mSessionMapper = new ConcurrentHashMap<>(100);
|
public ConcurrentHashMap<Integer, Session> mSessionMapper = new ConcurrentHashMap<>(100);
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public Handler mSessionCleanupHandler = new Handler(Looper.getMainLooper());
|
|
||||||
@VisibleForTesting
|
|
||||||
public java.lang.Runnable mCleanStaleSessions = () ->
|
public java.lang.Runnable mCleanStaleSessions = () ->
|
||||||
cleanupStaleSessions(getSessionCleanupTimeoutMs());
|
cleanupStaleSessions(getSessionCleanupTimeoutMs());
|
||||||
|
private Handler mSessionCleanupHandler = new Handler(Looper.getMainLooper());
|
||||||
|
|
||||||
// Overridden in LogTest to skip query to ContentProvider
|
// Overridden in LogTest to skip query to ContentProvider
|
||||||
private interface ISessionCleanupTimeoutMs {
|
private interface ISessionCleanupTimeoutMs {
|
||||||
@ -74,8 +71,7 @@ public class SessionManager {
|
|||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public ICurrentThreadId mCurrentThreadId = Process::myTid;
|
public ICurrentThreadId mCurrentThreadId = Process::myTid;
|
||||||
|
|
||||||
@VisibleForTesting
|
private ISessionCleanupTimeoutMs mSessionCleanupTimeoutMs = () -> {
|
||||||
public ISessionCleanupTimeoutMs mSessionCleanupTimeoutMs = () -> {
|
|
||||||
// mContext may be null in some cases, such as testing. For these cases, use the
|
// mContext may be null in some cases, such as testing. For these cases, use the
|
||||||
// default value.
|
// default value.
|
||||||
if (mContext == null) {
|
if (mContext == null) {
|
||||||
@ -119,6 +115,21 @@ public class SessionManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether or not to start a new session or continue an existing session based on
|
||||||
|
* the {@link Session.Info} info passed into startSession. If info is null, a new Session is
|
||||||
|
* created. This code must be accompanied by endSession() at the end of the Session.
|
||||||
|
*/
|
||||||
|
public synchronized void startSession(Session.Info info, String shortMethodName,
|
||||||
|
String callerIdentification) {
|
||||||
|
// Start a new session normally if the
|
||||||
|
if(info == null) {
|
||||||
|
startSession(shortMethodName, callerIdentification);
|
||||||
|
} else {
|
||||||
|
startExternalSession(info, shortMethodName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Call at an entry point to the Telecom code to track the session. This code must be
|
* Call at an entry point to the Telecom code to track the session. This code must be
|
||||||
* accompanied by a Log.endSession().
|
* accompanied by a Log.endSession().
|
||||||
@ -134,14 +145,54 @@ public class SessionManager {
|
|||||||
Session childSession = createSubsession(true);
|
Session childSession = createSubsession(true);
|
||||||
continueSession(childSession, shortMethodName);
|
continueSession(childSession, shortMethodName);
|
||||||
return;
|
return;
|
||||||
|
} else {
|
||||||
|
// Only Log that we are starting the parent session.
|
||||||
|
Log.d(LOGGING_TAG, Session.START_SESSION);
|
||||||
}
|
}
|
||||||
Session newSession = new Session(getNextSessionID(), shortMethodName,
|
Session newSession = new Session(getNextSessionID(), shortMethodName,
|
||||||
System.currentTimeMillis(), threadId, false, callerIdentification);
|
System.currentTimeMillis(), false, callerIdentification);
|
||||||
mSessionMapper.put(threadId, newSession);
|
mSessionMapper.put(threadId, newSession);
|
||||||
|
|
||||||
android.util.Slog.v(LOGGING_TAG, Session.START_SESSION);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers an external Session with the Manager using that external Session's sessionInfo.
|
||||||
|
* Log.endSession will still need to be called at the end of the session.
|
||||||
|
* @param sessionInfo Describes the external Session's information.
|
||||||
|
* @param shortMethodName The method name of the new session that is being started.
|
||||||
|
*/
|
||||||
|
public synchronized void startExternalSession(Session.Info sessionInfo,
|
||||||
|
String shortMethodName) {
|
||||||
|
if(sessionInfo == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int threadId = getCallingThreadId();
|
||||||
|
Session threadSession = mSessionMapper.get(threadId);
|
||||||
|
if (threadSession != null) {
|
||||||
|
// We should never get into a situation where there is already an active session AND
|
||||||
|
// an external session is added. We are just using that active session.
|
||||||
|
Log.w(LOGGING_TAG, "trying to start an external session with a session " +
|
||||||
|
"already active.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create Session from Info and add to the sessionMapper under this ID.
|
||||||
|
Session externalSession = new Session(Session.EXTERNAL_INDICATOR + sessionInfo.sessionId,
|
||||||
|
sessionInfo.shortMethodName, System.currentTimeMillis(),
|
||||||
|
false /*isStartedFromActiveSession*/, null);
|
||||||
|
externalSession.setIsExternal(true);
|
||||||
|
// Mark the external session as already completed, since we have no way of knowing when
|
||||||
|
// the external session actually has completed.
|
||||||
|
externalSession.markSessionCompleted(Session.UNDEFINED);
|
||||||
|
// Track the external session with the SessionMapper so that we can create and continue
|
||||||
|
// an active subsession based on it.
|
||||||
|
mSessionMapper.put(threadId, externalSession);
|
||||||
|
// Create a subsession from this external Session parent node
|
||||||
|
Session childSession = createSubsession();
|
||||||
|
continueSession(childSession, shortMethodName);
|
||||||
|
|
||||||
|
Log.d(LOGGING_TAG, Session.START_SESSION);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notifies the logging system that a subsession will be run at a later point and
|
* Notifies the logging system that a subsession will be run at a later point and
|
||||||
@ -156,22 +207,22 @@ public class SessionManager {
|
|||||||
int threadId = getCallingThreadId();
|
int threadId = getCallingThreadId();
|
||||||
Session threadSession = mSessionMapper.get(threadId);
|
Session threadSession = mSessionMapper.get(threadId);
|
||||||
if (threadSession == null) {
|
if (threadSession == null) {
|
||||||
android.util.Slog.d(LOGGING_TAG, "Log.createSubsession was called with no session " +
|
Log.d(LOGGING_TAG, "Log.createSubsession was called with no session " +
|
||||||
"active.");
|
"active.");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
// Start execution time of the session will be overwritten in continueSession(...).
|
// Start execution time of the session will be overwritten in continueSession(...).
|
||||||
Session newSubsession = new Session(threadSession.getNextChildId(),
|
Session newSubsession = new Session(threadSession.getNextChildId(),
|
||||||
threadSession.getShortMethodName(), System.currentTimeMillis(), threadId,
|
threadSession.getShortMethodName(), System.currentTimeMillis(),
|
||||||
isStartedFromActiveSession, null);
|
isStartedFromActiveSession, null);
|
||||||
threadSession.addChild(newSubsession);
|
threadSession.addChild(newSubsession);
|
||||||
newSubsession.setParentSession(threadSession);
|
newSubsession.setParentSession(threadSession);
|
||||||
|
|
||||||
if (!isStartedFromActiveSession) {
|
if (!isStartedFromActiveSession) {
|
||||||
android.util.Slog.v(LOGGING_TAG, Session.CREATE_SUBSESSION + " " +
|
Log.v(LOGGING_TAG, Session.CREATE_SUBSESSION + " " +
|
||||||
newSubsession.toString());
|
newSubsession.toString());
|
||||||
} else {
|
} else {
|
||||||
android.util.Slog.v(LOGGING_TAG, Session.CREATE_SUBSESSION +
|
Log.v(LOGGING_TAG, Session.CREATE_SUBSESSION +
|
||||||
" (Invisible subsession)");
|
" (Invisible subsession)");
|
||||||
}
|
}
|
||||||
return newSubsession;
|
return newSubsession;
|
||||||
@ -201,21 +252,20 @@ public class SessionManager {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
resetStaleSessionTimer();
|
resetStaleSessionTimer();
|
||||||
String callingMethodName = subsession.getShortMethodName();
|
subsession.setShortMethodName(shortMethodName);
|
||||||
subsession.setShortMethodName(callingMethodName + "->" + shortMethodName);
|
|
||||||
subsession.setExecutionStartTimeMs(System.currentTimeMillis());
|
subsession.setExecutionStartTimeMs(System.currentTimeMillis());
|
||||||
Session parentSession = subsession.getParentSession();
|
Session parentSession = subsession.getParentSession();
|
||||||
if (parentSession == null) {
|
if (parentSession == null) {
|
||||||
android.util.Slog.d(LOGGING_TAG, "Log.continueSession was called with no session " +
|
Log.i(LOGGING_TAG, "Log.continueSession was called with no session " +
|
||||||
"active for method " + shortMethodName);
|
"active for method " + shortMethodName);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mSessionMapper.put(getCallingThreadId(), subsession);
|
mSessionMapper.put(getCallingThreadId(), subsession);
|
||||||
if (!subsession.isStartedFromActiveSession()) {
|
if (!subsession.isStartedFromActiveSession()) {
|
||||||
android.util.Slog.v(LOGGING_TAG, Session.CONTINUE_SUBSESSION);
|
Log.v(LOGGING_TAG, Session.CONTINUE_SUBSESSION);
|
||||||
} else {
|
} else {
|
||||||
android.util.Slog.v(LOGGING_TAG, Session.CONTINUE_SUBSESSION +
|
Log.v(LOGGING_TAG, Session.CONTINUE_SUBSESSION +
|
||||||
" (Invisible Subsession) with Method " + shortMethodName);
|
" (Invisible Subsession) with Method " + shortMethodName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -228,16 +278,16 @@ public class SessionManager {
|
|||||||
int threadId = getCallingThreadId();
|
int threadId = getCallingThreadId();
|
||||||
Session completedSession = mSessionMapper.get(threadId);
|
Session completedSession = mSessionMapper.get(threadId);
|
||||||
if (completedSession == null) {
|
if (completedSession == null) {
|
||||||
android.util.Slog.w(LOGGING_TAG, "Log.endSession was called with no session active.");
|
Log.w(LOGGING_TAG, "Log.endSession was called with no session active.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
completedSession.markSessionCompleted(System.currentTimeMillis());
|
completedSession.markSessionCompleted(System.currentTimeMillis());
|
||||||
if (!completedSession.isStartedFromActiveSession()) {
|
if (!completedSession.isStartedFromActiveSession()) {
|
||||||
android.util.Slog.v(LOGGING_TAG, Session.END_SUBSESSION + " (dur: " +
|
Log.v(LOGGING_TAG, Session.END_SUBSESSION + " (dur: " +
|
||||||
completedSession.getLocalExecutionTime() + " mS)");
|
completedSession.getLocalExecutionTime() + " mS)");
|
||||||
} else {
|
} else {
|
||||||
android.util.Slog.v(LOGGING_TAG, Session.END_SUBSESSION +
|
Log.v(LOGGING_TAG, Session.END_SUBSESSION +
|
||||||
" (Invisible Subsession) (dur: " + completedSession.getLocalExecutionTime() +
|
" (Invisible Subsession) (dur: " + completedSession.getLocalExecutionTime() +
|
||||||
" ms)");
|
" ms)");
|
||||||
}
|
}
|
||||||
@ -260,25 +310,37 @@ public class SessionManager {
|
|||||||
if (!subsession.isSessionCompleted() || subsession.getChildSessions().size() != 0) {
|
if (!subsession.isSessionCompleted() || subsession.getChildSessions().size() != 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Session parentSession = subsession.getParentSession();
|
Session parentSession = subsession.getParentSession();
|
||||||
if (parentSession != null) {
|
if (parentSession != null) {
|
||||||
subsession.setParentSession(null);
|
subsession.setParentSession(null);
|
||||||
parentSession.removeChild(subsession);
|
parentSession.removeChild(subsession);
|
||||||
|
// Report the child session of the external session as being complete to the listeners,
|
||||||
|
// not the external session itself.
|
||||||
|
if (parentSession.isExternal()) {
|
||||||
|
long fullSessionTimeMs =
|
||||||
|
System.currentTimeMillis() - subsession.getExecutionStartTimeMilliseconds();
|
||||||
|
notifySessionCompleteListeners(subsession.getShortMethodName(), fullSessionTimeMs);
|
||||||
|
}
|
||||||
endParentSessions(parentSession);
|
endParentSessions(parentSession);
|
||||||
} else {
|
} else {
|
||||||
// All of the subsessions have been completed and it is time to report on the full
|
// All of the subsessions have been completed and it is time to report on the full
|
||||||
// running time of the session.
|
// running time of the session.
|
||||||
long fullSessionTimeMs =
|
long fullSessionTimeMs =
|
||||||
System.currentTimeMillis() - subsession.getExecutionStartTimeMilliseconds();
|
System.currentTimeMillis() - subsession.getExecutionStartTimeMilliseconds();
|
||||||
android.util.Slog.d(LOGGING_TAG, Session.END_SESSION + " (dur: " + fullSessionTimeMs
|
Log.d(LOGGING_TAG, Session.END_SESSION + " (dur: " + fullSessionTimeMs
|
||||||
+ " ms): " + subsession.toString());
|
+ " ms): " + subsession.toString());
|
||||||
for (ISessionListener l : mSessionListeners) {
|
if (!subsession.isExternal()) {
|
||||||
l.sessionComplete(subsession.getShortMethodName(), fullSessionTimeMs);
|
notifySessionCompleteListeners(subsession.getShortMethodName(), fullSessionTimeMs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void notifySessionCompleteListeners(String methodName, long sessionTimeMs) {
|
||||||
|
for (ISessionListener l : mSessionListeners) {
|
||||||
|
l.sessionComplete(methodName, sessionTimeMs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public String getSessionId() {
|
public String getSessionId() {
|
||||||
Session currentSession = mSessionMapper.get(getCallingThreadId());
|
Session currentSession = mSessionMapper.get(getCallingThreadId());
|
||||||
return currentSession != null ? currentSession.toString() : "";
|
return currentSession != null ? currentSession.toString() : "";
|
||||||
@ -299,24 +361,22 @@ public class SessionManager {
|
|||||||
return getBase64Encoding(nextId);
|
return getBase64Encoding(nextId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
private synchronized void restartSessionCounter() {
|
||||||
public synchronized void restartSessionCounter() {
|
|
||||||
sCodeEntryCounter = 0;
|
sCodeEntryCounter = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
private String getBase64Encoding(int number) {
|
||||||
public String getBase64Encoding(int number) {
|
|
||||||
byte[] idByteArray = ByteBuffer.allocate(4).putInt(number).array();
|
byte[] idByteArray = ByteBuffer.allocate(4).putInt(number).array();
|
||||||
idByteArray = Arrays.copyOfRange(idByteArray, 2, 4);
|
idByteArray = Arrays.copyOfRange(idByteArray, 2, 4);
|
||||||
return Base64.encodeToString(idByteArray, Base64.NO_WRAP | Base64.NO_PADDING);
|
return Base64.encodeToString(idByteArray, Base64.NO_WRAP | Base64.NO_PADDING);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getCallingThreadId() {
|
private int getCallingThreadId() {
|
||||||
return mCurrentThreadId.get();
|
return mCurrentThreadId.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
private synchronized void cleanupStaleSessions(long timeoutMs) {
|
public synchronized void cleanupStaleSessions(long timeoutMs) {
|
||||||
String logMessage = "Stale Sessions Cleaned:\n";
|
String logMessage = "Stale Sessions Cleaned:\n";
|
||||||
boolean isSessionsStale = false;
|
boolean isSessionsStale = false;
|
||||||
long currentTimeMs = System.currentTimeMillis();
|
long currentTimeMs = System.currentTimeMillis();
|
||||||
@ -335,9 +395,9 @@ public class SessionManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isSessionsStale) {
|
if (isSessionsStale) {
|
||||||
android.util.Slog.w(LOGGING_TAG, logMessage);
|
Log.w(LOGGING_TAG, logMessage);
|
||||||
} else {
|
} else {
|
||||||
android.util.Slog.v(LOGGING_TAG, "No stale logging sessions needed to be cleaned...");
|
Log.v(LOGGING_TAG, "No stale logging sessions needed to be cleaned...");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user