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