Fixes for voicemail notifications.

- Handle multiple notifications.
- Clear notifications correctly.
- Show un-cleared notifications on reboot.

Bug: 4968721
Change-Id: I1bd1eda4d75371fb7ba92063d74a61de232b61d6
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index ff7c089..6856452 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -647,7 +647,7 @@
                     android:resource="@xml/social_widget_info" />
         </receiver>
 
-        <receiver android:name=".calllog.NewVoicemailReceiver">
+        <receiver android:name=".calllog.CallLogReceiver">
             <intent-filter>
                 <action android:name="android.intent.action.NEW_VOICEMAIL" />
                 <data
@@ -655,6 +655,9 @@
                     android:host="com.android.voicemail"
                 />
             </intent-filter>
+            <intent-filter android:priority="100">
+                 <action android:name="android.intent.action.BOOT_COMPLETED"/>
+            </intent-filter>
         </receiver>
 
         <activity
@@ -664,5 +667,10 @@
                 <action android:name="android.intent.action.APPWIDGET_PICK" />
             </intent-filter>
         </activity>
+
+        <service
+            android:name=".calllog.CallLogNotificationsService"
+            android:exported="false"
+        />
     </application>
 </manifest>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 9962f0f..ea1d348 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1602,13 +1602,29 @@
     <!-- Button to view the updates from the current group on the group detail page [CHAR LIMIT=20] -->
     <string name="view_updates_from_group">View updates</string>
 
-    <!-- Title of the notification of new voicemail. -->
-    <string name="notification_voicemail_title">New voicemail</string>
+    <!-- Title of the notification of new voicemails. [CHAR LIMIT=30] -->
+    <plurals name="notification_voicemail_title">
+        <item quantity="one">Voicemail</item>
+        <item quantity="other"><xliff:g id="count">%1$d</xliff:g> Voicemails</item>
+    </plurals>
+
+    <!-- Used to build a list of names or phone numbers, to indicate the callers who left
+         voicemails.
+         The first argument may be one or more callers, the most recent ones.
+         The second argument is an additional callers.
+         This string is used to build a list of callers.
+
+         [CHAR LIMIT=10]
+     -->
+    <string name="notification_voicemail_callers_list"><xliff:g id="newer_callers">%1$s</xliff:g>, <xliff:g id="older_caller">%2$s</xliff:g></string>
+
+    <!-- Text used in the ticker to notify the user of the latest voicemail. [CHAR LIMIT=30] -->
+    <string name="notification_new_voicemail_ticker">New voicemail from <xliff:g id="caller">%1$s</xliff:g></string>
 
     <!-- Initial display for position of current playback, do not translate. -->
     <string name="voicemail_initial_time">00:05</string>
 
-    <!-- Message to show when there is an error playing back the voicemail. -->
+    <!-- Message to show when there is an error playing back the voicemail. [CHAR LIMIT=40] -->
     <string name="voicemail_playback_error">Could not play voicemail</string>
 
     <!-- The separator between the call type text and the date in the call log [CHAR LIMIT=3] -->
