Handle cancelMissedCallsNotification.

Change-Id: Icdbca5371c54af4da098409275092c34c3bddc8b
diff --git a/src/com/android/telecomm/Call.java b/src/com/android/telecomm/Call.java
index ddd4b1d..8766a63 100644
--- a/src/com/android/telecomm/Call.java
+++ b/src/com/android/telecomm/Call.java
@@ -75,6 +75,42 @@
         void onCannedSmsResponsesLoaded(Call call);
         void onCallVideoProviderChanged(Call call);
         void onFeaturesChanged(Call call);
+        void onCallerInfoChanged(Call call);
+    }
+
+    abstract static class ListenerBase implements Listener {
+        @Override
+        public void onSuccessfulOutgoingCall(Call call) {}
+        @Override
+        public void onFailedOutgoingCall(Call call, int errorCode, String errorMsg) {}
+        @Override
+        public void onCancelledOutgoingCall(Call call) {}
+        @Override
+        public void onSuccessfulIncomingCall(Call call, CallInfo callInfo) {}
+        @Override
+        public void onFailedIncomingCall(Call call) {}
+        @Override
+        public void onRequestingRingback(Call call, boolean requestingRingback) {}
+        @Override
+        public void onPostDialWait(Call call, String remaining) {}
+        @Override
+        public void onIsConferenceCapableChanged(Call call, boolean isConferenceCapable) {}
+        @Override
+        public void onExpiredConferenceCall(Call call) {}
+        @Override
+        public void onConfirmedConferenceCall(Call call) {}
+        @Override
+        public void onParentChanged(Call call) {}
+        @Override
+        public void onChildrenChanged(Call call) {}
+        @Override
+        public void onCannedSmsResponsesLoaded(Call call) {}
+        @Override
+        public void onCallVideoProviderChanged(Call call) {}
+        @Override
+        public void onFeaturesChanged(Call call) {}
+        @Override
+        public void onCallerInfoChanged(Call call) {}
     }
 
     private static final OnQueryCompleteListener sCallerInfoQueryListener =
@@ -991,6 +1027,10 @@
                         personUri,
                         sPhotoLoadListener,
                         this);
+            } else {
+                for (Listener l : mListeners) {
+                    l.onCallerInfoChanged(this);
+                }
             }
 
             processDirectToVoicemail();
@@ -1008,6 +1048,10 @@
         if (mQueryToken == token) {
             mCallerInfo.cachedPhoto = photo;
             mCallerInfo.cachedPhotoIcon = photoIcon;
+
+            for (Listener l : mListeners) {
+                l.onCallerInfoChanged(this);
+            }
         }
     }
 
diff --git a/src/com/android/telecomm/CallsManager.java b/src/com/android/telecomm/CallsManager.java
index cd5cb2a..56e8e51 100644
--- a/src/com/android/telecomm/CallsManager.java
+++ b/src/com/android/telecomm/CallsManager.java
@@ -44,7 +44,7 @@
  * access from other packages specifically refraining from passing the CallsManager instance
  * beyond the com.android.telecomm package boundary.
  */
