diff --git a/mobly/controllers/android_device_lib/adb.py b/mobly/controllers/android_device_lib/adb.py
index 12c14bd..cb5b6b6 100644
--- a/mobly/controllers/android_device_lib/adb.py
+++ b/mobly/controllers/android_device_lib/adb.py
@@ -237,7 +237,8 @@ class AdbProxy(object):
 
     def forward(self, args=None, shell=False):
         with ADB_PORT_LOCK:
-            return self._exec_adb_cmd('forward', args, shell, timeout=None)
+            return self._exec_adb_cmd(
+                'forward', args, shell, timeout=None, stderr=None)
 
     def instrument(self, package, options=None, runner=None):
         """Runs an instrumentation command on the device.

diff --git a/mobly/base_test.py b/mobly/base_test.py
index e4e047b..13a79b0 100644
--- a/mobly/base_test.py
+++ b/mobly/base_test.py
@@ -624,6 +624,10 @@ class BaseTestClass(object):
         tests = self._get_test_methods(test_names)
         try:
             # Setup for the class.
+            class_record = records.TestResultRecord('setup_class', self.TAG)
+            class_record.test_begin()
+            self.current_test_info = runtime_test_info.RuntimeTestInfo(
+                'setup_class', self.log_path, class_record)
             try:
                 self._setup_class()
             except signals.TestAbortSignal:
@@ -633,9 +637,6 @@ class BaseTestClass(object):
                 # Setup class failed for unknown reasons.
                 # Fail the class and skip all tests.
                 logging.exception('Error in setup_class %s.', self.TAG)
-                class_record = records.TestResultRecord(
-                    'setup_class', self.TAG)
-                class_record.test_begin()
                 class_record.test_error(e)
                 self._exec_procedure_func(self._on_fail, class_record)
                 self.results.add_class_error(class_record)
diff --git a/mobly/runtime_test_info.py b/mobly/runtime_test_info.py
index f4eea99..57b0742 100644
--- a/mobly/runtime_test_info.py
+++ b/mobly/runtime_test_info.py
@@ -19,10 +19,13 @@ from mobly import utils
 
 
 class RuntimeTestInfo(object):
-    """Container class for runtime information of a test.
+    """Container class for runtime information of a test or test stage.
 
     One object corresponds to one test. This is meant to be a read-only class.
 
+    This also applies to test stages like `setup_class`, which has its own
+    runtime info but is not part of any single test.
+
     Attributes:
         name: string, name of the test.
         signature: string, an identifier of the test, a combination of test
diff --git a/tests/mobly/base_test_test.py b/tests/mobly/base_test_test.py
index d78a640..a38b532 100755
--- a/tests/mobly/base_test_test.py
+++ b/tests/mobly/base_test_test.py
@@ -91,6 +91,25 @@ class BaseTestTest(unittest.TestCase):
         self.assertIsNone(actual_record.details)
         self.assertIsNone(actual_record.extras)
 
+    def test_current_test_info_in_setup_class(self):
+        class MockBaseTest(base_test.BaseTestClass):
+            def setup_class(self):
+                asserts.assert_true(
+                    self.current_test_info.name == 'setup_class',
+                    'Got unexpected test name %s.' %
+                    self.current_test_info.name)
+                output_path = self.current_test_info.output_path
+                asserts.assert_true(
+                    os.path.exists(output_path), 'test output path missing')
+                raise Exception(MSG_EXPECTED_EXCEPTION)
+
+        bt_cls = MockBaseTest(self.mock_test_cls_configs)
+        bt_cls.run()
+        actual_record = bt_cls.results.error[0]
+        self.assertEqual(actual_record.test_name, 'setup_class')
+        self.assertEqual(actual_record.details, MSG_EXPECTED_EXCEPTION)
+        self.assertIsNone(actual_record.extras)
+
     def test_self_tests_list(self):
         class MockBaseTest(base_test.BaseTestClass):
             def __init__(self, controllers):
diff --git a/tests/mobly/controllers/android_device_lib/adb_test.py b/tests/mobly/controllers/android_device_lib/adb_test.py
index 7bf61ab..cf699ce 100755
--- a/tests/mobly/controllers/android_device_lib/adb_test.py
+++ b/tests/mobly/controllers/android_device_lib/adb_test.py
@@ -173,6 +173,10 @@ class AdbTest(unittest.TestCase):
         self.assertEqual(MOCK_DEFAULT_STDERR,
                          stderr_redirect.getvalue().decode('utf-8'))
 
+    def test_forward(self):
+        with mock.patch.object(adb.AdbProxy, '_exec_cmd') as mock_exec_cmd:
+            adb.AdbProxy().forward(MOCK_SHELL_COMMAND)
+
     def test_instrument_without_parameters(self):
         """Verifies the AndroidDevice object's instrument command is correct in
         the basic case.

`current_test_info` should exist between `setup_class` and `setup_test`
Right now `current_test_info` is None between `setup_class` and `setup_test`, which makes it difficult to use this field consistently.

