Add RtcEventLog to AppRTCMobile with preference setting.

Enable diagnostic packet and event recording as in the "webrtc-internal"
setting in Chromium.

Bug: webrtc:8859
Change-Id: I1d4a19e0dd60133cdd0d4e18a55780623b65653c
Reviewed-on: https://webrtc-review.googlesource.com/49541
Commit-Queue: Qingsi Wang <qingsi@google.com>
Reviewed-by: Sami Kalliomäki <sakal@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#21987}
diff --git a/examples/BUILD.gn b/examples/BUILD.gn
index a8abe97..f39e23a 100644
--- a/examples/BUILD.gn
+++ b/examples/BUILD.gn
@@ -83,6 +83,7 @@
       "androidapp/src/org/appspot/apprtc/HudFragment.java",
       "androidapp/src/org/appspot/apprtc/PeerConnectionClient.java",
       "androidapp/src/org/appspot/apprtc/RoomParametersFetcher.java",
+      "androidapp/src/org/appspot/apprtc/RtcEventLog.java",
       "androidapp/src/org/appspot/apprtc/SettingsActivity.java",
       "androidapp/src/org/appspot/apprtc/SettingsFragment.java",
       "androidapp/src/org/appspot/apprtc/TCPChannelClient.java",
diff --git a/examples/androidapp/res/values/strings.xml b/examples/androidapp/res/values/strings.xml
index 231475f..4aec2c1 100644
--- a/examples/androidapp/res/values/strings.xml
+++ b/examples/androidapp/res/values/strings.xml
@@ -213,4 +213,8 @@
     <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>
+
+    <string name="pref_enable_rtceventlog_key">enable_rtceventlog_key</string>
+    <string name="pref_enable_rtceventlog_title">Enable RtcEventLog.</string>
+    <string name="pref_enable_rtceventlog_default">false</string>
 </resources>
diff --git a/examples/androidapp/res/xml/preferences.xml b/examples/androidapp/res/xml/preferences.xml
index c9fe750..e8b9547 100644
--- a/examples/androidapp/res/xml/preferences.xml
+++ b/examples/androidapp/res/xml/preferences.xml
@@ -229,13 +229,18 @@
             android:key="@string/pref_displayhud_key"
             android:title="@string/pref_displayhud_title"
             android:dialogTitle="@string/pref_displayhud_dlg"
-           android:defaultValue="@string/pref_displayhud_default" />
+            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" />
+            android:defaultValue="@string/pref_tracing_default" />
+
+        <CheckBoxPreference
+            android:key="@string/pref_enable_rtceventlog_key"
+            android:title="@string/pref_enable_rtceventlog_title"
+            android:defaultValue="@string/pref_enable_rtceventlog_default"/>
     </PreferenceCategory>
 
 </PreferenceScreen>
diff --git a/examples/androidapp/src/org/appspot/apprtc/CallActivity.java b/examples/androidapp/src/org/appspot/apprtc/CallActivity.java
index 19ff9ef..70d0e40 100644
--- a/examples/androidapp/src/org/appspot/apprtc/CallActivity.java
+++ b/examples/androidapp/src/org/appspot/apprtc/CallActivity.java
@@ -117,6 +117,7 @@
   public static final String EXTRA_PROTOCOL = "org.appspot.apprtc.PROTOCOL";
   public static final String EXTRA_NEGOTIATED = "org.appspot.apprtc.NEGOTIATED";
   public static final String EXTRA_ID = "org.appspot.apprtc.ID";
+  public static final String EXTRA_ENABLE_RTCEVENTLOG = "org.appspot.apprtc.ENABLE_RTCEVENTLOG";
 
   private static final int CAPTURE_PERMISSION_REQUEST_CODE = 1;
 
@@ -242,7 +243,7 @@
     final Intent intent = getIntent();
 
     // Create peer connection client.
-    peerConnectionClient = new PeerConnectionClient();
+    peerConnectionClient = new PeerConnectionClient(getApplicationContext());
 
     // Create video renderers.
     pipRenderer.init(peerConnectionClient.getRenderContext(), null);
@@ -335,7 +336,8 @@
             intent.getBooleanExtra(EXTRA_DISABLE_BUILT_IN_AGC, false),
             intent.getBooleanExtra(EXTRA_DISABLE_BUILT_IN_NS, false),
             intent.getBooleanExtra(EXTRA_ENABLE_LEVEL_CONTROL, false),
