Split out DeathRecipient behavior

We can re-use this class to monitor client interface death as well from
the WifiStateMachine.

Bug: 30041062
Test: Can kill wificond underneath the framework with tethering on
      and still see tethering taken down.
Test: unittests pass

Change-Id: I4dd60bd8eb674fc06d5eb301b0d8f10218126383
diff --git a/service/java/com/android/server/wifi/SoftApManager.java b/service/java/com/android/server/wifi/SoftApManager.java
index 46ef28f..99a6923 100644
--- a/service/java/com/android/server/wifi/SoftApManager.java
+++ b/service/java/com/android/server/wifi/SoftApManager.java
@@ -218,15 +218,8 @@
         private final State mIdleState = new IdleState();
         private final State mStartedState = new StartedState();
 
-        private ApInterfaceDeathRecipient mDeathRecipient;
-
-        private class ApInterfaceDeathRecipient implements DeathRecipient {
-            @Override
-            public void binderDied() {
-                SoftApStateMachine.this.sendMessage(CMD_AP_INTERFACE_BINDER_DEATH);
-            }
-        }
-
+        private final StateMachineDeathRecipient mDeathRecipient =
+                new StateMachineDeathRecipient(this, CMD_AP_INTERFACE_BINDER_DEATH);
 
         SoftApStateMachine(Looper looper) {
             super(TAG, looper);
@@ -241,44 +234,34 @@
         private class IdleState extends State {
             @Override
             public void enter() {
-                if (mDeathRecipient != null) {
-                    mApInterface.asBinder().unlinkToDeath(mDeathRecipient, 0);
-                    mDeathRecipient = null;
-                }
+                mDeathRecipient.unlinkToDeath();
             }
 
             @Override
             public boolean processMessage(Message message) {
                 switch (message.what) {
                     case CMD_START:
-                        int result = SUCCESS;
                         updateApState(WifiManager.WIFI_AP_STATE_ENABLING, 0);
-                        mDeathRecipient = new ApInterfaceDeathRecipient();
-                        try {
-                            mApInterface.asBinder().linkToDeath(mDeathRecipient, 0);
-                        } catch (RemoteException e) {
-                            // The remote has already died.
-                            result = ERROR_GENERIC;
+                        if (!mDeathRecipient.linkToDeath(mApInterface.asBinder())) {
+                            mDeathRecipient.unlinkToDeath();
+                            updateApState(WifiManager.WIFI_AP_STATE_FAILED,
+                                    WifiManager.SAP_START_FAILURE_GENERAL);
                             break;
                         }
 
-                        if (result == SUCCESS) {
-                            result = startSoftAp((WifiConfiguration) message.obj);
-                        }
-                        if (result == SUCCESS) {
-                            updateApState(WifiManager.WIFI_AP_STATE_ENABLED, 0);
-                            transitionTo(mStartedState);
-                        } else {
-                            int reason = WifiManager.SAP_START_FAILURE_GENERAL;
+                        int result = startSoftAp((WifiConfiguration) message.obj);
+                        if (result != SUCCESS) {
+                            int failureReason = WifiManager.SAP_START_FAILURE_GENERAL;
                             if (result == ERROR_NO_CHANNEL) {
-                                reason = WifiManager.SAP_START_FAILURE_NO_CHANNEL;
+                                failureReason = WifiManager.SAP_START_FAILURE_NO_CHANNEL;
                             }
-                            updateApState(WifiManager.WIFI_AP_STATE_FAILED, reason);
-                            if (mDeathRecipient != null) {
-                                mApInterface.asBinder().unlinkToDeath(mDeathRecipient, 0);
-                                mDeathRecipient = null;
-                            }
+                            mDeathRecipient.unlinkToDeath();
+                            updateApState(WifiManager.WIFI_AP_STATE_FAILED, failureReason);
+                            break;
                         }
+
+                        updateApState(WifiManager.WIFI_AP_STATE_ENABLED, 0);
+                        transitionTo(mStartedState);
                         break;
                     default:
                         /* Ignore all other commands. */
diff --git a/service/java/com/android/server/wifi/StateMachineDeathRecipient.java b/service/java/com/android/server/wifi/StateMachineDeathRecipient.java
new file mode 100644
index 0000000..64c4bee
--- /dev/null
+++ b/service/java/com/android/server/wifi/StateMachineDeathRecipient.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wifi;
+
+import android.os.IBinder;
+import android.os.IBinder.DeathRecipient;
+import android.os.RemoteException;
+import com.android.internal.util.StateMachine;
+
+/**
+ * Allows StateMachine instances to subscribe to binder death.
+ *
+ * @hide
+ */
+public class StateMachineDeathRecipient implements DeathRecipient {
+
+    private final StateMachine mStateMachine;
+    private final int mDeathCommand;
+    private IBinder mLinkedBinder;
+
+    /**
+     * Construct a StateMachineDeathRecipient.
+     *
+     * @param sm StateMachine instance to receive a message upon Binder death.
+     * @param command message to send the state machine.
+     */
+    public StateMachineDeathRecipient(StateMachine sm, int command) {
+        mStateMachine = sm;
+        mDeathCommand = command;
+    }
+
+    /**
+     * Listen for the death of a binder.
+     *
+     * This method will unlink from death notifications from any
+     * previously linked IBinder instance.
+     *
+     * @param binder remote object to listen for death.
+     * @return true iff we have successfully subscribed to death notifications of a live
+     *         IBinder instance.
+     */
+    public boolean linkToDeath(IBinder binder) {
+        unlinkToDeath();
+        try {
+            binder.linkToDeath(this, 0);
+        } catch (RemoteException e) {
+            // The remote has already died.
+            return false;
+        }
+        mLinkedBinder = binder;
+        return true;
+    }
+
+    /**
+     * Unlink from notifications from the last linked IBinder instance.
+     */
+    public void unlinkToDeath() {
+        if (mLinkedBinder == null) {
+            return;
+        }
+        mLinkedBinder.unlinkToDeath(this, 0);
+        mLinkedBinder = null;
+    }
+
+    /**
+     * Called by the binder subsystem upon remote object death.
+     */
+    @Override
+    public void binderDied() {
+        mStateMachine.sendMessage(mDeathCommand);
+    }
+}
\ No newline at end of file