diff --git a/src/com/android/contacts/calllog/CallLogNotificationsService.java b/src/com/android/contacts/calllog/CallLogNotificationsService.java
new file mode 100644
index 0000000..eda11d6
--- /dev/null
+++ b/src/com/android/contacts/calllog/CallLogNotificationsService.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2011 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.contacts.calllog;
+
+import android.app.IntentService;
+import android.content.Intent;
+import android.util.Log;
+
+/**
+ * Provides operations for managing notifications.
+ * <p>
+ * At the moment, it only handle {@link #ACTION_MARK_NEW_CALLS_AS_OLD}, which marks all the new
+ * items in the call log as old; this is called when a notification is dismissed.
+ */
+public class CallLogNotificationsService extends IntentService {
+    private static final String TAG = "CallLogNotificationsService";
+
+    // Action to mark all the new calls as old. Invoked when the notifications need to be cleared.
+    public static final String ACTION_MARK_NEW_CALLS_AS_OLD =
+            "com.android.contacts.ACTION_MARK_NEW_CALLS_AS_OLD";
+
+    private CallLogQueryHandler mCallLogQueryHandler;
+
+    public CallLogNotificationsService() {
+        super("CallLogNotificationsService");
+        mCallLogQueryHandler = new CallLogQueryHandler(getContentResolver(), null /*listener*/);
+    }
+
+    @Override
+    protected void onHandleIntent(Intent intent) {
+        if (ACTION_MARK_NEW_CALLS_AS_OLD.equals(intent.getAction())) {
+            mCallLogQueryHandler.markNewCallsAsOld();
+            return;
+        } else {
+            Log.d(TAG, "onHandleIntent: could not handle: " + intent);
+        }
+    }
+
+}
diff --git a/src/com/android/contacts/calllog/CallLogReceiver.java b/src/com/android/contacts/calllog/CallLogReceiver.java
new file mode 100644
index 0000000..a3ff1f2
--- /dev/null
+++ b/src/com/android/contacts/calllog/CallLogReceiver.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2011 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.contacts.calllog;
+
+import android.app.NotificationManager;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.provider.VoicemailContract;
+import android.util.Log;
+
+/**
+ * Receiver for call log events.
+ * <p>
+ * It is currently used to handle {@link VoicemailContract#ACTION_NEW_VOICEMAIL} and
+ * {@link Intent#ACTION_BOOT_COMPLETED}.
+ */
+public class CallLogReceiver extends BroadcastReceiver {
+    private static final String TAG = "CallLogReceiver";
+
+    private VoicemailNotifier mNotifier;
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        if (mNotifier == null) {
+            mNotifier = getNotifier(context);
+        }
+        if (VoicemailContract.ACTION_NEW_VOICEMAIL.equals(intent.getAction())) {
+            mNotifier.notifyNewVoicemail(intent.getData());
+        } else if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
+            mNotifier.updateNotification();
+        } else {
+            Log.d(TAG, "onReceive: could not handle: " + intent);
+        }
+    }
+
+    private VoicemailNotifier getNotifier(Context context) {
+        NotificationManager notificationManager =
+                (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+        ContentResolver contentResolver = context.getContentResolver();
+        return new DefaultVoicemailNotifier(context, notificationManager,
+                DefaultVoicemailNotifier.createNewCallsQuery(contentResolver),
+                DefaultVoicemailNotifier.createNameLookupQuery(contentResolver),
+                DefaultVoicemailNotifier.createPhoneNumberHelper(context));
+    }
+}
diff --git a/src/com/android/contacts/calllog/DefaultVoicemailNotifier.java b/src/com/android/contacts/calllog/DefaultVoicemailNotifier.java
index b2ee5ce..bb0b0f3 100644
--- a/src/com/android/contacts/calllog/DefaultVoicemailNotifier.java
+++ b/src/com/android/contacts/calllog/DefaultVoicemailNotifier.java
@@ -16,23 +16,36 @@
 
 package com.android.contacts.calllog;
 
+import com.android.common.io.MoreCloseables;
+import com.android.contacts.CallDetailActivity;
 import com.android.contacts.R;
+import com.google.common.collect.Maps;
 
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.content.ContentResolver;
+import android.content.ContentUris;
 import android.content.Context;
 import android.content.Intent;
+import android.content.res.Resources;
 import android.database.Cursor;
 import android.net.Uri;
+import android.provider.CallLog.Calls;
 import android.provider.ContactsContract.PhoneLookup;
