Adding the resource-deallocation utility.

Change-Id: I73ee3b648d7b224017bb2445597f199f979be5de
diff --git a/src/com/android/telecomm/Switchboard.java b/src/com/android/telecomm/Switchboard.java
index 8661e51..4967c36 100644
--- a/src/com/android/telecomm/Switchboard.java
+++ b/src/com/android/telecomm/Switchboard.java
@@ -27,6 +27,7 @@
 import android.util.Log;
 
 import java.util.Collection;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
 
@@ -42,7 +43,13 @@
      * The frequency of invoking tick in milliseconds.
      * TODO(gilad): May require tuning.
      */
-    private final static int TICK_FREQUENCY = 250;
+    private final static int TICK_FREQUENCY_MS = 250;
+
+    /**
+     * The timeout beyond which to drop ongoing attempts to place/receive calls.
+     * TODO(gilad): May require tuning.
+     */
+    private final static int NEW_CALL_TIMEOUT_MS = 5000;
 
     private final CallsManager mCallsManager;
 
@@ -56,6 +63,8 @@
 
     private final CallServiceSelectorRepository mSelectorRepository;
 
+    private final BinderDeallocator mBinderDeallocator;
+
     /** Used to schedule tasks on the main (UI) thread. */
     private final Handler mHandler = new Handler(Looper.getMainLooper());
 
@@ -108,6 +117,8 @@
         mSelectorRepository = new CallServiceSelectorRepository(this);
         mCallServiceRepository =
                 new CallServiceRepository(this, mOutgoingCallsManager, mIncomingCallsManager);
