151b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger/* 251b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger * Copyright (C) 2016 The Android Open Source Project 351b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger * 451b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger * Licensed under the Apache License, Version 2.0 (the "License"); 551b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger * you may not use this file except in compliance with the License. 651b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger * You may obtain a copy of the License at 751b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger * 851b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger * http://www.apache.org/licenses/LICENSE-2.0 951b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger * 1051b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger * Unless required by applicable law or agreed to in writing, software 1151b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger * distributed under the License is distributed on an "AS IS" BASIS, 1251b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1351b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger * See the License for the specific language governing permissions and 1451b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger * limitations under the License 1551b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger */ 1651b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger 1751b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebingerpackage android.telecom.Logging; 1851b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger 1951b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebingerimport android.content.Context; 2051b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebingerimport android.os.Handler; 2151b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebingerimport android.os.Looper; 22096d2829edd2cda66a004ea7216975730981814eBrad Ebingerimport android.os.Process; 2351b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebingerimport android.provider.Settings; 24a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebingerimport android.telecom.Log; 2551b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebingerimport android.util.Base64; 2651b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger 2751b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebingerimport com.android.internal.annotations.VisibleForTesting; 2851b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger 2951b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebingerimport java.nio.ByteBuffer; 3051b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebingerimport java.util.ArrayList; 3151b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebingerimport java.util.Arrays; 3251b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebingerimport java.util.Iterator; 3351b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebingerimport java.util.List; 3451b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebingerimport java.util.concurrent.ConcurrentHashMap; 3551b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger 3651b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger/** 3751b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger * TODO: Create better Sessions Documentation 3851b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger * @hide 3951b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger */ 4051b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger 4151b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebingerpublic class SessionManager { 4251b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger 4351b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger // Currently using 3 letters, So don't exceed 64^3 4451b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger private static final long SESSION_ID_ROLLOVER_THRESHOLD = 262144; 4551b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger // This parameter can be overridden in Telecom's Timeouts class. 46a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger private static final long DEFAULT_SESSION_TIMEOUT_MS = 30000L; // 30 seconds 47a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger private static final String LOGGING_TAG = "Logging"; 4851b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger private static final String TIMEOUTS_PREFIX = "telecom."; 4951b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger 5051b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger // Synchronized in all method calls 5151b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger private int sCodeEntryCounter = 0; 5251b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger private Context mContext; 5351b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger 5451b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger @VisibleForTesting 55096d2829edd2cda66a004ea7216975730981814eBrad Ebinger public ConcurrentHashMap<Integer, Session> mSessionMapper = new ConcurrentHashMap<>(100); 5651b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger @VisibleForTesting 57096d2829edd2cda66a004ea7216975730981814eBrad Ebinger public java.lang.Runnable mCleanStaleSessions = () -> 5851b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger cleanupStaleSessions(getSessionCleanupTimeoutMs()); 59a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger private Handler mSessionCleanupHandler = new Handler(Looper.getMainLooper()); 6051b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger 6151b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger // Overridden in LogTest to skip query to ContentProvider 6251b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger private interface ISessionCleanupTimeoutMs { 6351b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger long get(); 6451b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger } 6551b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger 66096d2829edd2cda66a004ea7216975730981814eBrad Ebinger // Overridden in tests to provide test Thread IDs 67096d2829edd2cda66a004ea7216975730981814eBrad Ebinger public interface ICurrentThreadId { 68096d2829edd2cda66a004ea7216975730981814eBrad Ebinger int get(); 69096d2829edd2cda66a004ea7216975730981814eBrad Ebinger } 70096d2829edd2cda66a004ea7216975730981814eBrad Ebinger 71096d2829edd2cda66a004ea7216975730981814eBrad Ebinger @VisibleForTesting 72096d2829edd2cda66a004ea7216975730981814eBrad Ebinger public ICurrentThreadId mCurrentThreadId = Process::myTid; 73096d2829edd2cda66a004ea7216975730981814eBrad Ebinger 74a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger private ISessionCleanupTimeoutMs mSessionCleanupTimeoutMs = () -> { 7551b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger // mContext may be null in some cases, such as testing. For these cases, use the 7651b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger // default value. 7751b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger if (mContext == null) { 7851b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger return DEFAULT_SESSION_TIMEOUT_MS; 7951b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger } 8051b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger return getCleanupTimeout(mContext); 8151b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger }; 8251b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger 8351b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger // Usage is synchronized on this class. 8451b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger private List<ISessionListener> mSessionListeners = new ArrayList<>(); 8551b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger 8651b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger public interface ISessionListener { 8751b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger /** 8851b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger * This method is run when a full Session has completed. 8951b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger * @param sessionName The name of the Session that has completed. 9051b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger * @param timeMs The time it took to complete in ms. 9151b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger */ 9251b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger void sessionComplete(String sessionName, long timeMs); 9351b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger } 9451b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger 9551b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger public interface ISessionIdQueryHandler { 9651b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger String getSessionId(); 9751b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger } 9851b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger 9951b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger public void setContext(Context context) { 10051b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger mContext = context; 10151b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger } 10251b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger 10351b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger public SessionManager() { 10451b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger } 10551b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger 10651b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger private long getSessionCleanupTimeoutMs() { 107096d2829edd2cda66a004ea7216975730981814eBrad Ebinger return mSessionCleanupTimeoutMs.get(); 10851b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger } 10951b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger 11051b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger private synchronized void resetStaleSessionTimer() { 111096d2829edd2cda66a004ea7216975730981814eBrad Ebinger mSessionCleanupHandler.removeCallbacksAndMessages(null); 11251b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger // Will be null in Log Testing 113096d2829edd2cda66a004ea7216975730981814eBrad Ebinger if (mCleanStaleSessions != null) { 114096d2829edd2cda66a004ea7216975730981814eBrad Ebinger mSessionCleanupHandler.postDelayed(mCleanStaleSessions, getSessionCleanupTimeoutMs()); 11551b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger } 11651b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger } 11751b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger 11851b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger /** 119a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger * Determines whether or not to start a new session or continue an existing session based on 120a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger * the {@link Session.Info} info passed into startSession. If info is null, a new Session is 121a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger * created. This code must be accompanied by endSession() at the end of the Session. 122a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger */ 123a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger public synchronized void startSession(Session.Info info, String shortMethodName, 124a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger String callerIdentification) { 125a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger // Start a new session normally if the 126a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger if(info == null) { 127a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger startSession(shortMethodName, callerIdentification); 128a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger } else { 129a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger startExternalSession(info, shortMethodName); 130a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger } 131a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger } 132a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger 133a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger /** 13451b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger * Call at an entry point to the Telecom code to track the session. This code must be 13551b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger * accompanied by a Log.endSession(). 13651b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger */ 13751b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger public synchronized void startSession(String shortMethodName, 13851b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger String callerIdentification) { 13951b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger resetStaleSessionTimer(); 14051b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger int threadId = getCallingThreadId(); 141096d2829edd2cda66a004ea7216975730981814eBrad Ebinger Session activeSession = mSessionMapper.get(threadId); 14251b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger // We have called startSession within an active session that has not ended... Register this 14351b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger // session as a subsession. 14451b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger if (activeSession != null) { 14551b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger Session childSession = createSubsession(true); 14651b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger continueSession(childSession, shortMethodName); 14751b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger return; 148a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger } else { 149a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger // Only Log that we are starting the parent session. 150a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger Log.d(LOGGING_TAG, Session.START_SESSION); 15151b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger } 15251b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger Session newSession = new Session(getNextSessionID(), shortMethodName, 153a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger System.currentTimeMillis(), false, callerIdentification); 154096d2829edd2cda66a004ea7216975730981814eBrad Ebinger mSessionMapper.put(threadId, newSession); 15551b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger } 15651b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger 157a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger /** 158a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger * Registers an external Session with the Manager using that external Session's sessionInfo. 159a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger * Log.endSession will still need to be called at the end of the session. 160a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger * @param sessionInfo Describes the external Session's information. 161a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger * @param shortMethodName The method name of the new session that is being started. 162a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger */ 163a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger public synchronized void startExternalSession(Session.Info sessionInfo, 164a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger String shortMethodName) { 165a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger if(sessionInfo == null) { 166a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger return; 167a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger } 168a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger 169a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger int threadId = getCallingThreadId(); 170a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger Session threadSession = mSessionMapper.get(threadId); 171a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger if (threadSession != null) { 172a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger // We should never get into a situation where there is already an active session AND 173a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger // an external session is added. We are just using that active session. 174a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger Log.w(LOGGING_TAG, "trying to start an external session with a session " + 175a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger "already active."); 176a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger return; 177a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger } 178a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger 179a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger // Create Session from Info and add to the sessionMapper under this ID. 1800c3541be65fa87519a879c053a7cf4b4526be5dbBrad Ebinger Log.d(LOGGING_TAG, Session.START_EXTERNAL_SESSION); 181a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger Session externalSession = new Session(Session.EXTERNAL_INDICATOR + sessionInfo.sessionId, 1820c3541be65fa87519a879c053a7cf4b4526be5dbBrad Ebinger sessionInfo.methodPath, System.currentTimeMillis(), 183a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger false /*isStartedFromActiveSession*/, null); 184a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger externalSession.setIsExternal(true); 185a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger // Mark the external session as already completed, since we have no way of knowing when 186a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger // the external session actually has completed. 187a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger externalSession.markSessionCompleted(Session.UNDEFINED); 188a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger // Track the external session with the SessionMapper so that we can create and continue 189a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger // an active subsession based on it. 190a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger mSessionMapper.put(threadId, externalSession); 191a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger // Create a subsession from this external Session parent node 192a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger Session childSession = createSubsession(); 193a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger continueSession(childSession, shortMethodName); 194a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger } 19551b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger 19651b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger /** 19751b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger * Notifies the logging system that a subsession will be run at a later point and 19851b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger * allocates the resources. Returns a session object that must be used in 19951b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger * Log.continueSession(...) to start the subsession. 20051b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger */ 20151b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger public Session createSubsession() { 20251b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger return createSubsession(false); 20351b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger } 20451b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger 20551b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger private synchronized Session createSubsession(boolean isStartedFromActiveSession) { 20651b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger int threadId = getCallingThreadId(); 207096d2829edd2cda66a004ea7216975730981814eBrad Ebinger Session threadSession = mSessionMapper.get(threadId); 20851b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger if (threadSession == null) { 209a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger Log.d(LOGGING_TAG, "Log.createSubsession was called with no session " + 21051b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger "active."); 21151b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger return null; 21251b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger } 21351b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger // Start execution time of the session will be overwritten in continueSession(...). 21451b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger Session newSubsession = new Session(threadSession.getNextChildId(), 215a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger threadSession.getShortMethodName(), System.currentTimeMillis(), 21651b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger isStartedFromActiveSession, null); 21751b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger threadSession.addChild(newSubsession); 21851b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger newSubsession.setParentSession(threadSession); 21951b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger 22051b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger if (!isStartedFromActiveSession) { 221a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger Log.v(LOGGING_TAG, Session.CREATE_SUBSESSION + " " + 22251b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger newSubsession.toString()); 22351b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger } else { 224a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger Log.v(LOGGING_TAG, Session.CREATE_SUBSESSION + 22551b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger " (Invisible subsession)"); 22651b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger } 22751b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger return newSubsession; 22851b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger } 22951b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger 23051b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger /** 2313445f829077cea72da77e31f0a2f6ccce3af295bBrad Ebinger * Retrieve the information of the currently active Session. This information is parcelable and 2323445f829077cea72da77e31f0a2f6ccce3af295bBrad Ebinger * is used to create an external Session ({@link #startExternalSession(Session.Info, String)}). 2333445f829077cea72da77e31f0a2f6ccce3af295bBrad Ebinger * If there is no Session active, this method will return null. 2343445f829077cea72da77e31f0a2f6ccce3af295bBrad Ebinger */ 2353445f829077cea72da77e31f0a2f6ccce3af295bBrad Ebinger public synchronized Session.Info getExternalSession() { 2363445f829077cea72da77e31f0a2f6ccce3af295bBrad Ebinger int threadId = getCallingThreadId(); 2373445f829077cea72da77e31f0a2f6ccce3af295bBrad Ebinger Session threadSession = mSessionMapper.get(threadId); 2383445f829077cea72da77e31f0a2f6ccce3af295bBrad Ebinger if (threadSession == null) { 2393445f829077cea72da77e31f0a2f6ccce3af295bBrad Ebinger Log.d(LOGGING_TAG, "Log.getExternalSession was called with no session " + 2403445f829077cea72da77e31f0a2f6ccce3af295bBrad Ebinger "active."); 2413445f829077cea72da77e31f0a2f6ccce3af295bBrad Ebinger return null; 2423445f829077cea72da77e31f0a2f6ccce3af295bBrad Ebinger } 2433445f829077cea72da77e31f0a2f6ccce3af295bBrad Ebinger 2443445f829077cea72da77e31f0a2f6ccce3af295bBrad Ebinger return threadSession.getInfo(); 2453445f829077cea72da77e31f0a2f6ccce3af295bBrad Ebinger } 2463445f829077cea72da77e31f0a2f6ccce3af295bBrad Ebinger 2473445f829077cea72da77e31f0a2f6ccce3af295bBrad Ebinger /** 24851b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger * Cancels a subsession that had Log.createSubsession() called on it, but will never have 24951b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger * Log.continueSession(...) called on it due to an error. Allows the subsession to be cleaned 250096d2829edd2cda66a004ea7216975730981814eBrad Ebinger * gracefully instead of being removed by the mSessionCleanupHandler forcefully later. 25151b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger */ 25251b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger public synchronized void cancelSubsession(Session subsession) { 25351b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger if (subsession == null) { 25451b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger return; 25551b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger } 25651b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger 257096d2829edd2cda66a004ea7216975730981814eBrad Ebinger subsession.markSessionCompleted(Session.UNDEFINED); 25851b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger endParentSessions(subsession); 25951b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger } 26051b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger 26151b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger /** 26251b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger * Starts the subsession that was created in Log.CreateSubsession. The Log.endSession() method 26351b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger * must be called at the end of this method. The full session will complete when all 26451b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger * subsessions are completed. 26551b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger */ 26651b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger public synchronized void continueSession(Session subsession, String shortMethodName) { 26751b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger if (subsession == null) { 26851b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger return; 26951b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger } 27051b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger resetStaleSessionTimer(); 271a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger subsession.setShortMethodName(shortMethodName); 27251b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger subsession.setExecutionStartTimeMs(System.currentTimeMillis()); 27351b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger Session parentSession = subsession.getParentSession(); 27451b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger if (parentSession == null) { 275a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger Log.i(LOGGING_TAG, "Log.continueSession was called with no session " + 276096d2829edd2cda66a004ea7216975730981814eBrad Ebinger "active for method " + shortMethodName); 27751b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger return; 27851b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger } 27951b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger 280096d2829edd2cda66a004ea7216975730981814eBrad Ebinger mSessionMapper.put(getCallingThreadId(), subsession); 28151b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger if (!subsession.isStartedFromActiveSession()) { 282a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger Log.v(LOGGING_TAG, Session.CONTINUE_SUBSESSION); 28351b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger } else { 284a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger Log.v(LOGGING_TAG, Session.CONTINUE_SUBSESSION + 28551b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger " (Invisible Subsession) with Method " + shortMethodName); 28651b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger } 28751b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger } 28851b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger 28951b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger /** 29051b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger * Ends the current session/subsession. Must be called after a Log.startSession(...) and 29151b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger * Log.continueSession(...) call. 29251b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger */ 29351b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger public synchronized void endSession() { 29451b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger int threadId = getCallingThreadId(); 295096d2829edd2cda66a004ea7216975730981814eBrad Ebinger Session completedSession = mSessionMapper.get(threadId); 29651b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger if (completedSession == null) { 297a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger Log.w(LOGGING_TAG, "Log.endSession was called with no session active."); 29851b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger return; 29951b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger } 30051b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger 30151b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger completedSession.markSessionCompleted(System.currentTimeMillis()); 30251b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger if (!completedSession.isStartedFromActiveSession()) { 303a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger Log.v(LOGGING_TAG, Session.END_SUBSESSION + " (dur: " + 30451b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger completedSession.getLocalExecutionTime() + " mS)"); 30551b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger } else { 306a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger Log.v(LOGGING_TAG, Session.END_SUBSESSION + 30751b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger " (Invisible Subsession) (dur: " + completedSession.getLocalExecutionTime() + 30851b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger " ms)"); 30951b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger } 31051b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger // Remove after completed so that reference still exists for logging the end events 31151b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger Session parentSession = completedSession.getParentSession(); 312096d2829edd2cda66a004ea7216975730981814eBrad Ebinger mSessionMapper.remove(threadId); 31351b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger endParentSessions(completedSession); 31451b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger // If this subsession was started from a parent session using Log.startSession, return the 31551b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger // ThreadID back to the parent after completion. 31651b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger if (parentSession != null && !parentSession.isSessionCompleted() && 31751b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger completedSession.isStartedFromActiveSession()) { 318096d2829edd2cda66a004ea7216975730981814eBrad Ebinger mSessionMapper.put(threadId, parentSession); 31951b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger } 32051b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger } 32151b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger 32251b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger // Recursively deletes all complete parent sessions of the current subsession if it is a leaf. 32351b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger private void endParentSessions(Session subsession) { 32451b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger // Session is not completed or not currently a leaf, so we can not remove because a child is 32551b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger // still running 32651b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger if (!subsession.isSessionCompleted() || subsession.getChildSessions().size() != 0) { 32751b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger return; 32851b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger } 32951b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger Session parentSession = subsession.getParentSession(); 33051b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger if (parentSession != null) { 33151b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger subsession.setParentSession(null); 33251b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger parentSession.removeChild(subsession); 333a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger // Report the child session of the external session as being complete to the listeners, 334a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger // not the external session itself. 335a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger if (parentSession.isExternal()) { 336a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger long fullSessionTimeMs = 337a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger System.currentTimeMillis() - subsession.getExecutionStartTimeMilliseconds(); 338a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger notifySessionCompleteListeners(subsession.getShortMethodName(), fullSessionTimeMs); 339a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger } 34051b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger endParentSessions(parentSession); 34151b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger } else { 34251b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger // All of the subsessions have been completed and it is time to report on the full 34351b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger // running time of the session. 34451b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger long fullSessionTimeMs = 34551b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger System.currentTimeMillis() - subsession.getExecutionStartTimeMilliseconds(); 346a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger Log.d(LOGGING_TAG, Session.END_SESSION + " (dur: " + fullSessionTimeMs 34751b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger + " ms): " + subsession.toString()); 348a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger if (!subsession.isExternal()) { 349a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger notifySessionCompleteListeners(subsession.getShortMethodName(), fullSessionTimeMs); 35051b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger } 35151b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger } 35251b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger } 35351b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger 354a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger private void notifySessionCompleteListeners(String methodName, long sessionTimeMs) { 355a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger for (ISessionListener l : mSessionListeners) { 356a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger l.sessionComplete(methodName, sessionTimeMs); 357a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger } 358a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger } 359a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger 36051b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger public String getSessionId() { 361096d2829edd2cda66a004ea7216975730981814eBrad Ebinger Session currentSession = mSessionMapper.get(getCallingThreadId()); 36251b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger return currentSession != null ? currentSession.toString() : ""; 36351b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger } 36451b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger 36551b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger public synchronized void registerSessionListener(ISessionListener l) { 36651b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger if (l != null) { 36751b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger mSessionListeners.add(l); 36851b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger } 36951b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger } 37051b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger 37151b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger private synchronized String getNextSessionID() { 37251b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger Integer nextId = sCodeEntryCounter++; 37351b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger if (nextId >= SESSION_ID_ROLLOVER_THRESHOLD) { 37451b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger restartSessionCounter(); 37551b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger nextId = sCodeEntryCounter++; 37651b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger } 37751b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger return getBase64Encoding(nextId); 37851b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger } 37951b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger 380a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger private synchronized void restartSessionCounter() { 38151b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger sCodeEntryCounter = 0; 38251b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger } 38351b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger 384a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger private String getBase64Encoding(int number) { 38551b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger byte[] idByteArray = ByteBuffer.allocate(4).putInt(number).array(); 38651b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger idByteArray = Arrays.copyOfRange(idByteArray, 2, 4); 38751b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger return Base64.encodeToString(idByteArray, Base64.NO_WRAP | Base64.NO_PADDING); 38851b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger } 38951b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger 390a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger private int getCallingThreadId() { 391096d2829edd2cda66a004ea7216975730981814eBrad Ebinger return mCurrentThreadId.get(); 39251b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger } 39351b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger 39451b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger @VisibleForTesting 395a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger public synchronized void cleanupStaleSessions(long timeoutMs) { 39651b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger String logMessage = "Stale Sessions Cleaned:\n"; 39751b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger boolean isSessionsStale = false; 39851b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger long currentTimeMs = System.currentTimeMillis(); 39951b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger // Remove references that are in the Session Mapper (causing GC to occur) on 40051b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger // sessions that are lasting longer than LOGGING_SESSION_TIMEOUT_MS. 40151b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger // If this occurs, then there is most likely a Session active that never had 40251b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger // Log.endSession called on it. 40351b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger for (Iterator<ConcurrentHashMap.Entry<Integer, Session>> it = 404096d2829edd2cda66a004ea7216975730981814eBrad Ebinger mSessionMapper.entrySet().iterator(); it.hasNext(); ) { 40551b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger ConcurrentHashMap.Entry<Integer, Session> entry = it.next(); 40651b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger Session session = entry.getValue(); 40751b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger if (currentTimeMs - session.getExecutionStartTimeMilliseconds() > timeoutMs) { 40851b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger it.remove(); 40951b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger logMessage += session.printFullSessionTree() + "\n"; 41051b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger isSessionsStale = true; 41151b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger } 41251b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger } 41351b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger if (isSessionsStale) { 414a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger Log.w(LOGGING_TAG, logMessage); 41551b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger } else { 416a0dc9765d339ee69da4a1adc3bd6863126267b08Brad Ebinger Log.v(LOGGING_TAG, "No stale logging sessions needed to be cleaned..."); 41751b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger } 41851b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger } 41951b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger 42051b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger /** 42151b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger * Returns the amount of time after a Logging session has been started that Telecom is set to 42251b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger * perform a sweep to check and make sure that the session is still not incomplete (stale). 42351b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger */ 42451b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger private long getCleanupTimeout(Context context) { 42551b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger return Settings.Secure.getLong(context.getContentResolver(), TIMEOUTS_PREFIX + 42651b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger "stale_session_cleanup_timeout_millis", DEFAULT_SESSION_TIMEOUT_MS); 42751b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger } 42851b9834180db6ecaf4edaf38fb12d5d408f2c1ceBrad Ebinger} 429