Adding a CallService & CallServiceProvider concrete classes.

First step in no longer using ICallService and ICallServiceProviders
directly.
Adds a generic ServiceBinder class to manage binding/unbinding.

Change-Id: I7d26958dd85a99316dbd5e70caba3fd91d35911b
diff --git a/src/com/android/telecomm/ServiceBinder.java b/src/com/android/telecomm/ServiceBinder.java
new file mode 100644
index 0000000..900d18f
--- /dev/null
+++ b/src/com/android/telecomm/ServiceBinder.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2014 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.telecomm;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.IBinder;
+import android.os.IInterface;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+
+/**
+ * Abstract class to perform the work of binding and unbinding to the specified service interface.
+ * Subclasses supply the service intent and component name and this class will invoke protected
+ * methods when the class is bound, unbound, or upon failure.
+ */
+abstract class ServiceBinder<ServiceInterface extends IInterface> {
+
+    private final class ServiceBinderConnection implements ServiceConnection {
+        @Override
+        public void onServiceConnected(ComponentName componentName, IBinder binder) {
+            ThreadUtil.checkOnMainThread();
+
+            // Unbind request was queued so unbind immediately.
+            if (mIsBindingAborted) {
+                clearAbort();
+                mContext.unbindService(this);
+                return;
+            }
+
+            mServiceConnection = this;
+            mBinder = binder;
+            handleSuccessfulConnection(binder);
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName componentName) {
+            mServiceConnection = null;
+            clearAbort();
+
+            handleServiceDisconnected();
+        }
+    }
+
+    /** The application context. */
+    private final Context mContext;
+
+    /** The intent action to use when binding through {@link Context#bindService}. */
+    private final String mServiceAction;
+
+    /** The component name of the service to bind to. */
+    private final ComponentName mComponentName;
+
+    /** Used to bind and unbind from the service. */
+    private ServiceConnection mServiceConnection;
+
+    /** The binder provided by {@link ServiceConnection#onServiceConnected} */
+    private IBinder mBinder;
+
+    /**
+     * Indicates that an unbind request was made when the service was not yet bound. If the service
+     * successfully connects when this is true, it should be unbound immediately.
+     */
+    private boolean mIsBindingAborted;
+
+    /**
+     * Persists the specified parameters and initializes the new instance.
+     *
+     * @param serviceAction The intent-action used with {@link Context#bindService}.
+     * @param componentName The component name of the service with which to bind.
+     */
+    protected ServiceBinder(String serviceAction, ComponentName componentName) {
+        Preconditions.checkState(!Strings.isNullOrEmpty(serviceAction));
+        Preconditions.checkNotNull(componentName);
+
+        mContext = TelecommApp.getInstance();
+        mServiceAction = serviceAction;
+        mComponentName = componentName;
+    }
+
+    /**
+     * Performs an asynchronous bind to the service if not already bound.
+     *
+     * @return The result of {#link Context#bindService} or true if already bound.
+     */
+    final boolean bind() {
+        ThreadUtil.checkOnMainThread();
+
+        // Reset any abort request if we're asked to bind again.
+        clearAbort();
+
+        if (mServiceConnection == null) {
+            Intent serviceIntent = new Intent(mServiceAction).setComponent(mComponentName);
+            ServiceConnection connection = new ServiceBinderConnection();
+
+            if (!mContext.bindService(serviceIntent, connection, Context.BIND_AUTO_CREATE)) {
+                handleFailedConnection();
+                return false;
+            }
+        } else {
+            Preconditions.checkNotNull(mBinder);
+            handleSuccessfulConnection(mBinder);
+        }
+
+        return true;
+    }
+
+    /**
+     * Unbinds from the service if already bound, no-op otherwise.
+     */
+    final void unbind() {
+        ThreadUtil.checkOnMainThread();
+
+        if (mServiceConnection == null) {
+            // We're not yet bound, so queue up an abort request.
+            mIsBindingAborted = true;
+        } else {
+            mContext.unbindService(mServiceConnection);
+            mServiceConnection = null;
+            mBinder = null;
+        }
+    }
+
+    ComponentName getComponentName() {
+        return mComponentName;
+    }
+
+    /**
+     * Handles a successful attempt to bind to service. See {@link Context#bindService}.
+     *
+     * @param service The actual bound service implementation.
+     */
+    protected abstract void handleSuccessfulConnection(IBinder binder);
+
+    /**
+     * Handles a failed attempt to bind to service. See {@link Context#bindService}.
+     */
+    protected abstract void handleFailedConnection();
+
+    /**
+     * Handles a service disconnection.
+     */
+    protected abstract void handleServiceDisconnected();
+
+    private void clearAbort() {
+        mIsBindingAborted = false;
+    }
+}