Merge "FM-APP2: Added recording option on RF Stats activity"
diff --git a/fmapp2/res/menu/menu_rf_stats.xml b/fmapp2/res/menu/menu_rf_stats.xml
new file mode 100644
index 0000000..505e49b
--- /dev/null
+++ b/fmapp2/res/menu/menu_rf_stats.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     * Copyright (c) 2013 The Linux Foundation. All rights reserved.
+     *
+     * Redistribution and use in source and binary forms, with or without
+     * modification, are permitted provided that the following conditions are met:
+     *    * Redistributions of source code must retain the above copyright
+     *      notice, this list of conditions and the following disclaimer.
+     *    * Redistributions in binary form must reproduce the above copyright
+     *      notice, this list of conditions and the following disclaimer in the
+     *      documentation and/or other materials provided with the distribution.
+     *    * Neither the name of The Linux Foundation nor
+     *      the names of its contributors may be used to endorse or promote
+     *      products derived from this software without specific prior written
+     *      permission.
+     *
+     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+     * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+     * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+     * NON-INFRINGEMENT ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+     * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+     * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+     * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+     * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+     * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+     * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+     * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+-->
+
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:id="@+id/menu_recording"
+        android:icon="@drawable/ic_menu_record"
+        android:showAsAction="ifRoom|withText" />
+
+    <item android:id="@+id/menu_record_duration"
+        android:showAsAction="ifRoom|withText"
+        android:title=""/>
+
+</menu>
+
diff --git a/fmapp2/src/com/caf/fmradio/FMRadio.java b/fmapp2/src/com/caf/fmradio/FMRadio.java
index 631ab40..e311172 100644
--- a/fmapp2/src/com/caf/fmradio/FMRadio.java
+++ b/fmapp2/src/com/caf/fmradio/FMRadio.java
@@ -460,7 +460,7 @@
               }
           }
       }
-      if(isRecordTimerActive()) {
+      if(isRecording()) {
           try {
               if (null != mRecordUpdateHandlerThread) {
                   mRecordUpdateHandlerThread.interrupt();
@@ -468,17 +468,6 @@
           }catch (NullPointerException e) {
               e.printStackTrace();
           }
-          long rtimeNow = ((SystemClock.elapsedRealtime()));
-          if (rtimeNow < mRecordDuration) {
-              try {
-                if (null != mService) {
-                    mService.delayedStop((mRecordDuration - rtimeNow),
-                                              FMRadioService.STOP_RECORD);
-                }
-              }catch (Exception e) {
-                e.printStackTrace();
-              }
-          }
       }
       super.onStop();
    }
@@ -505,8 +494,8 @@
           }
           initiateSleepThread();
       }
