1 /* 2 * Copyright (C) 2006 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.phone; 18 19 import android.content.Context; 20 import android.os.Debug; 21 import android.os.Handler; 22 import android.os.SystemClock; 23 import com.android.internal.telephony.Call; 24 import com.android.internal.telephony.Connection; 25 import android.util.Log; 26 27 import java.io.File; 28 import java.util.List; 29 30 /** 31 * Helper class used to keep track of various "elapsed time" indications 32 * in the Phone app, and also to start and stop tracing / profiling. 33 */ 34 public class CallTime extends Handler { 35 private static final String LOG_TAG = "PHONE/CallTime"; 36 private static final boolean DBG = false; 37 /* package */ static final boolean PROFILE = true; 38 39 private static final int PROFILE_STATE_NONE = 0; 40 private static final int PROFILE_STATE_READY = 1; 41 private static final int PROFILE_STATE_RUNNING = 2; 42 43 private static int sProfileState = PROFILE_STATE_NONE; 44 45 private Call mCall; 46 private long mLastReportedTime; 47 private boolean mTimerRunning; 48 private long mInterval; 49 private PeriodicTimerCallback mTimerCallback; 50 private OnTickListener mListener; 51 52 interface OnTickListener { 53 void onTickForCallTimeElapsed(long timeElapsed); 54 } 55 56 public CallTime(OnTickListener listener) { 57 mListener = listener; 58 mTimerCallback = new PeriodicTimerCallback(); 59 } 60 61 /** 62 * Sets the call timer to "active call" mode, where the timer will 63 * periodically update the UI to show how long the specified call 64 * has been active. 65 * 66 * After calling this you should also call reset() and 67 * periodicUpdateTimer() to get the timer started. 68 */ 69 /* package */ void setActiveCallMode(Call call) { 70 if (DBG) log("setActiveCallMode(" + call + ")..."); 71 mCall = call; 72 73 // How frequently should we update the UI? 74 mInterval = 1000; // once per second 75 } 76 77 /* package */ void reset() { 78 if (DBG) log("reset()..."); 79 mLastReportedTime = SystemClock.uptimeMillis() - mInterval; 80 } 81 82 /* package */ void periodicUpdateTimer() { 83 if (!mTimerRunning) { 84 mTimerRunning = true; 85 86 long now = SystemClock.uptimeMillis(); 87 long nextReport = mLastReportedTime + mInterval; 88 89 while (now >= nextReport) { 90 nextReport += mInterval; 91 } 92 93 if (DBG) log("periodicUpdateTimer() @ " + nextReport); 94 postAtTime(mTimerCallback, nextReport); 95 mLastReportedTime = nextReport; 96 97 if (mCall != null) { 98 Call.State state = mCall.getState(); 99 100 if (state == Call.State.ACTIVE) { 101 updateElapsedTime(mCall); 102 } 103 } 104 105 if (PROFILE && isTraceReady()) { 106 startTrace(); 107 } 108 } else { 109 if (DBG) log("periodicUpdateTimer: timer already running, bail"); 110 } 111 } 112 113 /* package */ void cancelTimer() { 114 if (DBG) log("cancelTimer()..."); 115 removeCallbacks(mTimerCallback); 116 mTimerRunning = false; 117 } 118 119 private void updateElapsedTime(Call call) { 120 if (mListener != null) { 121 long duration = getCallDuration(call); 122 mListener.onTickForCallTimeElapsed(duration / 1000); 123 } 124 } 125 126 /** 127 * Returns a "call duration" value for the specified Call, in msec, 128 * suitable for display in the UI. 129 */ 130 /* package */ static long getCallDuration(Call call) { 131 long duration = 0; 132 List connections = call.getConnections(); 133 int count = connections.size(); 134 Connection c; 135 136 if (count == 1) { 137 c = (Connection) connections.get(0); 138 //duration = (state == Call.State.ACTIVE 139 // ? c.getDurationMillis() : c.getHoldDurationMillis()); 140 duration = c.getDurationMillis(); 141 } else { 142 for (int i = 0; i < count; i++) { 143 c = (Connection) connections.get(i); 144 //long t = (state == Call.State.ACTIVE 145 // ? c.getDurationMillis() : c.getHoldDurationMillis()); 146 long t = c.getDurationMillis(); 147 if (t > duration) { 148 duration = t; 149 } 150 } 151 } 152 153 if (DBG) log("updateElapsedTime, count=" + count + ", duration=" + duration); 154 return duration; 155 } 156 157 private static void log(String msg) { 158 Log.d(LOG_TAG, "[CallTime] " + msg); 159 } 160 161 private class PeriodicTimerCallback implements Runnable { 162 PeriodicTimerCallback() { 163 164 } 165 166 public void run() { 167 if (PROFILE && isTraceRunning()) { 168 stopTrace(); 169 } 170 171 mTimerRunning = false; 172 periodicUpdateTimer(); 173 } 174 } 175 176 static void setTraceReady() { 177 if (sProfileState == PROFILE_STATE_NONE) { 178 sProfileState = PROFILE_STATE_READY; 179 log("trace ready..."); 180 } else { 181 log("current trace state = " + sProfileState); 182 } 183 } 184 185 boolean isTraceReady() { 186 return sProfileState == PROFILE_STATE_READY; 187 } 188 189 boolean isTraceRunning() { 190 return sProfileState == PROFILE_STATE_RUNNING; 191 } 192 193 void startTrace() { 194 if (PROFILE & sProfileState == PROFILE_STATE_READY) { 195 // For now, we move away from temp directory in favor of 196 // the application's data directory to store the trace 197 // information (/data/data/com.android.phone). 198 File file = PhoneGlobals.getInstance().getDir ("phoneTrace", Context.MODE_PRIVATE); 199 if (file.exists() == false) { 200 file.mkdirs(); 201 } 202 String baseName = file.getPath() + File.separator + "callstate"; 203 String dataFile = baseName + ".data"; 204 String keyFile = baseName + ".key"; 205 206 file = new File(dataFile); 207 if (file.exists() == true) { 208 file.delete(); 209 } 210 211 file = new File(keyFile); 212 if (file.exists() == true) { 213 file.delete(); 214 } 215 216 sProfileState = PROFILE_STATE_RUNNING; 217 log("startTrace"); 218 Debug.startMethodTracing(baseName, 8 * 1024 * 1024); 219 } 220 } 221 222 void stopTrace() { 223 if (PROFILE) { 224 if (sProfileState == PROFILE_STATE_RUNNING) { 225 sProfileState = PROFILE_STATE_NONE; 226 log("stopTrace"); 227 Debug.stopMethodTracing(); 228 } 229 } 230 } 231 } 232