-import android.provider.VoicemailContract;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.util.Map;
 
 /**
- * Implementation of {@link VoicemailNotifier} that shows a notification in the status bar.
+ * Implementation of {@link VoicemailNotifier} that shows a notification in the
+ * status bar.
  */
 public class DefaultVoicemailNotifier implements VoicemailNotifier {
+    public static final String TAG = "DefaultVoicemailNotifier";
+
     /** The tag used to identify notifications from this class. */
     private static final String NOTIFICATION_TAG = "DefaultVoicemailNotifier";
     /** The identifier of the notification of new voicemails. */
@@ -40,115 +53,211 @@
 
     private final Context mContext;
     private final NotificationManager mNotificationManager;
-    private final VoicemailNumberQuery mVoicemailNumberQuery;
+    private final NewCallsQuery mNewCallsQuery;
     private final NameLookupQuery mNameLookupQuery;
+    private final PhoneNumberHelper mPhoneNumberHelper;
 
-    public DefaultVoicemailNotifier(Context context, NotificationManager notificationManager,
-            VoicemailNumberQuery voicemailNumberQuery, NameLookupQuery nameLookupQuery) {
+    public DefaultVoicemailNotifier(Context context,
+            NotificationManager notificationManager, NewCallsQuery newCallsQuery,
+            NameLookupQuery nameLookupQuery, PhoneNumberHelper phoneNumberHelper) {
         mContext = context;
         mNotificationManager = notificationManager;
-        mVoicemailNumberQuery = voicemailNumberQuery;
+        mNewCallsQuery = newCallsQuery;
         mNameLookupQuery = nameLookupQuery;
+        mPhoneNumberHelper = phoneNumberHelper;
     }
 
     @Override
-    public void notifyNewVoicemail(Uri uri) {
-        // Lookup the number that left the voicemail.
-        String number = mVoicemailNumberQuery.query(uri);
-        // Lookup the name of the contact associated with this number.
-        String name = mNameLookupQuery.query(number);
-        // Show the name of the contact if available, falling back to using the number if not.
-        String displayName = name == null ? number : name;
+    public void notifyNewVoicemail(Uri newVoicemailUri) {
+        Log.d(TAG, "notifyNewVoicemail: " + newVoicemailUri);
+        updateNotification(newVoicemailUri);
+    }
+
+    @Override
+    public void updateNotification() {
+        Log.d(TAG, "updateNotification");
+        updateNotification(null);
+    }
+
+    /** Updates the notification and notifies of the call with the given URI. */
+    private void updateNotification(Uri newCallUri) {
+        // Lookup the list of new voicemails to include in the notification.
+        // TODO: Move this into a service, to avoid holding the receiver up.
+        final NewCall[] newCalls = mNewCallsQuery.query();
+
+        if (newCalls.length == 0) {
+            Log.e(TAG, "No voicemails to notify about: clear the notification.");
+            clearNotification();
+            return;
+        }
+
+        Resources resources = mContext.getResources();
+
+        // This represents a list of names to include in the notification.
+        String callers = null;
+
+        // Maps each number into a name: if a number is in the map, it has already left a more
+        // recent voicemail.
+        final Map<String, String> names = Maps.newHashMap();
+
+        // Determine the call corresponding to the new voicemail we have to notify about.
+        NewCall callToNotify = null;
+
+        // Iterate over the new voicemails to determine all the information above.
+        for (NewCall newCall : newCalls) {
+            // Check if we already know the name associated with this number.
+            String name = names.get(newCall.number);
+            if (name == null) {
+                // Look it up in the database.
+                name = mNameLookupQuery.query(newCall.number);
+                // If we cannot lookup the contact, use the number instead.
+                if (name == null) {
+                    name = mPhoneNumberHelper.getDisplayNumber(newCall.number, "").toString();
+                    if (TextUtils.isEmpty(name)) {
+                        name = newCall.number;
+                    }
+                }
+                names.put(newCall.number, name);
+                // This is a new caller. Add it to the back of the list of callers.
+                if (TextUtils.isEmpty(callers)) {
+                    callers = name;
+                } else {
+                    callers = resources.getString(
+                            R.string.notification_voicemail_callers_list, callers, name);
+                }
+            }
+            // Check if this is the new call we need to notify about.
+            if (newCallUri != null && newCallUri.equals(newCall.voicemailUri)) {
+                callToNotify = newCall;
+            }
+        }
+
+        if (newCallUri != null && callToNotify == null) {
+            Log.e(TAG, "The new call could not be found in the call log: " + newCallUri);
+        }
+
+        // Determine the title of the notification and the icon for it.
+        final String title = resources.getQuantityString(
+                R.plurals.notification_voicemail_title, newCalls.length, newCalls.length);
+        // TODO: Use the photo of contact if all calls are from the same person.
+        final int icon = android.R.drawable.stat_notify_voicemail;
+
         Notification notification = new Notification.Builder(mContext)
-                .setSmallIcon(android.R.drawable.stat_notify_voicemail)
-                .setContentTitle(mContext.getString(R.string.notification_voicemail_title))
-                .setContentText(displayName)
-                .setDefaults(Notification.DEFAULT_ALL)
+                .setSmallIcon(icon)
+                .setContentTitle(title)
+                .setContentText(callers)
+                .setDefaults(callToNotify != null ? Notification.DEFAULT_ALL : 0)
+                .setDeleteIntent(createMarkNewCallsAsOld())
                 .setAutoCancel(true)
                 .getNotification();
 
-        // Open the voicemail when clicking on the notification.
-        notification.contentIntent =
-                PendingIntent.getActivity(mContext, 0, new Intent(Intent.ACTION_VIEW, uri), 0);
+        // Determine the intent to fire when the notification is clicked on.
+        final Intent contentIntent;
+        if (newCalls.length == 1) {
+            // Open the voicemail directly.
+            Log.d(TAG, "Opening voicemail directly on select");
+            contentIntent = new Intent(mContext, CallDetailActivity.class);
+            contentIntent.setData(newCalls[0].callsUri);
+            contentIntent.putExtra(CallDetailActivity.EXTRA_VOICEMAIL_URI,
+                    newCalls[0].voicemailUri);
+        } else {
+            // Open the call log.
+            Log.d(TAG, "Opening call log on select");
+            contentIntent = new Intent(Intent.ACTION_VIEW, Calls.CONTENT_URI);
+        }
+        notification.contentIntent = PendingIntent.getActivity(mContext, 0, contentIntent, 0);
+
+        // The text to show in the ticker, describing the new event.
+        if (callToNotify != null) {
+            notification.tickerText = resources.getString(
+                    R.string.notification_new_voicemail_ticker, names.get(callToNotify.number));
+        }
 
         mNotificationManager.notify(NOTIFICATION_TAG, NOTIFICATION_ID, notification);
     }
 
+    /** Creates a pending intent that marks all new calls as old. */
+    private PendingIntent createMarkNewCallsAsOld() {
+        Intent intent = new Intent(mContext, CallLogNotificationsService.class);
+        intent.setAction(CallLogNotificationsService.ACTION_MARK_NEW_CALLS_AS_OLD);
+        return PendingIntent.getService(mContext, 0, intent, 0);
+    }
+
     @Override
-    public void clearNewVoicemailNotification() {
+    public void clearNotification() {
         mNotificationManager.cancel(NOTIFICATION_TAG, NOTIFICATION_ID);
     }
 
-    /** Allows determining the number associated with a given voicemail. */
-    public interface VoicemailNumberQuery {
-        /**
-         * Returns the number associated with a voicemail URI, or null if the URI does not actually
-         * correspond to a voicemail.
-         *
-         * @throws IllegalArgumentException if the given {@code uri} is not a voicemail URI.
-         */
-        public String query(Uri uri);
+    /** Information about a new voicemail. */
+    private static final class NewCall {
+        public final Uri callsUri;
+        public final Uri voicemailUri;
+        public final String number;
+
+        public NewCall(Uri callsUri, Uri voicemailUri, String number) {
+            this.callsUri = callsUri;
+            this.voicemailUri = voicemailUri;
+            this.number = number;
+        }
     }
 
-    /** Create a new instance of {@link VoicemailNumberQuery}. */
-    public static VoicemailNumberQuery createVoicemailNumberQuery(ContentResolver contentResolver) {
-        return new DefaultVoicemailNumberQuery(contentResolver);
+    /** Allows determining the new calls for which a notification should be generated. */
+    public interface NewCallsQuery {
+        /**
+         * Returns the new calls for which a notification should be generated.
+         */
+        public NewCall[] query();
+    }
+
+    /** Create a new instance of {@link NewCallsQuery}. */
+    public static NewCallsQuery createNewCallsQuery(ContentResolver contentResolver) {
+        return new DefaultNewCallsQuery(contentResolver);
     }
 
     /**
-     * Default implementation of {@link VoicemailNumberQuery} that looks up the number in the
-     * voicemail content provider.
+     * Default implementation of {@link NewCallsQuery} that looks up the list of new calls to
+     * notify about in the call log.
      */
-    private static final class DefaultVoicemailNumberQuery implements VoicemailNumberQuery {
-        private static final String[] PROJECTION = { VoicemailContract.Voicemails.NUMBER };
-        private static final int NUMBER_COLUMN_INDEX = 0;
+    private static final class DefaultNewCallsQuery implements NewCallsQuery {
+        private static final String[] PROJECTION = {
+            Calls._ID, Calls.NUMBER, Calls.VOICEMAIL_URI
+        };
+        private static final int ID_COLUMN_INDEX = 0;
+        private static final int NUMBER_COLUMN_INDEX = 1;
+        private static final int VOICEMAIL_URI_COLUMN_INDEX = 2;
 
         private final ContentResolver mContentResolver;
 
-        private DefaultVoicemailNumberQuery(ContentResolver contentResolver) {
+        private DefaultNewCallsQuery(ContentResolver contentResolver) {
             mContentResolver = contentResolver;
         }
 
         @Override
-        public String query(Uri uri) {
-            validateVoicemailUri(uri);
+        public NewCall[] query() {
+            final String selection = String.format("%s = 1 AND %s = ?", Calls.NEW, Calls.TYPE);
+            final String[] selectionArgs = new String[]{ Integer.toString(Calls.VOICEMAIL_TYPE) };
             Cursor cursor = null;
             try {
-                cursor = mContentResolver.query(uri, PROJECTION, null, null, null);
-                if (cursor.getCount() != 1) return null;
-                if (!cursor.moveToFirst()) return null;
-                return cursor.getString(NUMBER_COLUMN_INDEX);
-            } finally {
-                if (cursor != null) {
-                    cursor.close();
+                cursor = mContentResolver.query(Calls.CONTENT_URI_WITH_VOICEMAIL, PROJECTION,
+                        selection, selectionArgs, Calls.DEFAULT_SORT_ORDER);
+                NewCall[] newCalls = new NewCall[cursor.getCount()];
+                while (cursor.moveToNext()) {
+                    newCalls[cursor.getPosition()] = createNewCallsFromCursor(cursor);
                 }
+                Log.d(TAG, "DefaultNewCallsQuery: " + newCalls.length + " new calls");
+                return newCalls;
+            } finally {
+                MoreCloseables.closeQuietly(cursor);
             }
         }
 
-        /**
-         * Makes sure that the given URI is a valid voicemail URI.
-         *
-         * @throws IllegalArgumentException if the URI is not valid
-         */
-        private void validateVoicemailUri(Uri uri) {
-            // Cannot be null.
-            if (uri == null) throw new IllegalArgumentException("invalid voicemail URI");
-            // Must have the right schema.
-            if (!VoicemailContract.Voicemails.CONTENT_URI.getScheme().equals(uri.getScheme())) {
-                throw new IllegalArgumentException("invalid voicemail URI");
-            }
-            // Must have the right authority.
-            if (!VoicemailContract.AUTHORITY.equals(uri.getAuthority())) {
-                throw new IllegalArgumentException("invalid voicemail URI");
-            }
-            // Must have a valid path.
-            if (uri.getPath() == null) {
-                throw new IllegalArgumentException("invalid voicemail URI");
-            }
-            // Must be a path within the voicemails table.
-            if (!uri.getPath().startsWith(VoicemailContract.Voicemails.CONTENT_URI.getPath())) {
-                throw new IllegalArgumentException("invalid voicemail URI");
-            }
+        /** Returns an instance of {@link NewCall} created by using the values of the cursor. */
+        private NewCall createNewCallsFromCursor(Cursor cursor) {
+            String voicemailUriString = cursor.getString(VOICEMAIL_URI_COLUMN_INDEX);
+            Uri callsUri = ContentUris.withAppendedId(
+                    Calls.CONTENT_URI_WITH_VOICEMAIL, cursor.getLong(ID_COLUMN_INDEX));
+            Uri voicemailUri = voicemailUriString == null ? null : Uri.parse(voicemailUriString);
+            return new NewCall(callsUri, voicemailUri, cursor.getString(NUMBER_COLUMN_INDEX));
         }
     }
 
@@ -169,6 +278,10 @@
         return new DefaultNameLookupQuery(contentResolver);
     }
 
+    /**
+     * Default implementation of {@link NameLookupQuery} that looks up the name of a contact in the
+     * contacts database.
+     */
     private static final class DefaultNameLookupQuery implements NameLookupQuery {
         private static final String[] PROJECTION = { PhoneLookup.DISPLAY_NAME };
         private static final int DISPLAY_NAME_COLUMN_INDEX = 0;
@@ -195,4 +308,16 @@
             }
         }
     }
+
+    /**
+     * Create a new PhoneNumberHelper.
+     * <p>
+     * This will cause some Disk I/O, at least the first time it is created, so it should not be
+     * called from the main thread.
+     */
+    public static PhoneNumberHelper createPhoneNumberHelper(Context context) {
+        TelephonyManager telephonyManager =
+            (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+        return new PhoneNumberHelper(context.getResources(), telephonyManager.getVoiceMailNumber());
+    }
 }
diff --git a/src/com/android/contacts/calllog/NewVoicemailReceiver.java b/src/com/android/contacts/calllog/NewVoicemailReceiver.java
deleted file mode 100644
index 0b9f2fa..0000000
--- a/src/com/android/contacts/calllog/NewVoicemailReceiver.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2011 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.contacts.calllog;
-
-import android.app.NotificationManager;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-
-/**
- * Receiver for new voicemail notifications.
- * <p>
- * Delegates to a {@link VoicemailNotifier}.
- */
-public class NewVoicemailReceiver extends BroadcastReceiver {
-    @Override
-    public void onReceive(Context context, Intent intent) {
-        getVoicemailNotifier(context).notifyNewVoicemail(intent.getData());
-    }
-
-    private VoicemailNotifier getVoicemailNotifier(Context context) {
-        NotificationManager notificationManager =
-                (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
-        return new DefaultVoicemailNotifier(context, notificationManager,
-                DefaultVoicemailNotifier.createVoicemailNumberQuery(context.getContentResolver()),
-                DefaultVoicemailNotifier.createNameLookupQuery(context.getContentResolver()));
-    }
-}
diff --git a/src/com/android/contacts/calllog/VoicemailNotifier.java b/src/com/android/contacts/calllog/VoicemailNotifier.java
index ba82f21..1ac949d 100644
--- a/src/com/android/contacts/calllog/VoicemailNotifier.java
+++ b/src/com/android/contacts/calllog/VoicemailNotifier.java
@@ -22,14 +22,19 @@
  * Handles notifications for voicemails.
  */
 public interface VoicemailNotifier {
-      /**
-       * Notifies the user of a new voicemail.
-       *
-       * @param newVoicemailUri URI of the new voicemail record just inserted
-       * @throws IllegalArgumentException if the URI does not correspond to a voicemail
-       */
-      public void notifyNewVoicemail(Uri newVoicemailUri);
+    /**
+     * Notifies the user of a new voicemail.
+     *
+     * @param newVoicemailUri URI of the new voicemail record just inserted
+     */
+    public void notifyNewVoicemail(Uri newVoicemailUri);
 
-      /** Clears the new voicemail notification. */
-      public void clearNewVoicemailNotification();
+    /**
+     * Updates the notification and clears it if there are no new voicemails. Called when the phone
+     * just rebooted to put back notifications for anything the user has not acknowledged.
+     */
+    public void updateNotification();
+
+    /** Clears the new voicemail notification. */
+    public void clearNotification();
 }