Merge "Notify client via callback when service is connected."
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index ca726d6..fdef066 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -10045,6 +10045,9 @@
   public interface SharedConnectivityClientCallback {
     method public void onKnownNetworkConnectionStatusChanged(@NonNull android.net.wifi.sharedconnectivity.app.KnownNetworkConnectionStatus);
     method public void onKnownNetworksUpdated(@NonNull java.util.List<android.net.wifi.sharedconnectivity.app.KnownNetwork>);
+    method public void onRegisterCallbackFailed(@NonNull Exception);
+    method public void onServiceConnected();
+    method public void onServiceDisconnected();
     method public void onSharedConnectivitySettingsChanged(@NonNull android.net.wifi.sharedconnectivity.app.SharedConnectivitySettingsState);
     method public void onTetherNetworkConnectionStatusChanged(@NonNull android.net.wifi.sharedconnectivity.app.TetherNetworkConnectionStatus);
     method public void onTetherNetworksUpdated(@NonNull java.util.List<android.net.wifi.sharedconnectivity.app.TetherNetwork>);
@@ -10055,7 +10058,7 @@
     method public boolean connectTetherNetwork(@NonNull android.net.wifi.sharedconnectivity.app.TetherNetwork);
     method public boolean disconnectTetherNetwork();
     method public boolean forgetKnownNetwork(@NonNull android.net.wifi.sharedconnectivity.app.KnownNetwork);
-    method public boolean registerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.sharedconnectivity.app.SharedConnectivityClientCallback);
+    method public void registerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.sharedconnectivity.app.SharedConnectivityClientCallback);
     method public boolean unregisterCallback(@NonNull android.net.wifi.sharedconnectivity.app.SharedConnectivityClientCallback);
   }
 
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 28febbd..164c640 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1949,6 +1949,7 @@
 package android.net.wifi.sharedconnectivity.app {
 
   public class SharedConnectivityManager {
+    method @Nullable public android.content.ServiceConnection getServiceConnection();
     method public void setService(@Nullable android.os.IInterface);
   }
 
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityClientCallback.java b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityClientCallback.java
index f62bfd4..d2b9be7 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityClientCallback.java
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityClientCallback.java
@@ -64,5 +64,24 @@
      * @param status The new status.
      */
     void onKnownNetworkConnectionStatusChanged(@NonNull KnownNetworkConnectionStatus status);
+
+    /**
+     * This method is being called when the service is ready to be used.
+     */
+    void onServiceConnected();
+
+    /**
+     * This method is being called when the service is no longer available.
+     */
+    void onServiceDisconnected();
+
+    /**
+     * This method is called when the registration of the callback with the shared connectivity
+     * service failed.
+     *
+     * @param exception The exception received from the system when trying to connect to the
+     *                  service.
+     */
+    void onRegisterCallbackFailed(@NonNull Exception exception);
 }
 
diff --git a/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
index 8aa369e..74b42d4 100644
--- a/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
+++ b/wifi/java/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManager.java
@@ -47,6 +47,12 @@
  * This class is the library used by consumers of Shared Connectivity data to bind to the service,
  * receive callbacks from, and send user actions to the service.
  *
