Home | History | Annotate | Download | only in phone
      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