Implement standalone event tracing in AppRTCDemo.

Logs tracing events (TRACE_EVENT0 and friends) to storage in a format
compatible with chrome://tracing which can be used for performance
evaluation, finding lock contention and other sweet things). Tracing is
still basic and doesn't contain thread metadata or logging of tracing
arguments.

BUG=webrtc:5158
R=tommi@webrtc.org

Review URL: https://codereview.webrtc.org/1457383002 .

Cr-Commit-Position: refs/heads/master@{#10921}
diff --git a/talk/app/webrtc/java/jni/peerconnection_jni.cc b/talk/app/webrtc/java/jni/peerconnection_jni.cc
index 639d883..f22d604 100644
--- a/talk/app/webrtc/java/jni/peerconnection_jni.cc
+++ b/talk/app/webrtc/java/jni/peerconnection_jni.cc
@@ -74,10 +74,11 @@
 #include "talk/media/webrtc/webrtcvideoencoderfactory.h"
 #include "webrtc/base/bind.h"
 #include "webrtc/base/checks.h"
+#include "webrtc/base/event_tracer.h"
 #include "webrtc/base/logging.h"
 #include "webrtc/base/logsinks.h"
-#include "webrtc/base/networkmonitor.h"
 #include "webrtc/base/messagequeue.h"
+#include "webrtc/base/networkmonitor.h"
 #include "webrtc/base/ssladapter.h"
 #include "webrtc/base/stringutils.h"
 #include "webrtc/system_wrappers/include/field_trial_default.h"
@@ -1054,6 +1055,32 @@
   webrtc::field_trial::InitFieldTrialsFromString(field_trials_init_string);
 }
 
+JOW(void, PeerConnectionFactory_initializeInternalTracer)(JNIEnv* jni, jclass) {
+  rtc::tracing::SetupInternalTracer();
+}
+
+JOW(jboolean, PeerConnectionFactory_startInternalTracingCapture)(
+    JNIEnv* jni, jclass, jstring j_event_tracing_filename) {
+  if (!j_event_tracing_filename)
+    return false;
+
+  const char* init_string =
+      jni->GetStringUTFChars(j_event_tracing_filename, NULL);
+  LOG(LS_INFO) << "Starting internal tracing to: " << init_string;
+  bool ret = rtc::tracing::StartInternalCapture(init_string);
+  jni->ReleaseStringUTFChars(j_event_tracing_filename, init_string);
+  return ret;
+}
+
+JOW(void, PeerConnectionFactory_stopInternalTracingCapture)(
+    JNIEnv* jni, jclass) {
+  rtc::tracing::StopInternalCapture();
+}
+
+JOW(void, PeerConnectionFactory_shutdownInternalTracer)(JNIEnv* jni, jclass) {
+  rtc::tracing::ShutdownInternalTracer();
+}
+
 // Helper struct for working around the fact that CreatePeerConnectionFactory()
 // comes in two flavors: either entirely automagical (constructing its own
 // threads and deleting them on teardown, but no external codec factory support)
diff --git a/talk/app/webrtc/java/src/org/webrtc/PeerConnectionFactory.java b/talk/app/webrtc/java/src/org/webrtc/PeerConnectionFactory.java
index c2eff1a..d759c69 100644
--- a/talk/app/webrtc/java/src/org/webrtc/PeerConnectionFactory.java
+++ b/talk/app/webrtc/java/src/org/webrtc/PeerConnectionFactory.java
@@ -73,6 +73,15 @@
   // Field trial initialization. Must be called before PeerConnectionFactory
   // is created.
   public static native void initializeFieldTrials(String fieldTrialsInitString);
