Incoming call notifiers to tell Telecomm of incoming calls.

This CL creates instances of the notifier class for each of the PSTN
call services.
SIP is different and will come later.

Depends on: I6a7bc0273101dec77908fce796b25146d95a5e6a

Change-Id: I8499563b779c5cda6163db5e36f87dfb2629d450
diff --git a/src/com/android/phone/PhoneApp.java b/src/com/android/phone/PhoneApp.java
index c5f69bf..bf30d58 100644
--- a/src/com/android/phone/PhoneApp.java
+++ b/src/com/android/phone/PhoneApp.java
@@ -40,7 +40,7 @@
             mPhoneGlobals = new PhoneGlobals(this);
             mPhoneGlobals.onCreate();
 
-            mTelephonyGlobals = new TelephonyGlobals();
+            mTelephonyGlobals = new TelephonyGlobals(this);
             mTelephonyGlobals.onCreate();
         }
     }
diff --git a/src/com/android/services/telephony/IncomingCallNotifier.java b/src/com/android/services/telephony/IncomingCallNotifier.java
new file mode 100644
index 0000000..5121dcf
--- /dev/null
+++ b/src/com/android/services/telephony/IncomingCallNotifier.java
@@ -0,0 +1,125 @@
+/*
+ * 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.services.telephony;
+
+import com.google.common.base.Preconditions;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.AsyncResult;
+import android.os.Handler;
+import android.os.Message;
+import android.telecomm.CallService;
+import android.telecomm.CallServiceDescriptor;
+import android.telecomm.TelecommConstants;
+import android.util.Log;
+
+import com.android.internal.telephony.Call;
+import com.android.internal.telephony.Connection;
+import com.android.internal.telephony.Phone;
+
+/**
+ * Listens to incoming-call events from the associated phone object and notifies Telecomm upon each
+ * occurence. One instance of these exists for each of the telephony-based call services.
+ */
+final class IncomingCallNotifier {
+
+    private static final String TAG = IncomingCallNotifier.class.getSimpleName();
+    private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
+
+    /** New ringing connection event code. */
+    private static final int EVENT_NEW_RINGING_CONNECTION = 100;
+
+    /** The phone object to listen to. */
+    private final Phone mPhone;
+
+    /** The class for the associated call service. */
+    private final Class<? extends CallService> mCallServiceClass;
+
+    /**
+     * Used to listen to events from {@link #mPhone}.
+     */
+    private final Handler mHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            switch(msg.what) {
+                case EVENT_NEW_RINGING_CONNECTION:
+                    handleNewRingingConnection((AsyncResult) msg.obj);
+                    break;
+                default:
+                    break;
+            }
+        }
+    };
+
+    /**
+     * Persists the specified parameters and starts listening to phone events.
+     *
+     * @param callServiceClass The call service class.
+     * @param phone The phone object for listening to incoming calls.
+     */
+    IncomingCallNotifier(Class<? extends CallService> callServiceClass, Phone phone) {
+        Preconditions.checkNotNull(callServiceClass);
+        Preconditions.checkNotNull(phone);
+
+        mCallServiceClass = callServiceClass;
+        mPhone = phone;
+
+        mPhone.registerForNewRingingConnection(mHandler, EVENT_NEW_RINGING_CONNECTION, null);
+    }
+
+    /**
+     * Verifies the incoming call and triggers sending the incoming-call intent to Telecomm.
+     *
+     * @param asyncResult The result object from the new ringing event.
+     */
+    private void handleNewRingingConnection(AsyncResult asyncResult) {
+        if (DBG) {
+            Log.d(TAG, "handleNewRingingConnection");
+        }
+
+        Connection connection = (Connection) asyncResult.result;
+        if (connection != null) {
+            Call call = connection.getCall();
+
+            // Final verification of the ringing state before sending the intent to Telecomm.
+            if (call != null && call.getState().isRinging()) {
+                sendIncomingCallIntent();
+            }
+        }
+    }
+
+    /**
+     * Sends the incoming call intent to telecomm.
+     */
+    private void sendIncomingCallIntent() {
+        Context context = mPhone.getContext();
+
+        CallServiceDescriptor.Builder builder = CallServiceDescriptor.newBuilder(context);
+        builder.setCallService(mCallServiceClass);
+        builder.setNetworkType(CallServiceDescriptor.FLAG_PSTN);
+
+        Intent intent = new Intent(TelecommConstants.ACTION_INCOMING_CALL);
+        intent.putExtra(TelecommConstants.EXTRA_CALL_SERVICE_DESCRIPTOR, builder.build());
+
+        if (DBG) {
+            Log.d(TAG, "Sending incoming call intent: " + intent);
+        }
+
+        context.startActivity(intent);
+    }
+}
diff --git a/src/com/android/services/telephony/TelephonyGlobals.java b/src/com/android/services/telephony/TelephonyGlobals.java
index e86a40e..722a880 100644
--- a/src/com/android/services/telephony/TelephonyGlobals.java
+++ b/src/com/android/services/telephony/TelephonyGlobals.java
@@ -16,6 +16,11 @@
 
 package com.android.services.telephony;
 
