Home | History | Annotate | Download | only in telecom
      1 /*
      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 
     17 package com.android.server.telecom;
     18 
     19 import android.annotation.NonNull;
     20 
     21 import 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  */
     27 public 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;
     44     private int mChildCounter = 0;
     45     // 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.
     50     private boolean mIsStartedFromActiveSession = false;
     51     // 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;
     54 
     55     public Session(String sessionId, String shortMethodName, long startTimeMs, long threadID,
     56             boolean isStartedFromActiveSession, String ownerInfo) {
     57         setSessionId(sessionId);
     58         setShortMethodName(shortMethodName);
     59         mExecutionStartTimeMs = startTimeMs;
     60         mParentSession = null;
     61         mChildSessions = new ArrayList<>(5);
     62         mIsStartedFromActiveSession = isStartedFromActiveSession;
     63         mOwnerInfo = ownerInfo;
     64     }
     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 
    104     public void setExecutionStartTimeMs(long startTimeMs) {
    105         mExecutionStartTimeMs = startTimeMs;
    106     }
    107 
    108     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 
    120     public boolean isStartedFromActiveSession() {
    121         return mIsStartedFromActiveSession;
    122     }
    123 
    124     // 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 
    138     public synchronized String getNextChildId() {
    139         return String.valueOf(mChildCounter++);
    140     }
    141 
    142     @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 &&
    157                 mExecutionEndTimeMs == otherSession.mExecutionEndTimeMs &&
    158                 mChildCounter == otherSession.mChildCounter &&
    159                 mIsStartedFromActiveSession == otherSession.mIsStartedFromActiveSession &&
    160                 mOwnerInfo == otherSession.mOwnerInfo;
    161     }
    162 
    163     // Builds full session id recursively
    164     private String getFullSessionId() {
    165         // 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) {
    171             return mSessionId;
    172         } else {
    173             return parentSession.getFullSessionId() + "_" + mSessionId;
    174         }
    175     }
    176 
    177     // 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 
    205     @Override
    206     public String toString() {
    207         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 {
    212             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();
    220         }
    221     }
    222 }
    223