+  // Internal tracing initialization. Must be called before PeerConnectionFactory is created to
+  // prevent racing with tracing code.
+  public static native void initializeInternalTracer();
+  // Internal tracing shutdown, called to prevent resource leaks. Must be called after
+  // PeerConnectionFactory is gone to prevent races with code performing tracing.
+  public static native void shutdownInternalTracer();
+  // Start/stop internal capturing of internal tracing.
+  public static native boolean startInternalTracingCapture(String tracing_filename);
+  public static native void stopInternalTracingCapture();
 
   public PeerConnectionFactory() {
     nativeFactory = nativeCreatePeerConnectionFactory();
diff --git a/talk/session/media/channel.cc b/talk/session/media/channel.cc
index a25ab7d..4682c05 100644
--- a/talk/session/media/channel.cc
+++ b/talk/session/media/channel.cc
@@ -29,7 +29,6 @@
 
 #include "talk/media/base/constants.h"
 #include "talk/media/base/rtputils.h"
-#include "webrtc/p2p/base/transportchannel.h"
 #include "talk/session/media/channelmanager.h"
 #include "webrtc/base/bind.h"
 #include "webrtc/base/buffer.h"
@@ -37,6 +36,8 @@
 #include "webrtc/base/common.h"
 #include "webrtc/base/dscp.h"
 #include "webrtc/base/logging.h"
+#include "webrtc/base/trace_event.h"
+#include "webrtc/p2p/base/transportchannel.h"
 
 namespace cricket {
 
@@ -471,6 +472,7 @@
                                 const char* data, size_t len,
                                 const rtc::PacketTime& packet_time,
                                 int flags) {
+  TRACE_EVENT0("webrtc", "BaseChannel::OnChannelRead");
   // OnChannelRead gets called from P2PSocket; now pass data to MediaEngine
   ASSERT(worker_thread_ == rtc::Thread::Current());
 
@@ -1272,6 +1274,7 @@
 }
 
 void BaseChannel::OnMessage(rtc::Message *pmsg) {
+  TRACE_EVENT0("webrtc", "BaseChannel::OnMessage");
   switch (pmsg->message_id) {
     case MSG_RTPPACKET:
     case MSG_RTCPPACKET: {
diff --git a/webrtc/base/atomicops.h b/webrtc/base/atomicops.h
index b21bd99..42b8709 100644
--- a/webrtc/base/atomicops.h
+++ b/webrtc/base/atomicops.h
@@ -43,6 +43,16 @@
                                         new_value,
                                         old_value);
   }
+  // Pointer variants.
+  template <typename T>
+  static T* AtomicLoadPtr(T* volatile* ptr) {
+    return *ptr;
+  }
+  template <typename T>
+  static T* CompareAndSwapPtr(T* volatile* ptr, T* old_value, T* new_value) {
+    return static_cast<T*>(::InterlockedCompareExchangePointer(
+        reinterpret_cast<PVOID volatile*>(ptr), old_value, new_value));
+  }
 #else
   static int Increment(volatile int* i) {
     return __sync_add_and_fetch(i, 1);
@@ -59,6 +69,15 @@
   static int CompareAndSwap(volatile int* i, int old_value, int new_value) {
     return __sync_val_compare_and_swap(i, old_value, new_value);
   }
+  // Pointer variants.
+  template <typename T>
+  static T* AtomicLoadPtr(T* volatile* ptr) {
+    return __atomic_load_n(ptr, __ATOMIC_ACQUIRE);
+  }
+  template <typename T>
+  static T* CompareAndSwapPtr(T* volatile* ptr, T* old_value, T* new_value) {
+    return __sync_val_compare_and_swap(ptr, old_value, new_value);
+  }
 #endif
 };
 
diff --git a/webrtc/base/event_tracer.cc b/webrtc/base/event_tracer.cc
index 5c6d39f..dbf85a4 100644
--- a/webrtc/base/event_tracer.cc
+++ b/webrtc/base/event_tracer.cc
@@ -7,15 +7,26 @@
  *  in the file PATENTS.  All contributing project authors may
  *  be found in the AUTHORS file in the root of the source tree.
  */
-
 #include "webrtc/base/event_tracer.h"
 
+#include <inttypes.h>
+
+#include <vector>
+
+#include "webrtc/base/checks.h"
+#include "webrtc/base/criticalsection.h"
+#include "webrtc/base/event.h"
+#include "webrtc/base/logging.h"
+#include "webrtc/base/platform_thread.h"
+#include "webrtc/base/timeutils.h"
+#include "webrtc/base/trace_event.h"
+
 namespace webrtc {
 
 namespace {
 
-GetCategoryEnabledPtr g_get_category_enabled_ptr = 0;
-AddTraceEventPtr g_add_trace_event_ptr = 0;
+GetCategoryEnabledPtr g_get_category_enabled_ptr = nullptr;
+AddTraceEventPtr g_add_trace_event_ptr = nullptr;
 
 }  // namespace
 
@@ -25,7 +36,6 @@
   g_add_trace_event_ptr = add_trace_event_ptr;
 }
 
-// static
 const unsigned char* EventTracer::GetCategoryEnabled(const char* name) {
   if (g_get_category_enabled_ptr)
     return g_get_category_enabled_ptr(name);
@@ -34,7 +44,8 @@
   return reinterpret_cast<const unsigned char*>("\0");
 }
 
-// static
+// Arguments to this function (phase, etc.) are as defined in
+// webrtc/base/trace_event.h.
 void EventTracer::AddTraceEvent(char phase,
                                 const unsigned char* category_enabled,
                                 const char* name,
@@ -58,3 +69,197 @@
 }
 
 }  // namespace webrtc
+
+namespace rtc {
+namespace tracing {
+namespace {
+
+static bool EventTracingThreadFunc(void* params);
+
+// Atomic-int fast path for avoiding logging when disabled.
+static volatile int g_event_logging_active = 0;
+
+// TODO(pbos): Log metadata for all threads, etc.
+class EventLogger final {
+ public:
+  EventLogger()
+      : logging_thread_(EventTracingThreadFunc, this, "EventTracingThread"),
+        shutdown_event_(false, false) {}
+  ~EventLogger() { RTC_DCHECK(thread_checker_.CalledOnValidThread()); }
+
+  void AddTraceEvent(const char* name,
+                     const unsigned char* category_enabled,
+                     char phase,
+                     uint64_t timestamp,
+                     int pid,
+                     rtc::PlatformThreadId thread_id) {
+    rtc::CritScope lock(&crit_);
+    trace_events_.push_back(
+        {name, category_enabled, phase, timestamp, 1, thread_id});
+  }
+
+// The TraceEvent format is documented here:
+// https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview
+  void Log() {
+    RTC_DCHECK(output_file_);
+    static const int kLoggingIntervalMs = 100;
+    fprintf(output_file_, "{ \"traceEvents\": [\n");
+    bool has_logged_event = false;
+    while (true) {
+      bool shutting_down = shutdown_event_.Wait(kLoggingIntervalMs);
+      std::vector<TraceEvent> events;
+      {
+        rtc::CritScope lock(&crit_);
+        trace_events_.swap(events);
+      }
+      for (const TraceEvent& e : events) {
+        fprintf(output_file_,
+                "%s{ \"name\": \"%s\""
+                ", \"cat\": \"%s\""
+                ", \"ph\": \"%c\""
+                ", \"ts\": %" PRIu64
+                ", \"pid\": %d"
+                ", \"tid\": %d}\n",
+                has_logged_event ? "," : " ", e.name, e.category_enabled,
+                e.phase, e.timestamp, e.pid, e.tid);
+        has_logged_event = true;
+      }
+      if (shutting_down)
+        break;
+    }
+    fprintf(output_file_, "]}\n");
+    if (output_file_owned_)
+      fclose(output_file_);
+    output_file_ = nullptr;
+  }
+
+  void Start(FILE* file, bool owned) {
+    RTC_DCHECK(file);
+    RTC_DCHECK(!output_file_);
+    output_file_ = file;
+    output_file_owned_ = owned;
+    {
+      rtc::CritScope lock(&crit_);
+      // Since the atomic fast-path for adding events to the queue can be
+      // bypassed while the logging thread is shutting down there may be some
+      // stale events in the queue, hence the vector needs to be cleared to not
+      // log events from a previous logging session (which may be days old).
+      trace_events_.clear();
+    }
+    // Enable event logging (fast-path). This should be disabled since starting
+    // shouldn't be done twice.
+    RTC_CHECK_EQ(0,
+                 rtc::AtomicOps::CompareAndSwap(&g_event_logging_active, 0, 1));
+
+    // Finally start, everything should be set up now.
+    logging_thread_.Start();
+  }
+
+  void Stop() {
+    // Try to stop. Abort if we're not currently logging.
+    if (rtc::AtomicOps::CompareAndSwap(&g_event_logging_active, 1, 0) == 0)
+      return;
+
+    // Wake up logging thread to finish writing.
+    shutdown_event_.Set();
+    // Join the logging thread.
+    logging_thread_.Stop();
+  }
+
+ private:
+  struct TraceEvent {
+    const char* name;
+    const unsigned char* category_enabled;
+    char phase;
+    uint64_t timestamp;
+    int pid;
+    rtc::PlatformThreadId tid;
+  };
+
+  rtc::CriticalSection crit_;
+  std::vector<TraceEvent> trace_events_ GUARDED_BY(crit_);
+  rtc::PlatformThread logging_thread_;
+  rtc::Event shutdown_event_;
+  rtc::ThreadChecker thread_checker_;
+  FILE* output_file_ = nullptr;
+  bool output_file_owned_ = false;
+};
+
+static bool EventTracingThreadFunc(void* params) {
+  static_cast<EventLogger*>(params)->Log();
+  return true;
+}
+
+static EventLogger* volatile g_event_logger = nullptr;
+static const char* const kDisabledTracePrefix = TRACE_DISABLED_BY_DEFAULT("");
+const unsigned char* InternalGetCategoryEnabled(const char* name) {
+  const char* prefix_ptr = &kDisabledTracePrefix[0];
+  const char* name_ptr = name;
+  // Check whether name contains the default-disabled prefix.
+  while (*prefix_ptr == *name_ptr && *prefix_ptr != '\0') {
+    ++prefix_ptr;
+    ++name_ptr;
+  }
+  return reinterpret_cast<const unsigned char*>(*prefix_ptr == '\0' ? ""
+                                                                    : name);
+}
+
+void InternalAddTraceEvent(char phase,
+                           const unsigned char* category_enabled,
+                           const char* name,
+                           unsigned long long id,
+                           int num_args,
+                           const char** arg_names,
+                           const unsigned char* arg_types,
+                           const unsigned long long* arg_values,
+                           unsigned char flags) {
+  // Fast path for when event tracing is inactive.
+  if (rtc::AtomicOps::AcquireLoad(&g_event_logging_active) == 0)
+    return;
+
+  g_event_logger->AddTraceEvent(name, category_enabled, phase,
+                                rtc::TimeMicros(), 1, rtc::CurrentThreadId());
+}
+
+}  // namespace
+
+void SetupInternalTracer() {
+  RTC_CHECK(rtc::AtomicOps::CompareAndSwapPtr(
+                &g_event_logger, static_cast<EventLogger*>(nullptr),
+                new EventLogger()) == nullptr);
+  g_event_logger = new EventLogger();
+  webrtc::SetupEventTracer(InternalGetCategoryEnabled, InternalAddTraceEvent);
+}
+
+void StartInternalCaptureToFile(FILE* file) {
+  g_event_logger->Start(file, false);
+}
+
+bool StartInternalCapture(const char* filename) {
+  FILE* file = fopen(filename, "w");
+  if (!file) {
+    LOG(LS_ERROR) << "Failed to open trace file '" << filename
+                  << "' for writing.";
+    return false;
+  }
+  g_event_logger->Start(file, true);
+  return true;
+}
+
+void StopInternalCapture() {
+  g_event_logger->Stop();
+}
+
+void ShutdownInternalTracer() {
+  StopInternalCapture();
+  EventLogger* old_logger = rtc::AtomicOps::AtomicLoadPtr(&g_event_logger);
+  RTC_DCHECK(old_logger);
+  RTC_CHECK(rtc::AtomicOps::CompareAndSwapPtr(
+                &g_event_logger, old_logger,
+                static_cast<EventLogger*>(nullptr)) == old_logger);
+  delete old_logger;
+  webrtc::SetupEventTracer(nullptr, nullptr);
+}
+
+}  // namespace tracing
+}  // namespace rtc
diff --git a/webrtc/base/event_tracer.h b/webrtc/base/event_tracer.h
index cfc6e9e..51c8cfd 100644
--- a/webrtc/base/event_tracer.h
+++ b/webrtc/base/event_tracer.h
@@ -26,6 +26,8 @@
 #ifndef WEBRTC_BASE_EVENT_TRACER_H_
 #define WEBRTC_BASE_EVENT_TRACER_H_
 
+#include <stdio.h>
+
 namespace webrtc {
 
 typedef const unsigned char* (*GetCategoryEnabledPtr)(const char* name);
@@ -68,4 +70,16 @@
 
 }  // namespace webrtc
 
+namespace rtc {
+namespace tracing {
+// Set up internal event tracer.
+void SetupInternalTracer();
+bool StartInternalCapture(const char* filename);
+void StartInternalCaptureToFile(FILE* file);
+void StopInternalCapture();
+// Make sure we run this, this will tear down the internal tracing.
+void ShutdownInternalTracer();
+}  // namespace tracing
+}  // namespace rtc
+
 #endif  // WEBRTC_BASE_EVENT_TRACER_H_
diff --git a/webrtc/call/call.cc b/webrtc/call/call.cc
index 9209c7c..f6efa6d 100644
--- a/webrtc/call/call.cc
+++ b/webrtc/call/call.cc
@@ -655,6 +655,7 @@
 PacketReceiver::DeliveryStatus Call::DeliverRtcp(MediaType media_type,
                                                  const uint8_t* packet,
                                                  size_t length) {
+  TRACE_EVENT0("webrtc", "Call::DeliverRtcp");
   // TODO(pbos): Figure out what channel needs it actually.
   //             Do NOT broadcast! Also make sure it's a valid packet.
   //             Return DELIVERY_UNKNOWN_SSRC if it can be determined that
@@ -688,6 +689,7 @@
                                                 const uint8_t* packet,
                                                 size_t length,
                                                 const PacketTime& packet_time) {
+  TRACE_EVENT0("webrtc", "Call::DeliverRtp");
   // Minimum RTP header size.
   if (length < 12)
     return DELIVERY_PACKET_ERROR;
diff --git a/webrtc/examples/androidapp/res/values/strings.xml b/webrtc/examples/androidapp/res/values/strings.xml
index 573ff80..dadb34e 100644
--- a/webrtc/examples/androidapp/res/values/strings.xml
+++ b/webrtc/examples/androidapp/res/values/strings.xml
@@ -120,4 +120,9 @@
     <string name="pref_displayhud_dlg">Display call statistics.</string>
     <string name="pref_displayhud_default" translatable="false">false</string>
 
+    <string name="pref_tracing_key">tracing_preference</string>
+    <string name="pref_tracing_title">Debug performance tracing.</string>
+    <string name="pref_tracing_dlg">Debug performance tracing.</string>
+    <string name="pref_tracing_default" translatable="false">false</string>
+
 </resources>
diff --git a/webrtc/examples/androidapp/res/xml/preferences.xml b/webrtc/examples/androidapp/res/xml/preferences.xml
index b54f244..6915f4b 100644
--- a/webrtc/examples/androidapp/res/xml/preferences.xml
+++ b/webrtc/examples/androidapp/res/xml/preferences.xml
@@ -124,6 +124,12 @@
             android:title="@string/pref_displayhud_title"
             android:dialogTitle="@string/pref_displayhud_dlg"
            android:defaultValue="@string/pref_displayhud_default" />
+
+        <CheckBoxPreference
+            android:key="@string/pref_tracing_key"
+            android:title="@string/pref_tracing_title"
+            android:dialogTitle="@string/pref_tracing_dlg"
+           android:defaultValue="@string/pref_tracing_default" />
     </PreferenceCategory>
 
 </PreferenceScreen>
diff --git a/webrtc/examples/androidapp/src/org/appspot/apprtc/CallActivity.java b/webrtc/examples/androidapp/src/org/appspot/apprtc/CallActivity.java
index 7a4d09e..af7f4b4 100644
--- a/webrtc/examples/androidapp/src/org/appspot/apprtc/CallActivity.java
+++ b/webrtc/examples/androidapp/src/org/appspot/apprtc/CallActivity.java
@@ -78,6 +78,7 @@
       "org.appspot.apprtc.OPENSLES";
   public static final String EXTRA_DISPLAY_HUD =
       "org.appspot.apprtc.DISPLAY_HUD";
+  public static final String EXTRA_TRACING = "org.appspot.apprtc.TRACING";
   public static final String EXTRA_CMDLINE =
       "org.appspot.apprtc.CMDLINE";
   public static final String EXTRA_RUNTIME =
@@ -213,9 +214,11 @@
       return;
     }
     boolean loopback = intent.getBooleanExtra(EXTRA_LOOPBACK, false);
+    boolean tracing = intent.getBooleanExtra(EXTRA_TRACING, false);
     peerConnectionParameters = new PeerConnectionParameters(
         intent.getBooleanExtra(EXTRA_VIDEO_CALL, true),
         loopback,
+        tracing,
         intent.getIntExtra(EXTRA_VIDEO_WIDTH, 0),
         intent.getIntExtra(EXTRA_VIDEO_HEIGHT, 0),
         intent.getIntExtra(EXTRA_VIDEO_FPS, 0),
diff --git a/webrtc/examples/androidapp/src/org/appspot/apprtc/ConnectActivity.java b/webrtc/examples/androidapp/src/org/appspot/apprtc/ConnectActivity.java
index 2d1d5aa..64ca5da 100644
--- a/webrtc/examples/androidapp/src/org/appspot/apprtc/ConnectActivity.java
+++ b/webrtc/examples/androidapp/src/org/appspot/apprtc/ConnectActivity.java
@@ -69,6 +69,7 @@
   private String keyprefNoAudioProcessingPipeline;
   private String keyprefOpenSLES;
   private String keyprefDisplayHud;
+  private String keyprefTracing;
   private String keyprefRoomServerUrl;
   private String keyprefRoom;
   private String keyprefRoomList;
@@ -97,6 +98,7 @@
     keyprefNoAudioProcessingPipeline = getString(R.string.pref_noaudioprocessing_key);
     keyprefOpenSLES = getString(R.string.pref_opensles_key);
     keyprefDisplayHud = getString(R.string.pref_displayhud_key);
+    keyprefTracing = getString(R.string.pref_tracing_key);
     keyprefRoomServerUrl = getString(R.string.pref_room_server_url_key);
     keyprefRoom = getString(R.string.pref_room_key);
     keyprefRoomList = getString(R.string.pref_room_list_key);
@@ -328,6 +330,9 @@
     boolean displayHud = sharedPref.getBoolean(keyprefDisplayHud,
         Boolean.valueOf(getString(R.string.pref_displayhud_default)));
 
+    boolean tracing = sharedPref.getBoolean(
+            keyprefTracing, Boolean.valueOf(getString(R.string.pref_tracing_default)));
+
     // Start AppRTCDemo activity.
     Log.d(TAG, "Connecting to room " + roomId + " at URL " + roomUrl);
     if (validateUrl(roomUrl)) {
@@ -352,6 +357,7 @@
       intent.putExtra(CallActivity.EXTRA_AUDIO_BITRATE, audioStartBitrate);
       intent.putExtra(CallActivity.EXTRA_AUDIOCODEC, audioCodec);
       intent.putExtra(CallActivity.EXTRA_DISPLAY_HUD, displayHud);
+      intent.putExtra(CallActivity.EXTRA_TRACING, tracing);
       intent.putExtra(CallActivity.EXTRA_CMDLINE, commandLineRun);
       intent.putExtra(CallActivity.EXTRA_RUNTIME, runTimeMs);
 
diff --git a/webrtc/examples/androidapp/src/org/appspot/apprtc/PeerConnectionClient.java b/webrtc/examples/androidapp/src/org/appspot/apprtc/PeerConnectionClient.java
index 1bdbc3b..37f371d 100644
--- a/webrtc/examples/androidapp/src/org/appspot/apprtc/PeerConnectionClient.java
+++ b/webrtc/examples/androidapp/src/org/appspot/apprtc/PeerConnectionClient.java
@@ -128,6 +128,7 @@
   public static class PeerConnectionParameters {
     public final boolean videoCallEnabled;
     public final boolean loopback;
+    public final boolean tracing;
     public final int videoWidth;
     public final int videoHeight;
     public final int videoFps;
@@ -141,13 +142,14 @@
     public final boolean useOpenSLES;
 
     public PeerConnectionParameters(
-        boolean videoCallEnabled, boolean loopback,
+        boolean videoCallEnabled, boolean loopback, boolean tracing,
         int videoWidth, int videoHeight, int videoFps, int videoStartBitrate,
         String videoCodec, boolean videoCodecHwAcceleration, boolean captureToTexture,
         int audioStartBitrate, String audioCodec,
         boolean noAudioProcessing, boolean useOpenSLES) {
       this.videoCallEnabled = videoCallEnabled;
       this.loopback = loopback;
+      this.tracing = tracing;
       this.videoWidth = videoWidth;
       this.videoHeight = videoHeight;
       this.videoFps = videoFps;
@@ -285,6 +287,10 @@
   }
 
   private void createPeerConnectionFactoryInternal(Context context) {
+      PeerConnectionFactory.initializeInternalTracer();
+      if (peerConnectionParameters.tracing) {
+          PeerConnectionFactory.startInternalTracingCapture("/mnt/sdcard/webrtc-trace.txt");
+      }
     Log.d(TAG, "Create peer connection factory. Use video: " +
         peerConnectionParameters.videoCallEnabled);
     isError = false;
@@ -502,6 +508,8 @@
     options = null;
     Log.d(TAG, "Closing peer connection done.");
     events.onPeerConnectionClosed();
+    PeerConnectionFactory.stopInternalTracingCapture();
+    PeerConnectionFactory.shutdownInternalTracer();
   }
 
   public boolean isHDVideo() {
diff --git a/webrtc/examples/androidapp/src/org/appspot/apprtc/SettingsActivity.java b/webrtc/examples/androidapp/src/org/appspot/apprtc/SettingsActivity.java
index 1a636ca..fc76837 100644
--- a/webrtc/examples/androidapp/src/org/appspot/apprtc/SettingsActivity.java
+++ b/webrtc/examples/androidapp/src/org/appspot/apprtc/SettingsActivity.java
@@ -40,6 +40,7 @@
 
   private String keyPrefRoomServerUrl;
   private String keyPrefDisplayHud;
+  private String keyPrefTracing;
 
   @Override
   protected void onCreate(Bundle savedInstanceState) {
@@ -62,6 +63,7 @@
 
     keyPrefRoomServerUrl = getString(R.string.pref_room_server_url_key);
     keyPrefDisplayHud = getString(R.string.pref_displayhud_key);
+    keyPrefTracing = getString(R.string.pref_tracing_key);
 
     // Display the fragment as the main content.
     settingsFragment = new SettingsFragment();
@@ -97,6 +99,7 @@
 
     updateSummary(sharedPreferences, keyPrefRoomServerUrl);
     updateSummaryB(sharedPreferences, keyPrefDisplayHud);
+    updateSummaryB(sharedPreferences, keyPrefTracing);
   }
 
   @Override
@@ -122,6 +125,7 @@
         || key.equals(keyprefStartAudioBitrateValue)) {
       updateSummaryBitrate(sharedPreferences, key);
     } else if (key.equals(keyprefVideoCall)
+        || key.equals(keyPrefTracing)
         || key.equals(keyprefCaptureQualitySlider)
         || key.equals(keyprefHwCodec)
         || key.equals(keyprefCaptureToTexture)
diff --git a/webrtc/examples/androidtests/src/org/appspot/apprtc/test/PeerConnectionClientTest.java b/webrtc/examples/androidtests/src/org/appspot/apprtc/test/PeerConnectionClientTest.java
index 5330bd2..59cf4ac 100644
--- a/webrtc/examples/androidtests/src/org/appspot/apprtc/test/PeerConnectionClientTest.java
+++ b/webrtc/examples/androidtests/src/org/appspot/apprtc/test/PeerConnectionClientTest.java
@@ -249,7 +249,7 @@
   private PeerConnectionParameters createParametersForAudioCall() {
     PeerConnectionParameters peerConnectionParameters =
         new PeerConnectionParameters(
-            false, true, // videoCallEnabled, loopback.
+            false, true, false, // videoCallEnabled, loopback, tracing.
             0, 0, 0, 0, "", true, false, // video codec parameters.
             0, "OPUS", false, false); // audio codec parameters.
     return peerConnectionParameters;
@@ -259,7 +259,7 @@
       String videoCodec, boolean captureToTexture) {
     PeerConnectionParameters peerConnectionParameters =
         new PeerConnectionParameters(
-            true, true, // videoCallEnabled, loopback.
+            true, true, false, // videoCallEnabled, loopback, tracing.
             0, 0, 0, 0, videoCodec, true, captureToTexture, // video codec parameters.
             0, "OPUS", false, false); // audio codec parameters.
     return peerConnectionParameters;