+import android.content.Context;
+import android.telephony.TelephonyManager;
+
+import com.android.internal.telephony.PhoneFactory;
+
 /**
  * Singleton entry point for the telephony-services app. Initializes ongoing systems relating to
  * PSTN and SIP calls. This is started when the device starts and will be restarted automatically
@@ -27,7 +32,54 @@
  */
 public class TelephonyGlobals {
 
+    /** The application context. */
+    private final Context mContext;
+
+    /** Handles incoming calls for GSM. */
+    private IncomingCallNotifier mGsmIncomingCallNotifier;
+
+    /** Handles incoming calls for CDMA. */
+    private IncomingCallNotifier mCdmaIncomingCallNotifier;
+
+    /**
+     * Persists the specified parameters.
+     *
+     * @param context The application context.
+     */
+    public TelephonyGlobals(Context context) {
+        mContext = context.getApplicationContext();
+    }
+
     public void onCreate() {
-        // TODO(santoscordon): Start incoming-call listeners.
+        setupIncomingCallNotifiers();
+    }
+
+    /**
+     * Sets up incoming call notifiers for all the call services.
+     */
+    private void setupIncomingCallNotifiers() {
+        TelephonyManager telephonyManager = (TelephonyManager) mContext.getSystemService(
+                Context.TELEPHONY_SERVICE);
+
+        // TODO(santoscordon): getPhoneType() eventually executes
+        // PhoneInterfaceManager.getActivePhoneType() which references the PhoneProxy that is
+        // created in PhoneGlobals.onCreate(). IOW, this only works right now because PhoneApp calls
+        // PhoneGlobals.onCreate() *before* calling TelephonyGlobals.onCreate(). We want to
+        // eventually move that setup code into this method.
+        int phoneType = telephonyManager.getPhoneType();
+
+        if (TelephonyManager.PHONE_TYPE_GSM == phoneType) {
+            mGsmIncomingCallNotifier = new IncomingCallNotifier(
+                    GsmCallService.class, CachedPhoneFactory.getGsmPhone());
+
+        } else if (TelephonyManager.PHONE_TYPE_CDMA == phoneType) {
+            mCdmaIncomingCallNotifier = new IncomingCallNotifier(
+                    CdmaCallService.class, CachedPhoneFactory.getCdmaPhone());
+        }
+
+        // TODO(santoscordon): Do SIP.  SIP will require a slightly different solution since it
+        // doesn't have a phone object in the same way as PSTN calls. Additionally, the user can
+        // set up SIP to do outgoing calls, but not listen for incoming calls (uses extra battery).
+        // We need to make sure we take all of that into account.
     }
 }