Add an API to unload a single snippet. (#463)

* Add an API to unload a single snippet.
diff --git a/mobly/controllers/android_device.py b/mobly/controllers/android_device.py
index 746f819..2ff854e 100644
--- a/mobly/controllers/android_device.py
+++ b/mobly/controllers/android_device.py
@@ -903,6 +903,22 @@
         self._snippet_clients[name] = client
         setattr(self, name, client)
 
+    def unload_snippet(self, name):
+        """Stops a snippet apk.
+
+        Args:
+            name: The attribute name the snippet server is attached with.
+
+        Raises:
+            SnippetError: The given snippet name is not registered.
+        """
+        if name not in self._snippet_clients:
+            raise SnippetError(self,
+                               'No snippet registered with name "%s"' % name)
+        client = self._snippet_clients.pop(name)
+        delattr(self, name)
+        client.stop_app()
+
     def load_sl4a(self):
         """Start sl4a service on the Android device.
 
diff --git a/tests/mobly/controllers/android_device_test.py b/tests/mobly/controllers/android_device_test.py
index f175f17..6949903 100755
--- a/tests/mobly/controllers/android_device_test.py
+++ b/tests/mobly/controllers/android_device_test.py
@@ -901,6 +901,29 @@
     @mock.patch(
         'mobly.controllers.android_device_lib.snippet_client.SnippetClient')
     @mock.patch('mobly.utils.get_available_host_port')
+    def test_AndroidDevice_unload_snippet(self, MockGetPort, MockSnippetClient,
+                                          MockFastboot, MockAdbProxy):
+        ad = android_device.AndroidDevice(serial='1')
+        ad.load_snippet('snippet', MOCK_SNIPPET_PACKAGE_NAME)
+        ad.unload_snippet('snippet')
+        self.assertFalse(hasattr(ad, 'snippet'))
+        with self.assertRaisesRegex(
+                android_device.SnippetError,
+                '<AndroidDevice|1> No snippet registered with name "snippet"'):
+            ad.unload_snippet('snippet')
+        # Loading the same snippet again should succeed
+        ad.load_snippet('snippet', MOCK_SNIPPET_PACKAGE_NAME)
+        self.assertTrue(hasattr(ad, 'snippet'))
+
+    @mock.patch(
+        'mobly.controllers.android_device_lib.adb.AdbProxy',
+        return_value=mock_android_device.MockAdbProxy('1'))
+    @mock.patch(
+        'mobly.controllers.android_device_lib.fastboot.FastbootProxy',
+        return_value=mock_android_device.MockFastbootProxy('1'))
+    @mock.patch(
+        'mobly.controllers.android_device_lib.snippet_client.SnippetClient')
+    @mock.patch('mobly.utils.get_available_host_port')
     def test_AndroidDevice_snippet_cleanup(
             self, MockGetPort, MockSnippetClient, MockFastboot, MockAdbProxy):
         ad = android_device.AndroidDevice(serial='1')