-            intent.getBooleanExtra(EXTRA_DISABLE_WEBRTC_AGC_AND_HPF, false), dataChannelParameters);
+            intent.getBooleanExtra(EXTRA_DISABLE_WEBRTC_AGC_AND_HPF, false),
+            intent.getBooleanExtra(EXTRA_ENABLE_RTCEVENTLOG, false), dataChannelParameters);
     commandLineRun = intent.getBooleanExtra(EXTRA_CMDLINE, false);
     int runTimeMs = intent.getIntExtra(EXTRA_RUNTIME, 0);
 
@@ -384,8 +386,7 @@
       options.networkIgnoreMask = 0;
       peerConnectionClient.setPeerConnectionFactoryOptions(options);
     }
-    peerConnectionClient.createPeerConnectionFactory(
-        getApplicationContext(), peerConnectionParameters, CallActivity.this);
+    peerConnectionClient.createPeerConnectionFactory(peerConnectionParameters, CallActivity.this);
 
     if (screencaptureEnabled) {
       startScreenCapture();
diff --git a/examples/androidapp/src/org/appspot/apprtc/ConnectActivity.java b/examples/androidapp/src/org/appspot/apprtc/ConnectActivity.java
index 28add75..17b2f8da 100644
--- a/examples/androidapp/src/org/appspot/apprtc/ConnectActivity.java
+++ b/examples/androidapp/src/org/appspot/apprtc/ConnectActivity.java
@@ -430,6 +430,11 @@
     boolean tracing = sharedPrefGetBoolean(R.string.pref_tracing_key, CallActivity.EXTRA_TRACING,
         R.string.pref_tracing_default, useValuesFromIntent);
 
+    // Check Enable RtcEventLog.
+    boolean rtcEventLogEnabled = sharedPrefGetBoolean(R.string.pref_enable_rtceventlog_key,
+        CallActivity.EXTRA_ENABLE_RTCEVENTLOG, R.string.pref_enable_rtceventlog_default,
+        useValuesFromIntent);
+
     // Get datachannel options
     boolean dataChannelEnabled = sharedPrefGetBoolean(R.string.pref_enable_datachannel_key,
         CallActivity.EXTRA_DATA_CHANNEL_ENABLED, R.string.pref_enable_datachannel_default,
@@ -481,6 +486,7 @@
       intent.putExtra(CallActivity.EXTRA_AUDIOCODEC, audioCodec);
       intent.putExtra(CallActivity.EXTRA_DISPLAY_HUD, displayHud);
       intent.putExtra(CallActivity.EXTRA_TRACING, tracing);
+      intent.putExtra(CallActivity.EXTRA_ENABLE_RTCEVENTLOG, rtcEventLogEnabled);
       intent.putExtra(CallActivity.EXTRA_CMDLINE, commandLineRun);
       intent.putExtra(CallActivity.EXTRA_RUNTIME, runTimeMs);
 
diff --git a/examples/androidapp/src/org/appspot/apprtc/PeerConnectionClient.java b/examples/androidapp/src/org/appspot/apprtc/PeerConnectionClient.java
index 9ed48f5..961e2e8 100644
--- a/examples/androidapp/src/org/appspot/apprtc/PeerConnectionClient.java
+++ b/examples/androidapp/src/org/appspot/apprtc/PeerConnectionClient.java
@@ -13,16 +13,21 @@
 import android.content.Context;
 import android.os.Environment;
 import android.os.ParcelFileDescriptor;
+import android.preference.PreferenceManager;
 import android.util.Log;
 import java.io.File;
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.nio.charset.Charset;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.Date;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Locale;
 import java.util.Timer;
 import java.util.TimerTask;
 import java.util.concurrent.ExecutorService;
@@ -105,6 +110,7 @@
   private static final int HD_VIDEO_WIDTH = 1280;
   private static final int HD_VIDEO_HEIGHT = 720;
   private static final int BPS_IN_KBPS = 1000;
+  private static final String RTCEVENTLOG_OUTPUT_DIR_NAME = "rtc_event_log";
 
   // Executor thread is started once in private ctor and is used for all
   // peer connection API calls to ensure new peer connection factory is
@@ -115,6 +121,7 @@
   private final SDPObserver sdpObserver = new SDPObserver();
 
   private final EglBase rootEglBase;
+  private final Context appContext;
   private PeerConnectionFactory factory;
   private PeerConnection peerConnection;
   PeerConnectionFactory.Options options = null;
@@ -154,6 +161,8 @@
   private AudioTrack localAudioTrack;
   private DataChannel dataChannel;
   private boolean dataChannelEnabled;
+  // Enable RtcEventLog.
+  private RtcEventLog rtcEventLog;
 
   /**
    * Peer connection parameters.
@@ -201,6 +210,7 @@
     public final boolean disableBuiltInNS;
     public final boolean enableLevelControl;
     public final boolean disableWebRtcAGCAndHPF;
+    public final boolean enableRtcEventLog;
     private final DataChannelParameters dataChannelParameters;
 
     public PeerConnectionParameters(boolean videoCallEnabled, boolean loopback, boolean tracing,
@@ -208,11 +218,11 @@
         boolean videoCodecHwAcceleration, boolean videoFlexfecEnabled, int audioStartBitrate,
         String audioCodec, boolean noAudioProcessing, boolean aecDump, boolean useOpenSLES,
         boolean disableBuiltInAEC, boolean disableBuiltInAGC, boolean disableBuiltInNS,
-        boolean enableLevelControl, boolean disableWebRtcAGCAndHPF) {
+        boolean enableLevelControl, boolean disableWebRtcAGCAndHPF, boolean enableRtcEventLog) {
       this(videoCallEnabled, loopback, tracing, videoWidth, videoHeight, videoFps, videoMaxBitrate,
           videoCodec, videoCodecHwAcceleration, videoFlexfecEnabled, audioStartBitrate, audioCodec,
           noAudioProcessing, aecDump, useOpenSLES, disableBuiltInAEC, disableBuiltInAGC,
-          disableBuiltInNS, enableLevelControl, disableWebRtcAGCAndHPF, null);
+          disableBuiltInNS, enableLevelControl, disableWebRtcAGCAndHPF, enableRtcEventLog, null);
     }
 
     public PeerConnectionParameters(boolean videoCallEnabled, boolean loopback, boolean tracing,
@@ -220,7 +230,7 @@
         boolean videoCodecHwAcceleration, boolean videoFlexfecEnabled, int audioStartBitrate,
         String audioCodec, boolean noAudioProcessing, boolean aecDump, boolean useOpenSLES,
         boolean disableBuiltInAEC, boolean disableBuiltInAGC, boolean disableBuiltInNS,
-        boolean enableLevelControl, boolean disableWebRtcAGCAndHPF,
+        boolean enableLevelControl, boolean disableWebRtcAGCAndHPF, boolean enableRtcEventLog,
         DataChannelParameters dataChannelParameters) {
       this.videoCallEnabled = videoCallEnabled;
       this.loopback = loopback;
@@ -242,6 +252,7 @@
       this.disableBuiltInNS = disableBuiltInNS;
       this.enableLevelControl = enableLevelControl;
       this.disableWebRtcAGCAndHPF = disableWebRtcAGCAndHPF;
+      this.enableRtcEventLog = enableRtcEventLog;
       this.dataChannelParameters = dataChannelParameters;
     }
   }
@@ -293,15 +304,19 @@
     void onPeerConnectionError(final String description);
   }
 
-  public PeerConnectionClient() {
+  public PeerConnectionClient(Context appContext) {
+    if (appContext == null) {
+      throw new NullPointerException("The application context is null");
+    }
     rootEglBase = EglBase.create();
+    this.appContext = appContext;
   }
 
   public void setPeerConnectionFactoryOptions(PeerConnectionFactory.Options options) {
     this.options = options;
   }
 
-  public void createPeerConnectionFactory(final Context context,
+  public void createPeerConnectionFactory(
       final PeerConnectionParameters peerConnectionParameters, final PeerConnectionEvents events) {
     this.peerConnectionParameters = peerConnectionParameters;
     this.events = events;
@@ -328,7 +343,7 @@
     executor.execute(new Runnable() {
       @Override
       public void run() {
-        createPeerConnectionFactoryInternal(context);
+        createPeerConnectionFactoryInternal();
       }
     });
   }
@@ -357,6 +372,7 @@
         try {
           createMediaConstraintsInternal();
           createPeerConnectionInternal();
+          maybeCreateAndStartRtcEventLog();
         } catch (Exception e) {
           reportError("Failed to create peer connection: " + e.getMessage());
           throw e;
@@ -378,7 +394,7 @@
     return videoCallEnabled;
   }
 
-  private void createPeerConnectionFactoryInternal(Context context) {
+  private void createPeerConnectionFactoryInternal() {
     isError = false;
 
     // Initialize field trials.
@@ -422,7 +438,7 @@
         "Initialize WebRTC. Field trials: " + fieldTrials + " Enable video HW acceleration: "
             + peerConnectionParameters.videoCodecHwAcceleration);
     PeerConnectionFactory.initialize(
-        PeerConnectionFactory.InitializationOptions.builder(context)
+        PeerConnectionFactory.InitializationOptions.builder(appContext)
             .setFieldTrials(fieldTrials)
             .setEnableVideoHwAcceleration(peerConnectionParameters.videoCodecHwAcceleration)
             .setEnableInternalTracer(true)
@@ -664,6 +680,26 @@
     Log.d(TAG, "Peer connection created.");
   }
 
+  private File createRtcEventLogOutputFile() {
+    DateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_hhmm_ss", Locale.getDefault());
+    Date date = new Date();
+    final String outputFileName = "event_log_" + dateFormat.format(date) + ".log";
+    return new File(
+        appContext.getDir(RTCEVENTLOG_OUTPUT_DIR_NAME, Context.MODE_PRIVATE), outputFileName);
+  }
+
+  private void maybeCreateAndStartRtcEventLog() {
+    if (appContext == null || peerConnection == null) {
+      return;
+    }
+    if (!peerConnectionParameters.enableRtcEventLog) {
+      Log.d(TAG, "RtcEventLog is disabled.");
+      return;
+    }
+    rtcEventLog = new RtcEventLog(peerConnection);
+    rtcEventLog.start(createRtcEventLogOutputFile());
+  }
+
   private void closeInternal() {
     if (factory != null && peerConnectionParameters.aecDump) {
       factory.stopAecDump();
@@ -674,6 +710,11 @@
       dataChannel.dispose();
       dataChannel = null;
     }
+    if (rtcEventLog != null) {
+      // RtcEventLog should stop before the peer connection is disposed.
+      rtcEventLog.stop();
+      rtcEventLog = null;
+    }
     if (peerConnection != null) {
       peerConnection.dispose();
       peerConnection = null;
diff --git a/examples/androidapp/src/org/appspot/apprtc/RtcEventLog.java b/examples/androidapp/src/org/appspot/apprtc/RtcEventLog.java
new file mode 100644
index 0000000..e1cf8ce
--- /dev/null
+++ b/examples/androidapp/src/org/appspot/apprtc/RtcEventLog.java
@@ -0,0 +1,74 @@
+/*
+ *  Copyright 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+package org.appspot.apprtc;
+
+import android.content.Context;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+import java.io.File;
+import java.io.IOException;
+import org.webrtc.PeerConnection;
+
+public class RtcEventLog {
+  private static final String TAG = "RtcEventLog";
+  private static final int OUTPUT_FILE_MAX_BYTES = 10_000_000;
+  private final PeerConnection peerConnection;
+  private RtcEventLogState state = RtcEventLogState.INACTIVE;
+
+  enum RtcEventLogState {
+    INACTIVE,
+    STARTED,
+    STOPPED,
+  }
+
+  public RtcEventLog(PeerConnection peerConnection) {
+    if (peerConnection == null) {
+      throw new NullPointerException("The peer connection is null.");
+    }
+    this.peerConnection = peerConnection;
+  }
+
+  public void start(final File outputFile) {
+    if (state == RtcEventLogState.STARTED) {
+      Log.e(TAG, "RtcEventLog has already started.");
+      return;
+    }
+    final ParcelFileDescriptor fileDescriptor;
+    try {
+      fileDescriptor = ParcelFileDescriptor.open(outputFile,
+          ParcelFileDescriptor.MODE_READ_WRITE | ParcelFileDescriptor.MODE_CREATE
+              | ParcelFileDescriptor.MODE_TRUNCATE);
+    } catch (IOException e) {
+      Log.e(TAG, "Failed to create a new file", e);
+      return;
+    }
+
+    // Passes ownership of the file to WebRTC.
+    boolean success =
+        peerConnection.startRtcEventLog(fileDescriptor.detachFd(), OUTPUT_FILE_MAX_BYTES);
+    if (!success) {
+      Log.e(TAG, "Failed to start RTC event log.");
+      return;
+    }
+    state = RtcEventLogState.STARTED;
+    Log.d(TAG, "RtcEventLog started.");
+  }
+
+  public void stop() {
+    if (state != RtcEventLogState.STARTED) {
+      Log.e(TAG, "RtcEventLog was not started.");
+      return;
+    }
+    peerConnection.stopRtcEventLog();
+    state = RtcEventLogState.STOPPED;
+    Log.d(TAG, "RtcEventLog stopped.");
+  }
+}
\ No newline at end of file
diff --git a/examples/androidapp/src/org/appspot/apprtc/SettingsActivity.java b/examples/androidapp/src/org/appspot/apprtc/SettingsActivity.java
index 43b8a0a..152bb7d 100644
--- a/examples/androidapp/src/org/appspot/apprtc/SettingsActivity.java
+++ b/examples/androidapp/src/org/appspot/apprtc/SettingsActivity.java
@@ -53,6 +53,7 @@
   private String keyPrefRoomServerUrl;
   private String keyPrefDisplayHud;
   private String keyPrefTracing;
+  private String keyprefEnabledRtcEventLog;
 
   private String keyprefEnableDataChannel;
   private String keyprefOrdered;
@@ -102,6 +103,7 @@
     keyPrefRoomServerUrl = getString(R.string.pref_room_server_url_key);
     keyPrefDisplayHud = getString(R.string.pref_displayhud_key);
     keyPrefTracing = getString(R.string.pref_tracing_key);
+    keyprefEnabledRtcEventLog = getString(R.string.pref_enable_rtceventlog_key);
 
     // Display the fragment as the main content.
     settingsFragment = new SettingsFragment();
@@ -158,6 +160,7 @@
     updateSummary(sharedPreferences, keyPrefRoomServerUrl);
     updateSummaryB(sharedPreferences, keyPrefDisplayHud);
     updateSummaryB(sharedPreferences, keyPrefTracing);
+    updateSummaryB(sharedPreferences, keyprefEnabledRtcEventLog);
 
     if (!Camera2Enumerator.isSupported(this)) {
       Preference camera2Preference = settingsFragment.findPreference(keyprefCamera2);
@@ -241,7 +244,8 @@
         || key.equals(keyPrefDisplayHud)
         || key.equals(keyprefEnableDataChannel)
         || key.equals(keyprefOrdered)
-        || key.equals(keyprefNegotiated)) {
+        || key.equals(keyprefNegotiated)
+        || key.equals(keyprefEnabledRtcEventLog)) {
       updateSummaryB(sharedPreferences, key);
     } else if (key.equals(keyprefSpeakerphone)) {
       updateSummaryList(sharedPreferences, key);
diff --git a/examples/androidtests/src/org/appspot/apprtc/test/PeerConnectionClientTest.java b/examples/androidtests/src/org/appspot/apprtc/test/PeerConnectionClientTest.java
index 56eb3f7..c9c74b7 100644
--- a/examples/androidtests/src/org/appspot/apprtc/test/PeerConnectionClientTest.java
+++ b/examples/androidtests/src/org/appspot/apprtc/test/PeerConnectionClientTest.java
@@ -315,13 +315,13 @@
             null, null, null, // clientId, wssUrl, wssPostUrl.
             null, null); // offerSdp, iceCandidates.
 
-    PeerConnectionClient client = new PeerConnectionClient();
+    PeerConnectionClient client =
+        new PeerConnectionClient(InstrumentationRegistry.getTargetContext());
     PeerConnectionFactory.Options options = new PeerConnectionFactory.Options();
     options.networkIgnoreMask = 0;
     options.disableNetworkMonitor = true;
     client.setPeerConnectionFactoryOptions(options);
-    client.createPeerConnectionFactory(
-        InstrumentationRegistry.getTargetContext(), peerConnectionParameters, this);
+    client.createPeerConnectionFactory(peerConnectionParameters, this);
     client.createPeerConnection(localRenderer, remoteRenderer, videoCapturer, signalingParameters);
     client.createOffer();
     return client;
@@ -345,7 +345,8 @@
         false, /* noAudioProcessing */
         false, /* aecDump */
         false /* useOpenSLES */, false /* disableBuiltInAEC */, false /* disableBuiltInAGC */,
-        false /* disableBuiltInNS */, false /* enableLevelControl */, false /* disableWebRtcAGC */);
+        false /* disableBuiltInNS */, false /* enableLevelControl */, false /* disableWebRtcAGC */,
+        false /* enableRtcEventLog */);
   }
 
   private VideoCapturer createCameraCapturer(boolean captureToTexture) {
@@ -380,7 +381,8 @@
         false, /* noAudioProcessing */
         false, /* aecDump */
         false /* useOpenSLES */, false /* disableBuiltInAEC */, false /* disableBuiltInAGC */,
-        false /* disableBuiltInNS */, false /* enableLevelControl */, false /* disableWebRtcAGC */);
+        false /* disableBuiltInNS */, false /* enableLevelControl */, false /* disableWebRtcAGC */,
+        false /* enableRtcEventLog */);
   }
 
   @Before