-public final class CallsManager implements Call.Listener {
+public final class CallsManager extends Call.ListenerBase {
 
     // TODO(santoscordon): Consider renaming this CallsManagerPlugin.
     interface CallsManagerListener {
diff --git a/src/com/android/telecomm/MissedCallNotifier.java b/src/com/android/telecomm/MissedCallNotifier.java
index 79c9c0f..f0f5217 100644
--- a/src/com/android/telecomm/MissedCallNotifier.java
+++ b/src/com/android/telecomm/MissedCallNotifier.java
@@ -20,9 +20,11 @@
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.TaskStackBuilder;
+import android.content.AsyncQueryHandler;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
+import android.database.Cursor;
 import android.graphics.Bitmap;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
@@ -42,6 +44,14 @@
  */
 class MissedCallNotifier extends CallsManagerListenerBase {
 
+    private static final String[] CALL_LOG_PROJECTION = new String[] {
+        Calls._ID,
+        Calls.NUMBER,
+        Calls.NUMBER_PRESENTATION,
+        Calls.DATE,
+        Calls.DURATION,
+        Calls.TYPE,
+    };
     private static final int MISSED_CALL_NOTIFICATION_ID = 1;
     private static final String SCHEME_SMSTO = "smsto";
 
@@ -55,6 +65,8 @@
         mContext = context;
         mNotificationManager =
                 (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+
+        updateOnStartup();
     }
 
     /** {@inheritDoc} */
@@ -243,4 +255,53 @@
         notification.flags |= Notification.FLAG_SHOW_LIGHTS;
         notification.defaults |= Notification.DEFAULT_LIGHTS;
     }
+
+    /**
+     * Adds the missed call notification on startup if there are unread missed calls.
+     */
+    private void updateOnStartup() {
+        Log.d(this, "updateOnStartup()...");
+
+        // instantiate query handler
+        AsyncQueryHandler queryHandler = new AsyncQueryHandler(mContext.getContentResolver()) {
+            @Override
+            protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
+                if (cursor != null) {
+                    while (cursor.moveToNext()) {
+                        // Get data about the missed call from the cursor
+                        Uri handle = Uri.parse(cursor.getString(
+                                cursor.getColumnIndexOrThrow(Calls.NUMBER)));
+
+                        // Convert the data to a call object
+                        Call call = new Call(null, null, null, true, false);
+                        call.setDisconnectCause(DisconnectCause.INCOMING_MISSED, "");
+                        call.setState(CallState.DISCONNECTED);
+
+                        // Listen for the update to the caller information before posting the
+                        // notification so that we have the contact info and photo.
+                        call.addListener(new Call.ListenerBase() {
+                            @Override
+                            public void onCallerInfoChanged(Call call) {
+                                call.removeListener(this);  // No longer need to listen to call
+                                                            // changes after the contact info
+                                                            // is retrieved.
+                                showMissedCallNotification(call);
+                            }
+                        });
+                        // Set the handle here because that is what triggers the contact info query.
+                        call.setHandle(handle);
+                    }
+                }
+            }
+        };
+
+        // setup query spec, look for all Missed calls that are new.
+        StringBuilder where = new StringBuilder("type=");
+        where.append(Calls.MISSED_TYPE);
+        where.append(" AND new=1");
+
+        // start the query
+        queryHandler.startQuery(0, null, Calls.CONTENT_URI, CALL_LOG_PROJECTION,
+                where.toString(), null, Calls.DEFAULT_SORT_ORDER);
+    }
 }
diff --git a/src/com/android/telecomm/TelecommApp.java b/src/com/android/telecomm/TelecommApp.java
index 7f477f9..37c7aa0 100644
--- a/src/com/android/telecomm/TelecommApp.java
+++ b/src/com/android/telecomm/TelecommApp.java
@@ -40,7 +40,7 @@
 
         mMissedCallNotifier = new MissedCallNotifier(this);
         if (UserHandle.myUserId() == UserHandle.USER_OWNER) {
-            TelecommServiceImpl.init();
+            TelecommServiceImpl.init(mMissedCallNotifier);
         }
     }
 
diff --git a/src/com/android/telecomm/TelecommServiceImpl.java b/src/com/android/telecomm/TelecommServiceImpl.java
index 24d6cc4..d723aef 100644
--- a/src/com/android/telecomm/TelecommServiceImpl.java
+++ b/src/com/android/telecomm/TelecommServiceImpl.java
@@ -76,6 +76,9 @@
                     case MSG_ACCEPT_RINGING_CALL:
                         acceptRingingCallInternal();
                         break;
+                    case MSG_CANCEL_MISSED_CALLS_NOTIFICATION:
+                        mMissedCallNotifier.clearMissedCalls();
+                        break;
                 }
 
                 if (result != null) {
@@ -99,15 +102,17 @@
     private static final int MSG_IS_RINGING = 4;
     private static final int MSG_END_CALL = 5;
     private static final int MSG_ACCEPT_RINGING_CALL = 6;
+    private static final int MSG_CANCEL_MISSED_CALLS_NOTIFICATION = 7;
 
     /** The singleton instance. */
     private static TelecommServiceImpl sInstance;
 
     private final CallsManager mCallsManager = CallsManager.getInstance();
-
+    private final MissedCallNotifier mMissedCallNotifier;
     private final MainThreadHandler mMainThreadHandler = new MainThreadHandler();
 
-    private TelecommServiceImpl() {
+    private TelecommServiceImpl(MissedCallNotifier missedCallNotifier) {
+        mMissedCallNotifier = missedCallNotifier;
         publish();
     }
 
@@ -115,10 +120,10 @@
      * Initialize the singleton TelecommServiceImpl instance.
      * This is only done once, at startup, from TelecommApp.onCreate().
      */
-    static TelecommServiceImpl init() {
+    static TelecommServiceImpl init(MissedCallNotifier missedCallNotifier) {
         synchronized (TelecommServiceImpl.class) {
             if (sInstance == null) {
-                sInstance = new TelecommServiceImpl();
+                sInstance = new TelecommServiceImpl(missedCallNotifier);
             } else {
                 Log.wtf(TAG, "init() called multiple times!  sInstance %s", sInstance);
             }
@@ -206,8 +211,12 @@
 
     @Override
     public void showCallScreen(boolean showDialpad) {
-        mMainThreadHandler.obtainMessage(MSG_SHOW_CALL_SCREEN, showDialpad ? 1 : 0, 0)
-                .sendToTarget();
+        sendRequestAsync(MSG_SHOW_CALL_SCREEN, showDialpad ? 1 : 0);
+    }
+
+    @Override
+    public void cancelMissedCallsNotification() {
+        sendRequestAsync(MSG_CANCEL_MISSED_CALLS_NOTIFICATION, 0);
     }
 
     //