blob: 51ef0fa26fc6860715353cc26e9b46530534be52 [file] [log] [blame]
Brad Ebinger116b8592015-11-18 11:40:11 -08001/*
2 * Copyright (C) 2015 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 com.android.server.telecom;
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 */
27public class Session {
28
29 public static final String START_SESSION = "START_SESSION";
30 public static final String CREATE_SUBSESSION = "CREATE_SUBSESSION";
31 public static final String CONTINUE_SUBSESSION = "CONTINUE_SUBSESSION";
32 public static final String END_SUBSESSION = "END_SUBSESSION";
33 public static final String END_SESSION = "END_SESSION";
34
35 public static final int UNDEFINED = -1;
36
37 private String mSessionId;
38 private String mShortMethodName;
39 private long mExecutionStartTimeMs;
40 private long mExecutionEndTimeMs = UNDEFINED;
41 private Session mParentSession;
42 private ArrayList<Session> mChildSessions;
43 private boolean mIsCompleted = false;
Brad Ebinger11623a32015-11-25 13:52:02 -080044 private int mChildCounter = 0;
Brad Ebinger209e4132015-12-17 15:10:14 -080045 // True if this is a subsession that has been started from the same thread as the parent
46 // session. This can happen if Log.startSession(...) is called multiple times on the same
47 // thread in the case of one Telecom entry point method calling another entry point method.
48 // In this case, we can just make this subsession "invisible," but still keep track of it so
49 // that the Log.endSession() calls match up.
Brad Ebinger69fbbf22015-12-16 10:44:10 -080050 private boolean mIsStartedFromActiveSession = false;
Brad Ebinger209e4132015-12-17 15:10:14 -080051 // Optionally provided info about the method/class/component that started the session in order
52 // to make Logging easier. This info will be provided in parentheses along with the session.
53 private String mOwnerInfo;
Brad Ebinger116b8592015-11-18 11:40:11 -080054
Brad Ebinger69fbbf22015-12-16 10:44:10 -080055 public Session(String sessionId, String shortMethodName, long startTimeMs, long threadID,
Brad Ebinger209e4132015-12-17 15:10:14 -080056 boolean isStartedFromActiveSession, String ownerInfo) {
Brad Ebinger116b8592015-11-18 11:40:11 -080057 setSessionId(sessionId);
58 setShortMethodName(shortMethodName);
59 mExecutionStartTimeMs = startTimeMs;
60 mParentSession = null;
61 mChildSessions = new ArrayList<>(5);
Brad Ebinger69fbbf22015-12-16 10:44:10 -080062 mIsStartedFromActiveSession = isStartedFromActiveSession;
Brad Ebinger209e4132015-12-17 15:10:14 -080063 mOwnerInfo = ownerInfo;
Brad Ebinger116b8592015-11-18 11:40:11 -080064 }
65
66 public void setSessionId(@NonNull String sessionId) {
67 if(sessionId == null) {
68 mSessionId = "?";
69 }
70 mSessionId = sessionId;
71 }
72
73 public String getShortMethodName() {
74 return mShortMethodName;
75 }
76
77 public void setShortMethodName(String shortMethodName) {
78 if(shortMethodName == null) {
79 shortMethodName = "";
80 }
81 mShortMethodName = shortMethodName;
82 }
83
84 public void setParentSession(Session parentSession) {
85 mParentSession = parentSession;
86 }
87
88 public void addChild(Session childSession) {
89 if(childSession != null) {
90 mChildSessions.add(childSession);
91 }
92 }
93
94 public void removeChild(Session child) {
95 if(child != null) {
96 mChildSessions.remove(child);
97 }
98 }
99
100 public long getExecutionStartTimeMilliseconds() {
101 return mExecutionStartTimeMs;
102 }
103
Brad Ebinger11623a32015-11-25 13:52:02 -0800104 public void setExecutionStartTimeMs(long startTimeMs) {
105 mExecutionStartTimeMs = startTimeMs;
106 }
107
Brad Ebinger116b8592015-11-18 11:40:11 -0800108 public Session getParentSession() {
109 return mParentSession;
110 }
111
112 public ArrayList<Session> getChildSessions() {
113 return mChildSessions;
114 }
115
116 public boolean isSessionCompleted() {
117 return mIsCompleted;
118 }
119
Brad Ebinger69fbbf22015-12-16 10:44:10 -0800120 public boolean isStartedFromActiveSession() {
121 return mIsStartedFromActiveSession;
122 }
123
Brad Ebinger116b8592015-11-18 11:40:11 -0800124 // Mark this session complete. This will be deleted by Log when all subsessions are complete
125 // as well.
126 public void markSessionCompleted(long executionEndTimeMs) {
127 mExecutionEndTimeMs = executionEndTimeMs;
128 mIsCompleted = true;
129 }
130
131 public long getLocalExecutionTime() {
132 if(mExecutionEndTimeMs == UNDEFINED) {
133 return UNDEFINED;
134 }
135 return mExecutionEndTimeMs - mExecutionStartTimeMs;
136 }
137
Brad Ebinger11623a32015-11-25 13:52:02 -0800138 public synchronized String getNextChildId() {
139 return String.valueOf(mChildCounter++);
140 }
141
Brad Ebinger116b8592015-11-18 11:40:11 -0800142 @Override
143 public boolean equals(Object obj) {
144 if (!(obj instanceof Session)) {
145 return false;
146 }
147 if (obj == this) {
148 return true;
149 }
150 Session otherSession = (Session) obj;
151 return (mSessionId.equals(otherSession.mSessionId)) &&
152 (mShortMethodName.equals(otherSession.mShortMethodName)) &&
153 mExecutionStartTimeMs == otherSession.mExecutionStartTimeMs &&
154 mParentSession == otherSession.mParentSession &&
155 mChildSessions.equals(otherSession.mChildSessions) &&
156 mIsCompleted == otherSession.mIsCompleted &&
Brad Ebinger11623a32015-11-25 13:52:02 -0800157 mExecutionEndTimeMs == otherSession.mExecutionEndTimeMs &&
158 mChildCounter == otherSession.mChildCounter &&
Brad Ebinger209e4132015-12-17 15:10:14 -0800159 mIsStartedFromActiveSession == otherSession.mIsStartedFromActiveSession &&
160 mOwnerInfo == otherSession.mOwnerInfo;
Brad Ebinger116b8592015-11-18 11:40:11 -0800161 }
162
163 // Builds full session id recursively
164 private String getFullSessionId() {
Brad Ebingereae0bf02016-01-21 18:01:11 -0800165 // Cache mParentSession locally to prevent a concurrency problem where
166 // Log.endParentSessions() is called while a logging statement is running (Log.i, for
167 // example) and setting mParentSession to null in a different thread after the null check
168 // occurred.
169 Session parentSession = mParentSession;
170 if(parentSession == null) {
Brad Ebinger116b8592015-11-18 11:40:11 -0800171 return mSessionId;
172 } else {
Brad Ebingereae0bf02016-01-21 18:01:11 -0800173 return parentSession.getFullSessionId() + "_" + mSessionId;
Brad Ebinger116b8592015-11-18 11:40:11 -0800174 }
175 }
176
Brad Ebinger11623a32015-11-25 13:52:02 -0800177 // Print out the full Session tree from any subsession node
178 public String printFullSessionTree() {
179 // Get to the top of the tree
180 Session topNode = this;
181 while(topNode.getParentSession() != null) {
182 topNode = topNode.getParentSession();
183 }
184 return topNode.printSessionTree();
185 }
186
187 // Recursively move down session tree using DFS, but print out each node when it is reached.
188 public String printSessionTree() {
189 StringBuilder sb = new StringBuilder();
190 printSessionTree(0, sb);
191 return sb.toString();
192 }
193
194 private void printSessionTree(int tabI, StringBuilder sb) {
195 sb.append(toString());
196 for (Session child : mChildSessions) {
197 sb.append("\n");
198 for(int i = 0; i <= tabI; i++) {
199 sb.append("\t");
200 }
201 child.printSessionTree(tabI + 1, sb);
202 }
203 }
204
Brad Ebinger116b8592015-11-18 11:40:11 -0800205 @Override
206 public String toString() {
Brad Ebinger69fbbf22015-12-16 10:44:10 -0800207 if(mParentSession != null && mIsStartedFromActiveSession) {
208 // Log.startSession was called from within another active session. Use the parent's
209 // Id instead of the child to reduce confusion.
210 return mParentSession.toString();
211 } else {
Brad Ebinger209e4132015-12-17 15:10:14 -0800212 StringBuilder methodName = new StringBuilder();
213 methodName.append(mShortMethodName);
214 if(mOwnerInfo != null && !mOwnerInfo.isEmpty()) {
215 methodName.append("(InCall package: ");
216 methodName.append(mOwnerInfo);
217 methodName.append(")");
218 }
219 return methodName.toString() + "@" + getFullSessionId();
Brad Ebinger69fbbf22015-12-16 10:44:10 -0800220 }
Brad Ebinger116b8592015-11-18 11:40:11 -0800221 }
222}