blob: 510abddb4f93272b489b5b24040a3007bea089ac [file] [log] [blame]
Brad Ebinger51b98342016-09-22 16:30:46 -07001/*
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
Brad Ebinger096d2822016-10-13 15:26:58 -070036 /**
37 * Initial value of mExecutionEndTimeMs and the final value of {@link #getLocalExecutionTime()}
38 * if the Session is canceled.
39 */
Brad Ebinger51b98342016-09-22 16:30:46 -070040 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
Brad Ebinger51b98342016-09-22 16:30:46 -0700147 // 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
Brad Ebinger096d2822016-10-13 15:26:58 -0700190 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
Brad Ebinger51b98342016-09-22 16:30:46 -0700234 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}