| /* |
| * Copyright (C) 2006 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.phone; |
| |
| import android.content.Context; |
| import android.os.Debug; |
| import android.os.Handler; |
| import android.os.SystemClock; |
| import com.android.internal.telephony.Call; |
| import com.android.internal.telephony.Connection; |
| import android.util.Log; |
| |
| import java.io.File; |
| import java.util.List; |
| |
| /** |
| * Helper class used to keep track of various "elapsed time" indications |
| * in the Phone app, and also to start and stop tracing / profiling. |
| */ |
| public class CallTime extends Handler { |
| private static final String LOG_TAG = "PHONE/CallTime"; |
| private static final boolean DBG = false; |
| /* package */ static final boolean PROFILE = true; |
| |
| private static final int PROFILE_STATE_NONE = 0; |
| private static final int PROFILE_STATE_READY = 1; |
| private static final int PROFILE_STATE_RUNNING = 2; |
| |
| private static int sProfileState = PROFILE_STATE_NONE; |
| |
| private Call mCall; |
| private long mLastReportedTime; |
| private boolean mTimerRunning; |
| private long mInterval; |
| private PeriodicTimerCallback mTimerCallback; |
| private OnTickListener mListener; |
| |
| interface OnTickListener { |
| void onTickForCallTimeElapsed(long timeElapsed); |
| } |
| |
| public CallTime(OnTickListener listener) { |
| mListener = listener; |
| mTimerCallback = new PeriodicTimerCallback(); |
| } |
| |
| /** |
| * Sets the call timer to "active call" mode, where the timer will |
| * periodically update the UI to show how long the specified call |
| * has been active. |
| * |
| * After calling this you should also call reset() and |
| * periodicUpdateTimer() to get the timer started. |
| */ |
| /* package */ void setActiveCallMode(Call call) { |
| if (DBG) log("setActiveCallMode(" + call + ")..."); |
| mCall = call; |
| |
| // How frequently should we update the UI? |
| mInterval = 1000; // once per second |
| } |
| |
| /* package */ void reset() { |
| if (DBG) log("reset()..."); |
| mLastReportedTime = SystemClock.uptimeMillis() - mInterval; |
| } |
| |
| /* package */ void periodicUpdateTimer() { |
| if (!mTimerRunning) { |
| mTimerRunning = true; |
| |
| long now = SystemClock.uptimeMillis(); |
| long nextReport = mLastReportedTime + mInterval; |
| |
| while (now >= nextReport) { |
| nextReport += mInterval; |
| } |
| |
| if (DBG) log("periodicUpdateTimer() @ " + nextReport); |
| postAtTime(mTimerCallback, nextReport); |
| mLastReportedTime = nextReport; |
| |
| if (mCall != null) { |
| Call.State state = mCall.getState(); |
| |
| if (state == Call.State.ACTIVE) { |
| updateElapsedTime(mCall); |
| } |
| } |
| |
| if (PROFILE && isTraceReady()) { |
| startTrace(); |
| } |
| } else { |
| if (DBG) log("periodicUpdateTimer: timer already running, bail"); |
| } |
| } |
| |
| /* package */ void cancelTimer() { |
| if (DBG) log("cancelTimer()..."); |
| removeCallbacks(mTimerCallback); |
| mTimerRunning = false; |
| } |
| |
| private void updateElapsedTime(Call call) { |
| if (mListener != null) { |
| long duration = getCallDuration(call); |
| mListener.onTickForCallTimeElapsed(duration / 1000); |
| } |
| } |
| |
| /** |
| * Returns a "call duration" value for the specified Call, in msec, |
| * suitable for display in the UI. |
| */ |
| /* package */ static long getCallDuration(Call call) { |
| long duration = 0; |
| List connections = call.getConnections(); |
| int count = connections.size(); |
| Connection c; |
| |
| if (count == 1) { |
| c = (Connection) connections.get(0); |
| //duration = (state == Call.State.ACTIVE |
| // ? c.getDurationMillis() : c.getHoldDurationMillis()); |
| duration = c.getDurationMillis(); |
| } else { |
| for (int i = 0; i < count; i++) { |
| c = (Connection) connections.get(i); |
| //long t = (state == Call.State.ACTIVE |
| // ? c.getDurationMillis() : c.getHoldDurationMillis()); |
| long t = c.getDurationMillis(); |
| if (t > duration) { |
| duration = t; |
| } |
| } |
| } |
| |
| if (DBG) log("updateElapsedTime, count=" + count + ", duration=" + duration); |
| return duration; |
| } |
| |
| private static void log(String msg) { |
| Log.d(LOG_TAG, "[CallTime] " + msg); |
| } |
| |
| private class PeriodicTimerCallback implements Runnable { |
| PeriodicTimerCallback() { |
| |
| } |
| |
| public void run() { |
| if (PROFILE && isTraceRunning()) { |
| stopTrace(); |
| } |
| |
| mTimerRunning = false; |
| periodicUpdateTimer(); |
| } |
| } |
| |
| static void setTraceReady() { |
| if (sProfileState == PROFILE_STATE_NONE) { |
| sProfileState = PROFILE_STATE_READY; |
| log("trace ready..."); |
| } else { |
| log("current trace state = " + sProfileState); |
| } |
| } |
| |
| boolean isTraceReady() { |
| return sProfileState == PROFILE_STATE_READY; |
| } |
| |
| boolean isTraceRunning() { |
| return sProfileState == PROFILE_STATE_RUNNING; |
| } |
| |
| void startTrace() { |
| if (PROFILE & sProfileState == PROFILE_STATE_READY) { |
| // For now, we move away from temp directory in favor of |
| // the application's data directory to store the trace |
| // information (/data/data/com.android.phone). |
| File file = PhoneGlobals.getInstance().getDir ("phoneTrace", Context.MODE_PRIVATE); |
| if (file.exists() == false) { |
| file.mkdirs(); |
| } |
| String baseName = file.getPath() + File.separator + "callstate"; |
| String dataFile = baseName + ".data"; |
| String keyFile = baseName + ".key"; |
| |
| file = new File(dataFile); |
| if (file.exists() == true) { |
| file.delete(); |
| } |
| |
| file = new File(keyFile); |
| if (file.exists() == true) { |
| file.delete(); |
| } |
| |
| sProfileState = PROFILE_STATE_RUNNING; |
| log("startTrace"); |
| Debug.startMethodTracing(baseName, 8 * 1024 * 1024); |
| } |
| } |
| |
| void stopTrace() { |
| if (PROFILE) { |
| if (sProfileState == PROFILE_STATE_RUNNING) { |
| sProfileState = PROFILE_STATE_NONE; |
| log("stopTrace"); |
| Debug.stopMethodTracing(); |
| } |
| } |
| } |
| } |