+ * The methods {@link #connectTetherNetwork}, {@link #disconnectTetherNetwork},
+ * {@link #connectKnownNetwork} and {@link #forgetKnownNetwork} are not valid and will return false
+ * if not called between {@link SharedConnectivityClientCallback#onServiceConnected()}
+ * and {@link SharedConnectivityClientCallback#onServiceDisconnected()} or if
+ * {@link SharedConnectivityClientCallback#onRegisterCallbackFailed} was called.
+ *
  * @hide
  */
 @SystemApi
@@ -135,6 +141,10 @@
     private ISharedConnectivityService mService;
     private final Map<SharedConnectivityClientCallback, SharedConnectivityCallbackProxy>
             mProxyMap = new HashMap<>();
+    private final Map<SharedConnectivityClientCallback, SharedConnectivityCallbackProxy>
+            mCallbackProxyCache = new HashMap<>();
+    // Used for testing
+    private final ServiceConnection mServiceConnection;
 
     /**
      * Creates a new instance of {@link SharedConnectivityManager}.
@@ -164,23 +174,58 @@
 
     private SharedConnectivityManager(@NonNull Context context, String servicePackageName,
             String serviceIntentAction) {
-        ServiceConnection serviceConnection = new ServiceConnection() {
+        mServiceConnection = new ServiceConnection() {
             @Override
             public void onServiceConnected(ComponentName name, IBinder service) {
                 mService = ISharedConnectivityService.Stub.asInterface(service);
+                if (!mCallbackProxyCache.isEmpty()) {
+                    synchronized (mCallbackProxyCache) {
+                        mCallbackProxyCache.keySet().forEach(callback -> {
+                            registerCallbackInternal(callback, mCallbackProxyCache.get(callback));
+                        });
+                        mCallbackProxyCache.clear();
+                    }
+                }
             }
 
             @Override
             public void onServiceDisconnected(ComponentName name) {
                 if (DEBUG) Log.i(TAG, "onServiceDisconnected");
                 mService = null;
-                mProxyMap.clear();
+                if (!mCallbackProxyCache.isEmpty()) {
+                    synchronized (mCallbackProxyCache) {
+                        mCallbackProxyCache.keySet().forEach(
+                                SharedConnectivityClientCallback::onServiceDisconnected);
+                        mCallbackProxyCache.clear();
+                    }
+                }
+                if (!mProxyMap.isEmpty()) {
+                    synchronized (mProxyMap) {
+                        mProxyMap.keySet().forEach(
+                                SharedConnectivityClientCallback::onServiceDisconnected);
+                        mProxyMap.clear();
+                    }
+                }
             }
         };
 
         context.bindService(
                 new Intent().setPackage(servicePackageName).setAction(serviceIntentAction),
-                serviceConnection, Context.BIND_AUTO_CREATE);
+                mServiceConnection, Context.BIND_AUTO_CREATE);
+    }
+
+    private void registerCallbackInternal(SharedConnectivityClientCallback callback,
+            SharedConnectivityCallbackProxy proxy) {
+        try {
+            mService.registerCallback(proxy);
+            synchronized (mProxyMap) {
+                mProxyMap.put(callback, proxy);
+            }
+            callback.onServiceConnected();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Exception in registerCallback", e);
+            callback.onRegisterCallbackFailed(e);
+        }
     }
 
     /**
@@ -192,29 +237,45 @@
     }
 
     /**
+     * @hide
+     */
+    @TestApi
+    @Nullable
+    public ServiceConnection getServiceConnection() {
+        return mServiceConnection;
+    }
+
+    /**
      * Registers a callback for receiving updates to the list of Tether Networks and Known Networks.
+     * The {@link SharedConnectivityClientCallback#onRegisterCallbackFailed} will be called if the
+     * registration failed.
      *
      * @param executor The Executor used to invoke the callback.
      * @param callback The callback of type {@link SharedConnectivityClientCallback} that is invoked
      *                 when the service updates either the list of Tether Networks or Known
      *                 Networks.
-     * @return Returns true if the registration was successful, false otherwise.
      */
-    public boolean registerCallback(@NonNull @CallbackExecutor Executor executor,
+    public void registerCallback(@NonNull @CallbackExecutor Executor executor,
             @NonNull SharedConnectivityClientCallback callback) {
         Objects.requireNonNull(executor, "executor cannot be null");
         Objects.requireNonNull(callback, "callback cannot be null");
-        if (mService == null || mProxyMap.containsKey(callback)) return false;
-        try {
-            SharedConnectivityCallbackProxy proxy =
-                    new SharedConnectivityCallbackProxy(executor, callback);
-            mService.registerCallback(proxy);
-            mProxyMap.put(callback, proxy);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Exception in registerCallback", e);
-            return false;
+
+        if (mProxyMap.containsKey(callback) || mCallbackProxyCache.containsKey(callback)) {
+            Log.e(TAG, "Callback already registered");
+            callback.onRegisterCallbackFailed(new IllegalStateException(
+                    "Callback already registered"));
+            return;
         }
-        return true;
+
+        SharedConnectivityCallbackProxy proxy =
+                new SharedConnectivityCallbackProxy(executor, callback);
+        if (mService == null) {
+            synchronized (mCallbackProxyCache) {
+                mCallbackProxyCache.put(callback, proxy);
+            }
+            return;
+        }
+        registerCallbackInternal(callback, proxy);
     }
 
     /**
@@ -225,10 +286,24 @@
     public boolean unregisterCallback(
             @NonNull SharedConnectivityClientCallback callback) {
         Objects.requireNonNull(callback, "callback cannot be null");
-        if (mService == null || !mProxyMap.containsKey(callback)) return false;
+
+        if (!mProxyMap.containsKey(callback) && !mCallbackProxyCache.containsKey(callback)) {
+            Log.e(TAG, "Callback not found, cannot unregister");
+            return false;
+        }
+
+        if (mService == null) {
+            synchronized (mCallbackProxyCache) {
+                mCallbackProxyCache.remove(callback);
+            }
+            return true;
+        }
+
         try {
             mService.unregisterCallback(mProxyMap.get(callback));
-            mProxyMap.remove(callback);
+            synchronized (mProxyMap) {
+                mProxyMap.remove(callback);
+            }
         } catch (RemoteException e) {
             Log.e(TAG, "Exception in unregisterCallback", e);
             return false;
@@ -246,7 +321,12 @@
      *         connection was successful.
      */
     public boolean connectTetherNetwork(@NonNull TetherNetwork network) {
-        if (mService == null) return false;
+        Objects.requireNonNull(network, "Tether network cannot be null");
+
+        if (mService == null) {
+            return false;
+        }
+
         try {
             mService.connectTetherNetwork(network);
         } catch (RemoteException e) {
@@ -264,7 +344,10 @@
      *         disconnection was successful.
      */
     public boolean disconnectTetherNetwork() {
-        if (mService == null) return false;
+        if (mService == null) {
+            return false;
+        }
+
         try {
             mService.disconnectTetherNetwork();
         } catch (RemoteException e) {
@@ -284,7 +367,12 @@
      *         connection was successful.
      */
     public boolean connectKnownNetwork(@NonNull KnownNetwork network) {
-        if (mService == null) return false;
+        Objects.requireNonNull(network, "Known network cannot be null");
+
+        if (mService == null) {
+            return false;
+        }
+
         try {
             mService.connectKnownNetwork(network);
         } catch (RemoteException e) {
@@ -302,7 +390,12 @@
      *         forget action was successful.
      */
     public boolean forgetKnownNetwork(@NonNull KnownNetwork network) {
-        if (mService == null) return false;
+        Objects.requireNonNull(network, "Known network cannot be null");
+
+        if (mService == null) {
+            return false;
+        }
+
         try {
             mService.forgetKnownNetwork(network);
         } catch (RemoteException e) {
diff --git a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManagerTest.java b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManagerTest.java
index 784e9c4..815a012 100644
--- a/wifi/tests/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/sharedconnectivity/app/SharedConnectivityManagerTest.java
@@ -23,17 +23,20 @@
 import static android.net.wifi.sharedconnectivity.app.TetherNetwork.NETWORK_TYPE_CELLULAR;
 
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.res.Resources;
 import android.net.wifi.sharedconnectivity.service.ISharedConnectivityService;
+import android.os.Parcel;
 import android.os.RemoteException;
 
 import androidx.test.filters.SmallTest;
@@ -63,12 +66,9 @@
     private static final String SSID = "TEST_SSID";
     private static final int[] SECURITY_TYPES = {SECURITY_TYPE_WEP};
 
-    private static final int SERVICE_PACKAGE_ID = 1;
-    private static final int SERVICE_CLASS_ID = 2;
-
     private static final String SERVICE_PACKAGE_NAME = "TEST_PACKAGE";
-    private static final String SERVICE_CLASS_NAME = "TEST_CLASS";
-    private static final String PACKAGE_NAME = "TEST_PACKAGE";
+    private static final String SERVICE_INTENT_ACTION = "TEST_INTENT_ACTION";
+
 
     @Mock Context mContext;
     @Mock
@@ -77,6 +77,11 @@
     @Mock
     SharedConnectivityClientCallback mClientCallback;
     @Mock Resources mResources;
+    @Mock
+    ISharedConnectivityService.Stub mIBinder;
+
+    private static final ComponentName COMPONENT_NAME =
+            new ComponentName("dummypkg", "dummycls");
 
     @Before
     public void setUp() {
@@ -88,155 +93,281 @@
      * Verifies constructor is binding to service.
      */
     @Test
-    public void testBindingToService() {
+    public void bindingToService() {
         SharedConnectivityManager.create(mContext);
         verify(mContext).bindService(any(), any(), anyInt());
     }
 
     /**
-     * Verifies callback is registered in the service only once and only when service is not null.
+     * Verifies create method returns null when resources are not specified
      */
     @Test
-    public void testRegisterCallback() throws Exception {
-        SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
-        manager.setService(null);
-        assertFalse(manager.registerCallback(mExecutor, mClientCallback));
-
-        manager = SharedConnectivityManager.create(mContext);
-        manager.setService(mService);
-        assertTrue(manager.registerCallback(mExecutor, mClientCallback));
-        verify(mService).registerCallback(any());
-
-        // Registering the same callback twice should fail.
-        manager = SharedConnectivityManager.create(mContext);
-        manager.setService(mService);
-        manager.registerCallback(mExecutor, mClientCallback);
-        assertFalse(manager.registerCallback(mExecutor, mClientCallback));
-
-        manager = SharedConnectivityManager.create(mContext);
-        manager.setService(mService);
-        doThrow(new RemoteException()).when(mService).registerCallback(any());
-        assertFalse(manager.registerCallback(mExecutor, mClientCallback));
+    public void resourcesNotDefined() {
+        when(mResources.getString(anyInt())).thenThrow(new Resources.NotFoundException());
+        assertNull(SharedConnectivityManager.create(mContext));
     }
 
     /**
-     * Verifies callback is unregistered from the service if it was registered before and only when
-     * service is not null.
+     * Verifies registerCallback behavior.
      */
     @Test
-    public void testUnregisterCallback() throws Exception {
+    public void registerCallback_serviceNotConnected_registrationCachedThenConnected()
+            throws Exception {
+        SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
+        manager.setService(null);
+        manager.registerCallback(mExecutor, mClientCallback);
+        manager.getServiceConnection().onServiceConnected(COMPONENT_NAME, mIBinder);
+        // Since the binder is embedded in a proxy class, the call to registerCallback is done on
+        // the proxy. So instead verifying that the proxy is calling the binder.
+        verify(mIBinder).transact(anyInt(), any(Parcel.class), any(Parcel.class), anyInt());
+    }
+
+    @Test
+    public void registerCallback_serviceNotConnected_canUnregisterAndReregister() {
+        SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
+        manager.setService(null);
+        manager.registerCallback(mExecutor, mClientCallback);
+        manager.unregisterCallback(mClientCallback);
+        manager.registerCallback(mExecutor, mClientCallback);
+        verify(mClientCallback, never()).onRegisterCallbackFailed(any(Exception.class));
+    }
+
+    @Test
+    public void registerCallback_serviceConnected() throws Exception {
+        SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
+        manager.setService(mService);
+        manager.registerCallback(mExecutor, mClientCallback);
+        verify(mService).registerCallback(any());
+        verify(mClientCallback, never()).onRegisterCallbackFailed(any(Exception.class));
+    }
+
+    @Test
+    public void registerCallback_doubleRegistration_shouldFail() throws Exception {
+        SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
+        manager.setService(mService);
+        manager.registerCallback(mExecutor, mClientCallback);
+        manager.registerCallback(mExecutor, mClientCallback);
+        verify(mClientCallback).onRegisterCallbackFailed(any(IllegalStateException.class));
+    }
+
+    @Test
+    public void registerCallback_remoteException_shouldFail() throws Exception {
+        SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
+        manager.setService(mService);
+        doThrow(new RemoteException()).when(mService).registerCallback(any());
+        manager.registerCallback(mExecutor, mClientCallback);
+        verify(mClientCallback).onRegisterCallbackFailed(any(RemoteException.class));
+    }
+
+    /**
+     * Verifies unregisterCallback behavior.
+     */
+    @Test
+    public void unregisterCallback_withoutRegisteringFirst_serviceNotConnected_shouldFail() {
         SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
         manager.setService(null);
         assertFalse(manager.unregisterCallback(mClientCallback));
+    }
 
-        manager = SharedConnectivityManager.create(mContext);
+    @Test
+    public void unregisterCallback_withoutRegisteringFirst_serviceConnected_shouldFail() {
+        SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
+        manager.setService(mService);
+        assertFalse(manager.unregisterCallback(mClientCallback));
+    }
+
+    @Test
+    public void unregisterCallback() throws Exception {
+        SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
         manager.setService(mService);
         manager.registerCallback(mExecutor, mClientCallback);
         assertTrue(manager.unregisterCallback(mClientCallback));
         verify(mService).unregisterCallback(any());
+    }
 
-
-        manager = SharedConnectivityManager.create(mContext);
+    @Test
+    public void unregisterCallback_doubleUnregistration_serviceConnected_shouldFail() {
+        SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
         manager.setService(mService);
         manager.registerCallback(mExecutor, mClientCallback);
         manager.unregisterCallback(mClientCallback);
         assertFalse(manager.unregisterCallback(mClientCallback));
+    }
 
-        manager = SharedConnectivityManager.create(mContext);
+    @Test
+    public void unregisterCallback_doubleUnregistration_serviceNotConnected_shouldFail() {
+        SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
+        manager.setService(null);
+        manager.registerCallback(mExecutor, mClientCallback);
+        manager.unregisterCallback(mClientCallback);
+        assertFalse(manager.unregisterCallback(mClientCallback));
+    }
+
+    @Test
+    public void unregisterCallback_remoteException_shouldFail() throws Exception {
+        SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
         manager.setService(mService);
         doThrow(new RemoteException()).when(mService).unregisterCallback(any());
         assertFalse(manager.unregisterCallback(mClientCallback));
     }
 
     /**
-     * Verifies service is called when not null and exceptions are handles when calling
-     * connectTetherNetwork.
+     * Verifies callback is called when service is connected
      */
     @Test
-    public void testConnectTetherNetwork() throws RemoteException {
-        TetherNetwork network = buildTetherNetwork();
+    public void onServiceConnected_registerCallbackBeforeConnection() {
+        SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
+        manager.registerCallback(mExecutor, mClientCallback);
+        manager.getServiceConnection().onServiceConnected(COMPONENT_NAME, mIBinder);
+        verify(mClientCallback).onServiceConnected();
+    }
 
+    @Test
+    public void onServiceConnected_registerCallbackAfterConnection() {
+        SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
+        manager.getServiceConnection().onServiceConnected(COMPONENT_NAME, mIBinder);
+        manager.registerCallback(mExecutor, mClientCallback);
+        verify(mClientCallback).onServiceConnected();
+    }
+
+    /**
+     * Verifies callback is called when service is disconnected
+     */
+    @Test
+    public void onServiceDisconnected_registerCallbackBeforeConnection() {
+        SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
+        manager.registerCallback(mExecutor, mClientCallback);
+        manager.getServiceConnection().onServiceConnected(COMPONENT_NAME, mIBinder);
+        manager.getServiceConnection().onServiceDisconnected(COMPONENT_NAME);
+        verify(mClientCallback).onServiceDisconnected();
+    }
+
+    @Test
+    public void onServiceDisconnected_registerCallbackAfterConnection() {
+        SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
+        manager.getServiceConnection().onServiceConnected(COMPONENT_NAME, mIBinder);
+        manager.registerCallback(mExecutor, mClientCallback);
+        manager.getServiceConnection().onServiceDisconnected(COMPONENT_NAME);
+        verify(mClientCallback).onServiceDisconnected();
+    }
+
+    /**
+     * Verifies connectTetherNetwork behavior.
+     */
+    @Test
+    public void connectTetherNetwork_serviceNotConnected_shouldFail() {
+        TetherNetwork network = buildTetherNetwork();
         SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
         manager.setService(null);
         assertFalse(manager.connectTetherNetwork(network));
+    }
 
-        manager = SharedConnectivityManager.create(mContext);
+    @Test
+    public void connectTetherNetwork() throws RemoteException {
+        TetherNetwork network = buildTetherNetwork();
+        SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
         manager.setService(mService);
         manager.connectTetherNetwork(network);
         verify(mService).connectTetherNetwork(network);
+    }
 
+    @Test
+    public void connectTetherNetwork_remoteException_shouldFail() throws RemoteException {
+        TetherNetwork network = buildTetherNetwork();
+        SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
+        manager.setService(mService);
         doThrow(new RemoteException()).when(mService).connectTetherNetwork(network);
         assertFalse(manager.connectTetherNetwork(network));
     }
 
     /**
-     * Verifies service is called when not null and exceptions are handles when calling
-     * disconnectTetherNetwork.
+     * Verifies disconnectTetherNetwork behavior.
      */
     @Test
-    public void testDisconnectTetherNetwork() throws RemoteException {
+    public void disconnectTetherNetwork_serviceNotConnected_shouldFail() {
         SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
         manager.setService(null);
         assertFalse(manager.disconnectTetherNetwork());
+    }
 
-        manager = SharedConnectivityManager.create(mContext);
+    @Test
+    public void disconnectTetherNetwork() throws RemoteException {
+        SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
         manager.setService(mService);
         manager.disconnectTetherNetwork();
         verify(mService).disconnectTetherNetwork();
+    }
 
+    @Test
+    public void disconnectTetherNetwork_remoteException_shouldFail() throws RemoteException {
+        SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
+        manager.setService(mService);
         doThrow(new RemoteException()).when(mService).disconnectTetherNetwork();
         assertFalse(manager.disconnectTetherNetwork());
     }
 
     /**
-     * Verifies service is called when not null and exceptions are handles when calling
-     * connectKnownNetwork.
+     * Verifies connectKnownNetwork behavior.
      */
     @Test
-    public void testConnectKnownNetwork() throws RemoteException {
+    public void connectKnownNetwork_serviceNotConnected_shouldFail() throws RemoteException {
         KnownNetwork network = buildKnownNetwork();
-
         SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
         manager.setService(null);
         assertFalse(manager.connectKnownNetwork(network));
+    }
 
-        manager = SharedConnectivityManager.create(mContext);
+    @Test
+    public void connectKnownNetwork() throws RemoteException {
+        KnownNetwork network = buildKnownNetwork();
+        SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
         manager.setService(mService);
         manager.connectKnownNetwork(network);
         verify(mService).connectKnownNetwork(network);
+    }
 
+    @Test
+    public void connectKnownNetwork_remoteException_shouldFail() throws RemoteException {
+        KnownNetwork network = buildKnownNetwork();
+        SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
+        manager.setService(mService);
         doThrow(new RemoteException()).when(mService).connectKnownNetwork(network);
         assertFalse(manager.connectKnownNetwork(network));
     }
 
     /**
-     * Verifies service is called when not null and exceptions are handles when calling
-     * forgetKnownNetwork.
+     * Verifies forgetKnownNetwork behavior.
      */
     @Test
-    public void testForgetKnownNetwork() throws RemoteException {
+    public void forgetKnownNetwork_serviceNotConnected_shouldFail() {
         KnownNetwork network = buildKnownNetwork();
-
         SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
         manager.setService(null);
         assertFalse(manager.forgetKnownNetwork(network));
+    }
 
-        manager = SharedConnectivityManager.create(mContext);
+    @Test
+    public void forgetKnownNetwork_serviceConnected() throws RemoteException {
+        KnownNetwork network = buildKnownNetwork();
+        SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
         manager.setService(mService);
         manager.forgetKnownNetwork(network);
         verify(mService).forgetKnownNetwork(network);
+    }
 
+    @Test
+    public void forgetKnownNetwork_remoteException_shouldFail() throws RemoteException {
+        KnownNetwork network = buildKnownNetwork();
+        SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
+        manager.setService(mService);
         doThrow(new RemoteException()).when(mService).forgetKnownNetwork(network);
         assertFalse(manager.forgetKnownNetwork(network));
     }
 
     private void setResources(@Mock Context context) {
         when(context.getResources()).thenReturn(mResources);
-        when(context.getPackageName()).thenReturn(PACKAGE_NAME);
-        when(mResources.getIdentifier(anyString(), anyString(), anyString()))
-                .thenReturn(SERVICE_PACKAGE_ID, SERVICE_CLASS_ID);
-        when(mResources.getString(SERVICE_PACKAGE_ID)).thenReturn(SERVICE_PACKAGE_NAME);
-        when(mResources.getString(SERVICE_CLASS_ID)).thenReturn(SERVICE_CLASS_NAME);
+        when(mResources.getString(anyInt()))
+                .thenReturn(SERVICE_PACKAGE_NAME, SERVICE_INTENT_ACTION);
     }
 
     private TetherNetwork buildTetherNetwork() {