Fix help() rpc to display new lines properly in snippet shell. (#546)

diff --git a/mobly/controllers/android_device_lib/snippet_client.py b/mobly/controllers/android_device_lib/snippet_client.py
index a8bffe8..c33f373 100644
--- a/mobly/controllers/android_device_lib/snippet_client.py
+++ b/mobly/controllers/android_device_lib/snippet_client.py
@@ -13,6 +13,8 @@
 # limitations under the License.
 """JSON RPC interface to Mobly Snippet Lib."""
 
+from __future__ import print_function
+
 import re
 import time
 
@@ -178,8 +180,7 @@
 
         # Yaaay! We're done!
         self.log.debug('Snippet %s started after %.1fs on host port %s',
-                       self.package,
-                       time.time() - start_time, self.host_port)
+                       self.package, time.time() - start_time, self.host_port)
 
     def restore_app_connection(self, port=None):
         """Restores the app after device got reconnected.
@@ -336,3 +337,23 @@
             'at the same time performs USB disconnection may fail',
             _SETSID_COMMAND, _NOHUP_COMMAND)
         return ''
+
+    def help(self, print_output=True):
+        """Calls the help RPC, which returns the list of RPC calls available.
+
+        This RPC should normally be used in an interactive console environment
+        where the output should be printed instead of returned. Otherwise,
+        newlines will be escaped, which will make the output difficult to read.
+
+        Args:
+            print_output: A bool for whether the output should be printed.
+
+        Returns:
+            A str containing the help output otherwise None if print_output
+                wasn't set.
+        """
+        help_text = self._rpc('help')
+        if print_output:
+            print(help_text)
+        else:
+            return help_text
diff --git a/tests/mobly/controllers/android_device_lib/snippet_client_test.py b/tests/mobly/controllers/android_device_lib/snippet_client_test.py
index b0f28c7..5968411 100755
--- a/tests/mobly/controllers/android_device_lib/snippet_client_test.py
+++ b/tests/mobly/controllers/android_device_lib/snippet_client_test.py
@@ -16,6 +16,7 @@
 from builtins import bytes
 
 import mock
+import sys
 from future.tests.base import unittest
 
 from mobly.controllers.android_device_lib import adb
@@ -28,6 +29,18 @@
 JSONRPC_BASE_CLASS = 'mobly.controllers.android_device_lib.jsonrpc_client_base.JsonRpcClientBase'
 
 
+def get_print_function_name():
+    """Gets the name of the print function for mocking.
+
+    Returns:
+        A str representing the print function to mock.
+    """
+    if sys.version_info >= (3, 0):
+        return 'builtins.print'
+    else:
+        return '__builtin__.print'
+
+
 class MockAdbProxy(object):
     def __init__(self, **kwargs):
         self.apk_not_installed = kwargs.get('apk_not_installed', False)
@@ -449,6 +462,28 @@
                                     'Unexpected EOF waiting for app to start'):
             client.start_app_and_connect()
 
+    @mock.patch(get_print_function_name())
+    def test_help_rpc_when_printing_by_default(self, mock_print):
+        client = self._make_client()
+        mock_rpc = mock.MagicMock()
+        client._rpc = mock_rpc
+
+        result = client.help()
+        mock_rpc.assert_called_once_with('help')
+        self.assertEqual(None, result)
+        mock_print.assert_called_once_with(mock_rpc.return_value)
+
+    @mock.patch(get_print_function_name())
+    def test_help_rpc_when_not_printing(self, mock_print):
+        client = self._make_client()
+        mock_rpc = mock.MagicMock()
+        client._rpc = mock_rpc
+
+        result = client.help(print_output=False)
+        mock_rpc.assert_called_once_with('help')
+        self.assertEqual(mock_rpc.return_value, result)
+        mock_print.assert_not_called()
+
     def _make_client(self, adb_proxy=None):
         adb_proxy = adb_proxy or MockAdbProxy()
         ad = mock.Mock()