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