Session.java revision 096d2829edd2cda66a004ea7216975730981814e
1/* 2 * Copyright (C) 2016 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 17package android.telecom.Logging; 18 19import android.annotation.NonNull; 20 21import java.util.ArrayList; 22 23/** 24 * The session that stores information about a thread's point of entry into the Telecom code that 25 * persists until the thread exits Telecom. 26 * @hide 27 */ 28public class Session { 29 30 public static final String START_SESSION = "START_SESSION"; 31 public static final String CREATE_SUBSESSION = "CREATE_SUBSESSION"; 32 public static final String CONTINUE_SUBSESSION = "CONTINUE_SUBSESSION"; 33 public static final String END_SUBSESSION = "END_SUBSESSION"; 34 public static final String END_SESSION = "END_SESSION"; 35 36 /** 37 * Initial value of mExecutionEndTimeMs and the final value of {@link #getLocalExecutionTime()} 38 * if the Session is canceled. 39 */ 40 public static final int UNDEFINED = -1; 41 42 private String mSessionId; 43 private String mShortMethodName; 44 private long mExecutionStartTimeMs; 45 private long mExecutionEndTimeMs = UNDEFINED; 46 private Session mParentSession; 47 private ArrayList<Session> mChildSessions; 48 private boolean mIsCompleted = false; 49 private int mChildCounter = 0; 50 // True if this is a subsession that has been started from the same thread as the parent 51 // session. This can happen if Log.startSession(...) is called multiple times on the same 52 // thread in the case of one Telecom entry point method calling another entry point method. 53 // In this case, we can just make this subsession "invisible," but still keep track of it so 54 // that the Log.endSession() calls match up. 55 private boolean mIsStartedFromActiveSession = false; 56 // Optionally provided info about the method/class/component that started the session in order 57 // to make Logging easier. This info will be provided in parentheses along with the session. 58 private String mOwnerInfo; 59 60 public Session(String sessionId, String shortMethodName, long startTimeMs, long threadID, 61 boolean isStartedFromActiveSession, String ownerInfo) { 62 setSessionId(sessionId); 63 setShortMethodName(shortMethodName); 64 mExecutionStartTimeMs = startTimeMs; 65 mParentSession = null; 66 mChildSessions = new ArrayList<>(5); 67 mIsStartedFromActiveSession = isStartedFromActiveSession; 68 mOwnerInfo = ownerInfo; 69 } 70 71 public void setSessionId(@NonNull String sessionId) { 72 if (sessionId == null) { 73 mSessionId = "?"; 74 } 75 mSessionId = sessionId; 76 } 77 78 public String getShortMethodName() { 79 return mShortMethodName; 80 } 81 82 public void setShortMethodName(String shortMethodName) { 83 if (shortMethodName == null) { 84 shortMethodName = ""; 85 } 86 mShortMethodName = shortMethodName; 87 } 88 89 public void setParentSession(Session parentSession) { 90 mParentSession = parentSession; 91 } 92 93 public void addChild(Session childSession) { 94 if (childSession != null) { 95 mChildSessions.add(childSession); 96 } 97 } 98 99 public void removeChild(Session child) { 100 if (child != null) { 101 mChildSessions.remove(child); 102 } 103 } 104 105 public long getExecutionStartTimeMilliseconds() { 106 return mExecutionStartTimeMs; 107 } 108 109 public void setExecutionStartTimeMs(long startTimeMs) { 110 mExecutionStartTimeMs = startTimeMs; 111 } 112 113 public Session getParentSession() { 114 return mParentSession; 115 } 116 117 public ArrayList<Session> getChildSessions() { 118 return mChildSessions; 119 } 120 121 public boolean isSessionCompleted() { 122 return mIsCompleted; 123 } 124 125 public boolean isStartedFromActiveSession() { 126 return mIsStartedFromActiveSession; 127 } 128 129 // Mark this session complete. This will be deleted by Log when all subsessions are complete 130 // as well. 131 public void markSessionCompleted(long executionEndTimeMs) { 132 mExecutionEndTimeMs = executionEndTimeMs; 133 mIsCompleted = true; 134 } 135 136 public long getLocalExecutionTime() { 137 if (mExecutionEndTimeMs == UNDEFINED) { 138 return UNDEFINED; 139 } 140 return mExecutionEndTimeMs - mExecutionStartTimeMs; 141 } 142 143 public synchronized String getNextChildId() { 144 return String.valueOf(mChildCounter++); 145 } 146 147 // Builds full session id recursively 148 private String getFullSessionId() { 149 // Cache mParentSession locally to prevent a concurrency problem where 150 // Log.endParentSessions() is called while a logging statement is running (Log.i, for 151 // example) and setting mParentSession to null in a different thread after the null check 152 // occurred. 153 Session parentSession = mParentSession; 154 if (parentSession == null) { 155 return mSessionId; 156 } else { 157 return parentSession.getFullSessionId() + "_" + mSessionId; 158 } 159 } 160 161 // Print out the full Session tree from any subsession node 162 public String printFullSessionTree() { 163 // Get to the top of the tree 164 Session topNode = this; 165 while (topNode.getParentSession() != null) { 166 topNode = topNode.getParentSession(); 167 } 168 return topNode.printSessionTree(); 169 } 170 171 // Recursively move down session tree using DFS, but print out each node when it is reached. 172 public String printSessionTree() { 173 StringBuilder sb = new StringBuilder(); 174 printSessionTree(0, sb); 175 return sb.toString(); 176 } 177 178 private void printSessionTree(int tabI, StringBuilder sb) { 179 sb.append(toString()); 180 for (Session child : mChildSessions) { 181 sb.append("\n"); 182 for (int i = 0; i <= tabI; i++) { 183 sb.append("\t"); 184 } 185 child.printSessionTree(tabI + 1, sb); 186 } 187 } 188 189 @Override 190 public int hashCode() { 191 int result = mSessionId != null ? mSessionId.hashCode() : 0; 192 result = 31 * result + (mShortMethodName != null ? mShortMethodName.hashCode() : 0); 193 result = 31 * result + (int) (mExecutionStartTimeMs ^ (mExecutionStartTimeMs >>> 32)); 194 result = 31 * result + (int) (mExecutionEndTimeMs ^ (mExecutionEndTimeMs >>> 32)); 195 result = 31 * result + (mParentSession != null ? mParentSession.hashCode() : 0); 196 result = 31 * result + (mChildSessions != null ? mChildSessions.hashCode() : 0); 197 result = 31 * result + (mIsCompleted ? 1 : 0); 198 result = 31 * result + mChildCounter; 199 result = 31 * result + (mIsStartedFromActiveSession ? 1 : 0); 200 result = 31 * result + (mOwnerInfo != null ? mOwnerInfo.hashCode() : 0); 201 return result; 202 } 203 204 @Override 205 public boolean equals(Object o) { 206 if (this == o) return true; 207 if (o == null || getClass() != o.getClass()) return false; 208 209 Session session = (Session) o; 210 211 if (mExecutionStartTimeMs != session.mExecutionStartTimeMs) return false; 212 if (mExecutionEndTimeMs != session.mExecutionEndTimeMs) return false; 213 if (mIsCompleted != session.mIsCompleted) return false; 214 if (mChildCounter != session.mChildCounter) return false; 215 if (mIsStartedFromActiveSession != session.mIsStartedFromActiveSession) return false; 216 if (mSessionId != null ? 217 !mSessionId.equals(session.mSessionId) : session.mSessionId != null) 218 return false; 219 if (mShortMethodName != null ? !mShortMethodName.equals(session.mShortMethodName) 220 : session.mShortMethodName != null) 221 return false; 222 if (mParentSession != null ? !mParentSession.equals(session.mParentSession) 223 : session.mParentSession != null) 224 return false; 225 if (mChildSessions != null ? !mChildSessions.equals(session.mChildSessions) 226 : session.mChildSessions != null) 227 return false; 228 return mOwnerInfo != null ? mOwnerInfo.equals(session.mOwnerInfo) 229 : session.mOwnerInfo == null; 230 231 } 232 233 @Override 234 public String toString() { 235 if (mParentSession != null && mIsStartedFromActiveSession) { 236 // Log.startSession was called from within another active session. Use the parent's 237 // Id instead of the child to reduce confusion. 238 return mParentSession.toString(); 239 } else { 240 StringBuilder methodName = new StringBuilder(); 241 methodName.append(mShortMethodName); 242 if (mOwnerInfo != null && !mOwnerInfo.isEmpty()) { 243 methodName.append("(InCall package: "); 244 methodName.append(mOwnerInfo); 245 methodName.append(")"); 246 } 247 return methodName.toString() + "@" + getFullSessionId(); 248 } 249 } 250} 251