+
+        mBinderDeallocator = new BinderDeallocator();
     }
 
     /**
@@ -118,7 +129,7 @@
      * @param call The yet-to-be-connected outgoing-call object.
      */
     void placeOutgoingCall(Call call) {
-        ThreadUtil.checkOnMainThread();
+        mBinderDeallocator.acquireUsePermit();
 
         // Reset prior to initiating the next lookup. One case to consider is (1) placeOutgoingCall
         // is invoked with call A, (2) the call-service lookup completes, but the one for selectors
@@ -147,6 +158,8 @@
      */
     void retrieveIncomingCall(Call call, CallServiceDescriptor descriptor) {
         Log.d(TAG, "retrieveIncomingCall");
+        mBinderDeallocator.acquireUsePermit();
+
         CallServiceWrapper callService = mCallServiceRepository.getCallService(descriptor);
         call.setCallService(callService);
         mIncomingCallsManager.retrieveIncomingCall(call);
@@ -161,7 +174,7 @@
      *     hence effectively omitted from the specified list.
      */
     void setCallServices(Set<CallServiceWrapper> callServices) {
-        ThreadUtil.checkOnMainThread();
+        mBinderDeallocator.updateBinders(mCallServices);
 
         mCallServices = callServices;
         processNewOutgoingCalls();
@@ -177,7 +190,8 @@
      */
     void setSelectors(Set<ICallServiceSelector> selectors) {
         // TODO(santoscordon): This should take in CallServiceSelectorWrapper instead of the direct
-        // ICallServiceSelector implementation. Copy what we have for CallServiceWrapper.
+        // ICallServiceSelector implementation. Copy what we have for CallServiceWrapper. Also need
+        // to invoke updateBinders(selectors) once this to-do is addressed.
         ThreadUtil.checkOnMainThread();
 
         // TODO(gilad): Add logic to include the built-in selectors (e.g. for dealing with
@@ -193,10 +207,10 @@
      * see {@link OutgoingCallProcessor}.
      */
     void handleSuccessfulOutgoingCall(Call call) {
-        mCallsManager.handleSuccessfulOutgoingCall(call);
+        Log.d(TAG, "handleSuccessfulOutgoingCall");
 
-        // Process additional (new) calls, if any.
-        processNewOutgoingCalls();
+        mCallsManager.handleSuccessfulOutgoingCall(call);
+        finalizeOutgoingCall(call);
     }
 
     /**
@@ -204,10 +218,11 @@
      * selector/call-service implementations, see {@link OutgoingCallProcessor}.
      */
     void handleFailedOutgoingCall(Call call) {
-        // TODO(gilad): More here.
+        Log.d(TAG, "handleFailedOutgoingCall");
 
-        // Process additional (new) calls, if any.
-        processNewOutgoingCalls();
+        // TODO(gilad): Notify mCallsManager.
+
+        finalizeOutgoingCall(call);
     }
 
     /**
@@ -217,7 +232,9 @@
      */
     void handleSuccessfulIncomingCall(Call call) {
         Log.d(TAG, "handleSuccessfulIncomingCall");
+
         mCallsManager.handleSuccessfulIncomingCall(call);
+        mBinderDeallocator.releaseUsePermit();
     }
 
     /**
@@ -227,10 +244,14 @@
      * @param call The call.
      */
     void handleFailedIncomingCall(Call call) {
+        Log.d(TAG, "handleFailedIncomingCall");
+
         // Since we set the call service before calling into incoming-calls manager, we clear it for
         // good measure if an error is reported.
         call.clearCallService();
 
+        mBinderDeallocator.releaseUsePermit();
+
         // At the moment there is nothing more to do if an incoming call is not retrieved. We may at
         // a future date bind to the in-call app optimistically during the incoming-call sequence
         // and this method could tell {@link CallsManager} to unbind from the in-call app if the
@@ -251,19 +272,7 @@
      * Schedules the next tick invocation.
      */
     private void scheduleNextTick() {
-         mHandler.postDelayed(mTicker, TICK_FREQUENCY);
-    }
-
-    /**
-     * Performs the set of tasks that needs to be executed on polling basis.
-     * TODO(gilad): Check for stale pending calls that may need to be terminated etc, see
-     * mNewOutgoingCalls and mPendingOutgoingCalls.
-     * TODO(gilad): Also intended to trigger the call switching/hand-off logic when applicable.
-     */
-    private void tick() {
-        // TODO(gilad): More here.
-        // TODO(santoscordon): Clear mCallServices if there exist no more new or pending outgoing
-        // calls.
+         mHandler.postDelayed(mTicker, TICK_FREQUENCY_MS);
     }
 
     /**
@@ -304,6 +313,56 @@
     }
 
     /**
+     * Finalizes the outgoing-call sequence, regardless if it succeeded or failed.
+     */
+    private void finalizeOutgoingCall(Call call) {
+        mPendingOutgoingCalls.remove(call);
+
+        mBinderDeallocator.releaseUsePermit();
+        processNewOutgoingCalls();  // Process additional (new) calls, if any.
+    }
+
+    /**
+     * Performs the set of tasks that needs to be executed on polling basis.
+     */
+    private void tick() {
+        // TODO(gilad): Add the necessary logic to support switching.
+
+        expireStaleOutgoingCalls(mNewOutgoingCalls);
+        expireStaleOutgoingCalls(mPendingOutgoingCalls);
+    }
+
+    /**
+     * Identifies stale calls and takes the necessary steps to mark these as expired.
+     *
+     * @param calls The set of calls to iterate through.
+     */
+    private void expireStaleOutgoingCalls(Set<Call> calls) {
+        if (calls.isEmpty()) {
+            return;
+        }
+
+        Iterator<Call> iterator = calls.iterator();
+        while (iterator.hasNext()) {
+            Call call = iterator.next();
+            if (call.getAgeInMilliseconds() >= NEW_CALL_TIMEOUT_MS) {
+                mOutgoingCallsManager.abort(call);
+                calls.remove(call);
+
+                // TODO(gilad): We may also have expired calls that are not yet associated with an
+                // OutgoingCallProcessor (e.g. when newer calls are "blocked" on older-yet-expired
+                // ones), in which case call.abort may need to be invoked directly.  Alternatively
+                // we can also create an OutgoingCallsManager instance for every new call at intent-
+                // processing time.
+
+                // TODO(gilad): Notify the user in the relevant cases (at least outgoing).
+
+                mBinderDeallocator.releaseUsePermit();
+            }
+        }
+    }
+
+    /**
      * Determines whether or not the specified collection is either null or empty.
      *
      * @param collection Either null or the collection object to be evaluated.