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 {
onTickForCallTimeElapsed(long timeElapsed)53         void onTickForCallTimeElapsed(long timeElapsed);
54     }
55 
CallTime(OnTickListener listener)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      */
setActiveCallMode(Call call)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 
reset()77     /* package */ void reset() {
78         if (DBG) log("reset()...");
79         mLastReportedTime = SystemClock.uptimeMillis() - mInterval;
80     }
81 
periodicUpdateTimer()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 
cancelTimer()113     /* package */ void cancelTimer() {
114         if (DBG) log("cancelTimer()...");
115         removeCallbacks(mTimerCallback);
116         mTimerRunning = false;
117     }
118 
updateElapsedTime(Call call)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      */
getCallDuration(Call call)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 
log(String msg)157     private static void log(String msg) {
158         Log.d(LOG_TAG, "[CallTime] " + msg);
159     }
160 
161     private class PeriodicTimerCallback implements Runnable {
PeriodicTimerCallback()162         PeriodicTimerCallback() {
163 
164         }
165 
run()166         public void run() {
167             if (PROFILE && isTraceRunning()) {
168                 stopTrace();
169             }
170 
171             mTimerRunning = false;
172             periodicUpdateTimer();
173         }
174     }
175 
setTraceReady()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 
isTraceReady()185     boolean isTraceReady() {
186         return sProfileState == PROFILE_STATE_READY;
187     }
188 
isTraceRunning()189     boolean isTraceRunning() {
190         return sProfileState == PROFILE_STATE_RUNNING;
191     }
192 
startTrace()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 
stopTrace()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