-      if(isRecordTimerActive()) {
-          Log.d(LOGTAG, "isRecordTimerActive is true");
+      if(isRecording()) {
+          Log.d(LOGTAG,"isRecordTimerActive is true");
           try {
             if (null != mService) {
                 mService.cancelDelayedStop(FMRadioService.STOP_RECORD);
@@ -774,66 +763,35 @@
    private static final int RECORDTIMER_EXPIRED = 0x1003;
    private static final int RECORDTIMER_UPDATE = 0x1004;
 
-   private boolean hasRecordTimerExpired() {
-      boolean expired = true;
-      /* If record duration is 'until stopped' then return false and exit */
-      if (mRecordUntilStopped)
-          return false;
-      if (isRecordTimerActive()) {
-         long timeNow = ((SystemClock.elapsedRealtime()));
-         //Log.d(LOGTAG, "hasSleepTimerExpired - " + mSleepAtPhoneTime + " now: "+ timeNow);
-         if ((mRecording == false) || (timeNow < mRecordDuration)) {
-             expired = false;
-         }
-      }
-      return expired;
-   }
-
-   private boolean isRecordTimerActive() {
-      boolean active = false;
-      if (mRecordDuration > 0) {
-          active = true;
-      }
-      return active;
-   }
-
    private void updateExpiredRecordTime() {
       int vis = View.VISIBLE;
-      if (mRecordUntilStopped || isRecordTimerActive()) {
+      if(isRecording())
+      {
          long timeNow = ((SystemClock.elapsedRealtime()));
-         if (mRecordUntilStopped || (mRecordDuration >= timeNow)) {
-            long seconds = (timeNow - mRecordStartTime) / 1000;
-            String Msg = makeTimeString(seconds);
-            mRecordingMsgTV.setText(Msg);
-         }else {
-            /* Clean up timer */
-            mRecordDuration = 0;
-         }
+         long seconds = (timeNow - getRecordingStartTime()) / 1000;
+         String Msg = makeTimeString(seconds);
+         mRecordingMsgTV.setText(Msg);
+         mRecordingMsgTV.setVisibility(vis);
       }
-      mRecordingMsgTV.setVisibility(vis);
    }
 
    /* Recorder Thread processing */
    private Runnable doRecordProcessing = new Runnable() {
       public void run() {
-         boolean recordTimerExpired;
-         if (mRecordUntilStopped) {
-             recordTimerExpired = false;
-         }else {
-             recordTimerExpired = hasRecordTimerExpired();
-         }
-         while ((recordTimerExpired == false) &&
-                  (!Thread.currentThread().isInterrupted())) {
-            try {
-                Thread.sleep(500);
-                Message statusUpdate = new Message();
-                statusUpdate.what = RECORDTIMER_UPDATE;
-                mUIUpdateHandlerHandler.sendMessage(statusUpdate);
-                recordTimerExpired = hasRecordTimerExpired();
-            }catch (InterruptedException e) {
-                Thread.currentThread().interrupt();
+         while (isRecording() &&
+                 (!Thread.currentThread().isInterrupted()))
+         {
+            try
+            {
+               Thread.sleep(500);
+               Message statusUpdate = new Message();
+               statusUpdate.what = RECORDTIMER_UPDATE;
+               mUIUpdateHandlerHandler.sendMessage(statusUpdate);
+            } catch (InterruptedException e)
+            {
+               Thread.currentThread().interrupt();
             }
-            if(true == recordTimerExpired) {
+            if(!isRecording()) {
                Message finished = new Message();
                finished.what = RECORDTIMER_EXPIRED;
                mUIUpdateHandlerHandler.sendMessage(finished);
@@ -842,24 +800,22 @@
       }
    };
 
-   private static long mRecordDuration = 0;
-   private static long mRecordStartTime = 0;
-   private static boolean mRecordUntilStopped = false;
    private Thread mRecordUpdateHandlerThread = null;
 
-   private void initiateRecordDurationTimer(long mins) {
+   private long getRecordingStartTime() {
 
-      if(mins == FmSharedPreferences.RECORD_DUR_INDEX_3_VAL) {
-          mRecordUntilStopped = true;
-          mRecordStartTime = SystemClock.elapsedRealtime();
-      }else {
-          mRecordUntilStopped = false;
-          mRecordDuration = (mRecordStartTime = SystemClock.elapsedRealtime())
-                                   +
-                            (mins * 60 * 1000);
+      if(mService == null)
+         return 0;
+
+      try {
+           return mService.getRecordingStartTime();
+      }catch(RemoteException e) {
+           return 0;
       }
+   }
 
-      Log.d(LOGTAG, "Stop Recording in mins: " + mins);
+   private void initiateRecordDurationTimer(long mins ) {
+      Log.d(LOGTAG, "Stop Recording in mins : " + mins);
       initiateRecordThread();
     }
     private void initiateRecordThread() {
@@ -1775,11 +1731,27 @@
       }
    }
 
+   private void setRecordingStopImage() {
+       if(null != mRecordingMsgTV) {
+          mRecordingMsgTV.setCompoundDrawablesWithIntrinsicBounds
+                           (R.drawable.recorder_stop, 0, 0, 0);
+       }
+   }
+
+   private void setRecordingStartImage() {
+       if(null != mRecordingMsgTV) {
+          mRecordingMsgTV.setCompoundDrawablesWithIntrinsicBounds
+                           (R.drawable.recorder_start, 0, 0, 0);
+       }
+   }
+
    private void startRecordingTimer() {
       mRecording = true;
       int durationInMins = FmSharedPreferences.getRecordDuration();
       Log.e(LOGTAG, " Fected duration:" + durationInMins );
       initiateRecordDurationTimer( durationInMins );
+      setRecordingStopImage();
+      invalidateOptionsMenu();
    }
 
    private void stopRecording() {
@@ -1790,8 +1762,7 @@
        }
        if(null != mRecordingMsgTV) {
           mRecordingMsgTV.setText("");
-          mRecordingMsgTV.setCompoundDrawablesWithIntrinsicBounds
-                           (R.drawable.recorder_start, 0, 0, 0);
+          setRecordingStartImage();
        }
        if (mService != null) {
            try {
@@ -1800,7 +1771,7 @@
               e.printStackTrace();
            }
         }
-        mRecordDuration = 0;
+        invalidateOptionsMenu();
    }
 
    private boolean isRecording() {
@@ -1874,6 +1845,11 @@
          if ((mRecordingMsgTV != null) && !isRecording()) {
              mRecordingMsgTV.setText("");
          }
+         if(isRecording()) {
+            setRecordingStopImage();
+         }else {
+            setRecordingStartImage();
+         }
       }else {
          if (mRadioTextScroller != null) {
              mRadioTextScroller.stopScroll();
@@ -2376,13 +2352,11 @@
             }
          case RECORDTIMER_EXPIRED: {
                Log.d(LOGTAG, "mUIUpdateHandlerHandler - RECORDTIMER_EXPIRED");
-               mRecordDuration = 0;
                //Clear the Recorder text
                mRecordingMsgTV.setText("");
                if (mRecording != false) {
                   DebugToasts("Stop Recording", Toast.LENGTH_SHORT);
                   stopRecording();
-                  invalidateOptionsMenu();
                }
               return;
             }
@@ -2921,16 +2895,7 @@
                e.printStackTrace();
             }
             if (isRecording()) {
-                mRecordingMsgTV.setCompoundDrawablesWithIntrinsicBounds
-                                  (R.drawable.recorder_stop, 0, 0, 0);
                 initiateRecordThread();
-            }else if((mRecordDuration > 0) &&
-                      (mRecordUpdateHandlerThread != null)) {
-                mRecordUpdateHandlerThread.interrupt();
-                if(mRecordingMsgTV != null) {
-                   mRecordingMsgTV.setText("");
-                }
-                mRecordDuration = 0;
             }
             return;
          }else {
diff --git a/fmapp2/src/com/caf/fmradio/FMRadioService.java b/fmapp2/src/com/caf/fmradio/FMRadioService.java
index a0df1ef..5dfe72d 100644
--- a/fmapp2/src/com/caf/fmradio/FMRadioService.java
+++ b/fmapp2/src/com/caf/fmradio/FMRadioService.java
@@ -294,6 +294,7 @@
                          if (state == RECORD_START) {
                              Log.d(LOGTAG, "FM Recording started");
                              mFmRecordingOn = true;
+                             mSampleStart = SystemClock.elapsedRealtime();
                              try {
                                   if ((mServiceInUse) && (mCallbacks != null) ) {
                                       Log.d(LOGTAG, "start recording thread");
@@ -312,6 +313,7 @@
                              } catch (RemoteException e) {
                                   e.printStackTrace();
                              }
+                             mSampleStart = 0;
                         }
                      }
                  }
@@ -688,6 +690,14 @@
    private void sendRecordIntent(int action) {
        Intent intent = new Intent(ACTION_FM_RECORDING);
        intent.putExtra("state", action);
+       if(action == RECORD_START) {
+          int mRecordDuration = -1;
+          if(FmSharedPreferences.getRecordDuration() !=
+             FmSharedPreferences.RECORD_DUR_INDEX_3_VAL) {
+             mRecordDuration = (FmSharedPreferences.getRecordDuration() * 60 * 1000);
+          }
+          intent.putExtra("record_duration", mRecordDuration);
+        }
        Log.d(LOGTAG, "Sending Recording intent for = " +action);
        getApplicationContext().sendBroadcast(intent);
    }
@@ -1469,6 +1479,10 @@
       {
            return (mService.get().setRxRepeatCount(count));
       }
+      public long getRecordingStartTime()
+      {
+           return (mService.get().getRecordingStartTime());
+      }
    }
    private final IBinder mBinder = new ServiceStub(this);
 
@@ -2929,6 +2943,9 @@
          return false;
    }
 
+   public long getRecordingStartTime() {
+      return mSampleStart;
+   }
    //handling the sleep and record stop when FM App not in focus
    private void delayedStop(long duration, int nType) {
        int whatId = (nType == STOP_SERVICE) ? STOPSERVICE_ONSLEEP: STOPRECORD_ONTIMEOUT;
diff --git a/fmapp2/src/com/caf/fmradio/FMStats.java b/fmapp2/src/com/caf/fmradio/FMStats.java
index 52b9caa..18ab71c 100644
--- a/fmapp2/src/com/caf/fmradio/FMStats.java
+++ b/fmapp2/src/com/caf/fmradio/FMStats.java
@@ -65,6 +65,9 @@
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.IntentFilter;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.MenuInflater;
 import android.os.SystemClock;
 
 import java.io.FileNotFoundException;
@@ -75,6 +78,8 @@
 import android.os.SystemProperties;
 import java.io.BufferedReader;
 import java.io.FileReader;
+import java.util.Formatter;
+import java.util.Locale;
 
 public class FMStats extends Activity  {
 
@@ -177,6 +182,8 @@
     private Thread mMultiUpdateThread = null;
     private static final int STATUS_UPDATE = 1;
     private static final int STATUS_DONE = 2;
+    private static final int RECORDTIMER_UPDATE = 3;
+    private static final int RECORDTIMER_EXPIRED = 4;
     private static final int STOP_ROW_ID = 200;
     private static final int NEW_ROW_ID = 300;
     private int mStopIds = STOP_ROW_ID;
@@ -230,6 +237,15 @@
 
     private int curSweepMthd = 0;
 
+    private Thread mRecordUpdateHandlerThread = null;
+    boolean mRecording = false;
+
+
+    private static StringBuilder sFormatBuilder = new StringBuilder();
+    private static Formatter sFormatter = new Formatter(sFormatBuilder, Locale
+                                                       .getDefault());
+    private static final Object[] sTimeArgs = new Object[5];
+
     private final String FREQ_LIST_FILE_NAME = "/freq_list_comma_separated.txt";
     private static final String BAND_SWEEP_START_DELAY_TIMEOUT = "com.caf.fmradio.SWEEP_START_DELAY_EXP";
     private static final String BAND_SWEEP_DWELL_DELAY_TIMEOUT = "com.caf.fmradio.SWEEP_DWELL_DELAY_EXP";
@@ -328,6 +344,30 @@
         registerBandSweepDwellExprdListener();
     }
 
+    @Override
+    public void onStart() {
+       super.onStart();
+       if(isRecording()) {
+          Log.d(LOGTAG, "onStart");
+          initiateRecordThread();
+       }
+    }
+
+    @Override
+    public void onStop() {
+       super.onStop();
+       if(isRecording()) {
+          try {
+               if(null != mRecordUpdateHandlerThread) {
+                  mRecordUpdateHandlerThread.interrupt();
+               }
+          }catch (NullPointerException e) {
+               e.printStackTrace();
+          }
+       }
+    }
+
+    @Override
     public void onDestroy() {
 
         stopCurTest();
@@ -1894,6 +1934,17 @@
                case STATUS_DONE:
                     SetButtonState(true);
                     break;
+               case RECORDTIMER_EXPIRED:
+                    Log.d(LOGTAG, "mUIUpdateHandlerHandler - RECORDTIMER_EXPIRED");
+                    if(!isRecording()) {
+                       Log.d(LOGTAG, "Stop Recording");
+                       stopRecording();
+                    }
+                   break;
+               case RECORDTIMER_UPDATE:
+                   Log.d(LOGTAG, "mUIUpdateHandlerHandler - RECORDTIMER_UPDATE");
+                   updateExpiredRecordTime();
+                   break;
                }
             }
     };
@@ -1972,6 +2023,9 @@
                 {
                    e.printStackTrace();
                 }
+                if(isRecording()) {
+                   initiateRecordThread();
+                }
                 return;
              } else
              {
@@ -1989,17 +2043,19 @@
           public void onEnabled()
           {
              Log.d(LOGTAG, "mServiceCallbacks.onEnabled :");
+             invalidateOptionsMenu();
           }
 
           public void onDisabled()
           {
              Log.d(LOGTAG, "mServiceCallbacks.onDisabled :");
-             stopCurTest();
+             stopAllOperations();
           }
 
           public void onRadioReset()
           {
              Log.d(LOGTAG, "mServiceCallbacks.onRadioReset :");
+             stopAllOperations();
           }
 
           public void onTuneStatusChanged()
@@ -2064,6 +2120,7 @@
           public void onRecordingStopped()
           {
              Log.d(LOGTAG, "mServiceCallbacks.onDisabled :");
+             stopRecording();
           }
           public void onFinishActivity()
           {
@@ -2072,6 +2129,10 @@
           public void onRecordingStarted()
           {
              Log.d(LOGTAG, "mServiceCallbacks.onRecordingStarted:");
+             int durationInMins = FmSharedPreferences.getRecordDuration();
+             Log.e(LOGTAG, " Fected duration: " + durationInMins);
+             initiateRecordDurationTimer(durationInMins);
+             invalidateOptionsMenu();
           }
       };
       /* Radio Vars */
@@ -2314,4 +2375,187 @@
           mNextFreqInterface = null;
        }
     }
- }
+
+    public boolean isRecording() {
+       if(mService == null)
+          return false;
+       try {
+            return mService.isFmRecordingOn();
+       }catch(RemoteException e) {
+            return false;
+       }
+    }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        MenuInflater inflater = getMenuInflater();
+        inflater.inflate(R.menu.menu_rf_stats, menu);
+        return true;
+    }
+
+    @Override
+    public boolean onPrepareOptionsMenu(Menu menu) {
+        super.onPrepareOptionsMenu(menu);
+        MenuItem item;
+
+        item = menu.findItem(R.id.menu_recording);
+        if(item != null && !isRecording()) {
+           item.setTitle(R.string.menu_record_start);
+           item.setEnabled(isFmOn());
+        }else if(item != null) {
+           item.setTitle(R.string.menu_record_stop);
+           setRecordDurationDisplay(menu, R.id.menu_record_duration);
+        }
+        return true;
+    }
+
+    private void setRecordDurationDisplay(Menu menu, int id) {
+       MenuItem item;
+       long timeNow;
+       long seconds;
+
+       if(menu == null)
+          return;
+       item = menu.findItem(id);
+       if(item != null) {
+          timeNow = SystemClock.elapsedRealtime();
+          seconds = (timeNow - getRecordingStartTime()) / 1000;
+          item.setTitle(makeTimeString(seconds));
+       }
+    }
+
+    private void startRecording() {
+      if(isFmOn()) {
+         try {
+              mService.startRecording();
+         }catch(RemoteException e) {
+              e.printStackTrace();
+         }
+      }
+    }
+
+    private void stopRecording() {
+       if(null != mRecordUpdateHandlerThread) {
+          mRecordUpdateHandlerThread.interrupt();
+       }
+       if(mService != null) {
+          try {
+               mService.stopRecording();
+           }catch (RemoteException e) {
+               e.printStackTrace();
+           }
+        }
+        invalidateOptionsMenu();
+    }
+
+    private long getRecordingStartTime() {
+       if(mService == null)
+          return 0;
+       try {
+            return mService.getRecordingStartTime();
+       }catch(RemoteException e) {
+            return 0;
+       }
+    }
+
+    private void initiateRecordDurationTimer(long mins ) {
+       Log.d(LOGTAG, "Stop Recording in mins : " + mins);
+       initiateRecordThread();
+    }
+
+    private void initiateRecordThread() {
+      if(mRecordUpdateHandlerThread == null) {
+         mRecordUpdateHandlerThread = new Thread(null, doRecordProcessing,
+                                                "RecordUpdateThread");
+      }
+      /* Launch the dummy thread to simulate the transfer progress */
+      Log.d(LOGTAG, "Thread State: " + mRecordUpdateHandlerThread.getState());
+      if(mRecordUpdateHandlerThread.getState() == Thread.State.TERMINATED) {
+         mRecordUpdateHandlerThread = new Thread(null, doRecordProcessing,
+                                                "RecordUpdateThread");
+      }
+      /* If the thread state is "new" then the thread has not yet started */
+      if(mRecordUpdateHandlerThread.getState() == Thread.State.NEW) {
+         mRecordUpdateHandlerThread.start();
+      }
+   }
+
+   /* Recorder Thread processing */
+   private Runnable doRecordProcessing = new Runnable() {
+      public void run() {
+         while(isRecording() &&
+                 (!Thread.currentThread().isInterrupted())) {
+               try {
+                    Thread.sleep(500);
+                    Message statusUpdate = new Message();
+                    statusUpdate.what = RECORDTIMER_UPDATE;
+                    mUIUpdateHandlerHandler.sendMessage(statusUpdate);
+               }catch (InterruptedException e) {
+                    Thread.currentThread().interrupt();
+               }
+               if(!isRecording()) {
+                  Message finished = new Message();
+                  finished.what = RECORDTIMER_EXPIRED;
+                  mUIUpdateHandlerHandler.sendMessage(finished);
+               }
+         }
+      }
+   };
+
+   private void updateExpiredRecordTime() {
+      int vis = View.VISIBLE;
+      if(isRecording()) {
+         invalidateOptionsMenu();
+      }
+   }
+
+   private String makeTimeString(long secs) {
+      String durationformat = getString(R.string.durationformat);
+
+      /** Provide multiple arguments so the format can be changed easily by
+       *  modifying the xml.
+       **/
+      sFormatBuilder.setLength(0);
+
+      final Object[] timeArgs = sTimeArgs;
+      timeArgs[0] = secs / 3600;
+      timeArgs[1] = secs / 60;
+      timeArgs[2] = (secs / 60) % 60;
+      timeArgs[3] = secs;
+      timeArgs[4] = secs % 60;
+
+      return sFormatter.format(durationformat, timeArgs).toString();
+   }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+        switch(item.getItemId())
+        {
+          case R.id.menu_recording:
+               if(isRecording()) {
+                  stopRecording();
+               }else {
+                  startRecording();
+               }
+               break;
+        }
+        return true;
+    }
+
+    private boolean isFmOn() {
+       boolean status = false;
+
+       if(mService != null) {
+          try {
+               status = mService.isFmOn();
+          }catch(RemoteException e) {
+          }
+       }
+       return status;
+    }
+
+    private void stopAllOperations() {
+       stopCurTest();
+       stopRecording();
+    }
+}
diff --git a/fmapp2/src/com/caf/fmradio/IFMRadioService.aidl b/fmapp2/src/com/caf/fmradio/IFMRadioService.aidl
index c2dc7fb..fe54124 100644
--- a/fmapp2/src/com/caf/fmradio/IFMRadioService.aidl
+++ b/fmapp2/src/com/caf/fmradio/IFMRadioService.aidl
@@ -70,5 +70,6 @@
     int getGoodChRmssiTh();
     int getAfJmpRmssiSamplesCnt();
     boolean setRxRepeatCount(int count);
+    long getRecordingStartTime();
 }