E.g. if a test relies on this field in `on_fail`, if `setup_class` fails, the logic in `on_fail` would raise an exception for any call to `current_test_info`.
tests/mobly/controllers/android_device_lib/adb_test.py::AdbTest::test_forward
tests/mobly/base_test_test.py::BaseTestTest::test_abort_all_in_on_fail tests/mobly/base_test_test.py::BaseTestTest::test_abort_all_in_on_fail_from_setup_class tests/mobly/base_test_test.py::BaseTestTest::test_abort_all_in_setup_test tests/mobly/base_test_test.py::BaseTestTest::test_abort_all_in_test tests/mobly/base_test_test.py::BaseTestTest::test_abort_all_setup_class tests/mobly/base_test_test.py::BaseTestTest::test_abort_class_in_on_fail tests/mobly/base_test_test.py::BaseTestTest::test_abort_class_in_setup_test tests/mobly/base_test_test.py::BaseTestTest::test_abort_class_in_test tests/mobly/base_test_test.py::BaseTestTest::test_abort_class_setup_class tests/mobly/base_test_test.py::BaseTestTest::test_assert_equal_fail tests/mobly/base_test_test.py::BaseTestTest::test_assert_equal_fail_with_msg tests/mobly/base_test_test.py::BaseTestTest::test_assert_equal_pass tests/mobly/base_test_test.py::BaseTestTest::test_assert_raises_fail_with_noop tests/mobly/base_test_test.py::BaseTestTest::test_assert_raises_fail_with_wrong_error tests/mobly/base_test_test.py::BaseTestTest::test_assert_raises_fail_with_wrong_regex tests/mobly/base_test_test.py::BaseTestTest::test_assert_raises_pass tests/mobly/base_test_test.py::BaseTestTest::test_assert_raises_regex_fail_with_noop tests/mobly/base_test_test.py::BaseTestTest::test_assert_raises_regex_fail_with_wrong_error tests/mobly/base_test_test.py::BaseTestTest::test_assert_raises_regex_pass tests/mobly/base_test_test.py::BaseTestTest::test_assert_true tests/mobly/base_test_test.py::BaseTestTest::test_both_teardown_and_test_body_raise_exceptions tests/mobly/base_test_test.py::BaseTestTest::test_cli_test_selection_fail_by_convention tests/mobly/base_test_test.py::BaseTestTest::test_cli_test_selection_override_self_tests_list tests/mobly/base_test_test.py::BaseTestTest::test_current_test_info tests/mobly/base_test_test.py::BaseTestTest::test_current_test_info_in_setup_class tests/mobly/base_test_test.py::BaseTestTest::test_current_test_name tests/mobly/base_test_test.py::BaseTestTest::test_default_execution_of_all_tests tests/mobly/base_test_test.py::BaseTestTest::test_exception_objects_in_record tests/mobly/base_test_test.py::BaseTestTest::test_expect_equal tests/mobly/base_test_test.py::BaseTestTest::test_expect_false tests/mobly/base_test_test.py::BaseTestTest::test_expect_in_teardown_test tests/mobly/base_test_test.py::BaseTestTest::test_expect_multiple_fails tests/mobly/base_test_test.py::BaseTestTest::test_expect_no_op tests/mobly/base_test_test.py::BaseTestTest::test_expect_no_raises_custom_msg tests/mobly/base_test_test.py::BaseTestTest::test_expect_no_raises_default_msg tests/mobly/base_test_test.py::BaseTestTest::test_expect_true tests/mobly/base_test_test.py::BaseTestTest::test_expect_true_and_assert_true tests/mobly/base_test_test.py::BaseTestTest::test_expect_two_tests tests/mobly/base_test_test.py::BaseTestTest::test_explicit_pass tests/mobly/base_test_test.py::BaseTestTest::test_explicit_pass_but_teardown_test_raises_an_exception tests/mobly/base_test_test.py::BaseTestTest::test_fail tests/mobly/base_test_test.py::BaseTestTest::test_failure_in_procedure_functions_is_recorded tests/mobly/base_test_test.py::BaseTestTest::test_failure_to_call_procedure_function_is_recorded tests/mobly/base_test_test.py::BaseTestTest::test_generate_tests_call_outside_of_setup_generated_tests tests/mobly/base_test_test.py::BaseTestTest::test_generate_tests_dup_test_name tests/mobly/base_test_test.py::BaseTestTest::test_generate_tests_run tests/mobly/base_test_test.py::BaseTestTest::test_generate_tests_selected_run tests/mobly/base_test_test.py::BaseTestTest::test_implicit_pass tests/mobly/base_test_test.py::BaseTestTest::test_missing_requested_test_func tests/mobly/base_test_test.py::BaseTestTest::test_on_fail_cannot_modify_original_record tests/mobly/base_test_test.py::BaseTestTest::test_on_fail_executed_if_both_test_and_teardown_test_fails tests/mobly/base_test_test.py::BaseTestTest::test_on_fail_executed_if_teardown_test_fails tests/mobly/base_test_test.py::BaseTestTest::test_on_fail_executed_if_test_fails tests/mobly/base_test_test.py::BaseTestTest::test_on_fail_executed_if_test_setup_fails_by_exception tests/mobly/base_test_test.py::BaseTestTest::test_on_fail_raise_exception tests/mobly/base_test_test.py::BaseTestTest::test_on_pass_cannot_modify_original_record tests/mobly/base_test_test.py::BaseTestTest::test_on_pass_raise_exception tests/mobly/base_test_test.py::BaseTestTest::test_procedure_function_gets_correct_record tests/mobly/base_test_test.py::BaseTestTest::test_promote_extra_errors_to_termination_signal tests/mobly/base_test_test.py::BaseTestTest::test_self_tests_list tests/mobly/base_test_test.py::BaseTestTest::test_self_tests_list_fail_by_convention tests/mobly/base_test_test.py::BaseTestTest::test_setup_and_teardown_execution_count tests/mobly/base_test_test.py::BaseTestTest::test_setup_class_fail_by_exception tests/mobly/base_test_test.py::BaseTestTest::test_setup_test_fail_by_exception tests/mobly/base_test_test.py::BaseTestTest::test_setup_test_fail_by_test_signal tests/mobly/base_test_test.py::BaseTestTest::test_skip tests/mobly/base_test_test.py::BaseTestTest::test_skip_if tests/mobly/base_test_test.py::BaseTestTest::test_skip_in_setup_test tests/mobly/base_test_test.py::BaseTestTest::test_teardown_class_fail_by_exception tests/mobly/base_test_test.py::BaseTestTest::test_teardown_test_assert_fail tests/mobly/base_test_test.py::BaseTestTest::test_teardown_test_executed_if_setup_test_fails tests/mobly/base_test_test.py::BaseTestTest::test_teardown_test_executed_if_test_fails tests/mobly/base_test_test.py::BaseTestTest::test_teardown_test_executed_if_test_pass tests/mobly/base_test_test.py::BaseTestTest::test_teardown_test_raise_exception tests/mobly/base_test_test.py::BaseTestTest::test_uncaught_exception tests/mobly/base_test_test.py::BaseTestTest::test_unpack_userparams_basic tests/mobly/base_test_test.py::BaseTestTest::test_unpack_userparams_default_None tests/mobly/base_test_test.py::BaseTestTest::test_unpack_userparams_default_overwrite tests/mobly/base_test_test.py::BaseTestTest::test_unpack_userparams_default_overwrite_by_optional_param_list tests/mobly/base_test_test.py::BaseTestTest::test_unpack_userparams_default_overwrite_by_required_param_list tests/mobly/base_test_test.py::BaseTestTest::test_unpack_userparams_optional tests/mobly/base_test_test.py::BaseTestTest::test_unpack_userparams_optional_missing tests/mobly/base_test_test.py::BaseTestTest::test_unpack_userparams_optional_with_default tests/mobly/base_test_test.py::BaseTestTest::test_unpack_userparams_required tests/mobly/base_test_test.py::BaseTestTest::test_unpack_userparams_required_missing tests/mobly/controllers/android_device_lib/adb_test.py::AdbTest::test_cli_cmd_to_string tests/mobly/controllers/android_device_lib/adb_test.py::AdbTest::test_exec_adb_cmd tests/mobly/controllers/android_device_lib/adb_test.py::AdbTest::test_exec_adb_cmd_with_serial tests/mobly/controllers/android_device_lib/adb_test.py::AdbTest::test_exec_adb_cmd_with_shell_true tests/mobly/controllers/android_device_lib/adb_test.py::AdbTest::test_exec_adb_cmd_with_shell_true_with_serial tests/mobly/controllers/android_device_lib/adb_test.py::AdbTest::test_exec_adb_cmd_with_stderr_pipe tests/mobly/controllers/android_device_lib/adb_test.py::AdbTest::test_exec_cmd_error_no_timeout tests/mobly/controllers/android_device_lib/adb_test.py::AdbTest::test_exec_cmd_no_timeout_success tests/mobly/controllers/android_device_lib/adb_test.py::AdbTest::test_exec_cmd_timed_out tests/mobly/controllers/android_device_lib/adb_test.py::AdbTest::test_exec_cmd_with_negative_timeout_value tests/mobly/controllers/android_device_lib/adb_test.py::AdbTest::test_exec_cmd_with_timeout_success tests/mobly/controllers/android_device_lib/adb_test.py::AdbTest::test_has_shell_command_called_correctly tests/mobly/controllers/android_device_lib/adb_test.py::AdbTest::test_has_shell_command_with_existing_command tests/mobly/controllers/android_device_lib/adb_test.py::AdbTest::test_has_shell_command_with_missing_command_on_newer_devices tests/mobly/controllers/android_device_lib/adb_test.py::AdbTest::test_has_shell_command_with_missing_command_on_older_devices tests/mobly/controllers/android_device_lib/adb_test.py::AdbTest::test_instrument_with_options tests/mobly/controllers/android_device_lib/adb_test.py::AdbTest::test_instrument_with_runner tests/mobly/controllers/android_device_lib/adb_test.py::AdbTest::test_instrument_without_parameters