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