resolve merge conflicts of 253b129 to nyc-mr1-dev-plus-aosp

Change-Id: I9d37a418982377eb1b94596998e22467d6c86402
diff --git a/src/com/android/server/telecom/CallLogManager.java b/src/com/android/server/telecom/CallLogManager.java
index 80f1da2..64e266d 100755
--- a/src/com/android/server/telecom/CallLogManager.java
+++ b/src/com/android/server/telecom/CallLogManager.java
@@ -172,7 +172,8 @@
                     new LogCallCompletedListener() {
                         @Override
                         public void onLogCompleted(@Nullable Uri uri) {
-                            mMissedCallNotifier.showMissedCallNotification(call);
+                            mMissedCallNotifier.showMissedCallNotification(
+                                    new MissedCallNotifier.CallInfo(call));
                         }
                     });
         } else {
diff --git a/src/com/android/server/telecom/CallsManager.java b/src/com/android/server/telecom/CallsManager.java
index fefca78..5318f16 100644
--- a/src/com/android/server/telecom/CallsManager.java
+++ b/src/com/android/server/telecom/CallsManager.java
@@ -410,7 +410,8 @@
                         result.shouldShowNotification);
             } else if (result.shouldShowNotification) {
                 Log.i(this, "onCallScreeningCompleted: blocked call, showing notification.");
-                mMissedCallNotifier.showMissedCallNotification(incomingCall);
+                mMissedCallNotifier.showMissedCallNotification(
+                        new MissedCallNotifier.CallInfo(incomingCall));
             }
         }
     }
@@ -2112,8 +2113,13 @@
     }
 
     private void reloadMissedCallsOfUser(UserHandle userHandle) {
-        mMissedCallNotifier.reloadFromDatabase(
-                mLock, this, mContactsAsyncHelper, mCallerInfoAsyncQueryFactory, userHandle);
+        mMissedCallNotifier.reloadFromDatabase(mCallerInfoLookupHelper,
+                new MissedCallNotifier.CallInfoFactory(), userHandle);
+    }
+
+    public void onBootCompleted() {
+        mMissedCallNotifier.reloadAfterBootComplete(mCallerInfoLookupHelper,
+                new MissedCallNotifier.CallInfoFactory());
     }
 
     /**
diff --git a/src/com/android/server/telecom/MissedCallNotifier.java b/src/com/android/server/telecom/MissedCallNotifier.java
index 1125a8e..500122b 100644
--- a/src/com/android/server/telecom/MissedCallNotifier.java
+++ b/src/com/android/server/telecom/MissedCallNotifier.java
@@ -16,23 +16,82 @@
 
 package com.android.server.telecom;
 
+import android.net.Uri;
 import android.os.UserHandle;
+import android.telecom.PhoneAccountHandle;
+
+import com.android.internal.telephony.CallerInfo;
 
 /**
  * Creates a notification for calls that the user missed (neither answered nor rejected).
  */
 public interface MissedCallNotifier extends CallsManager.CallsManagerListener {
+    class CallInfoFactory {
+        public CallInfo makeCallInfo(CallerInfo callerInfo, PhoneAccountHandle phoneAccountHandle,
+                Uri handle, long creationTimeMillis) {
+            return new CallInfo(callerInfo, phoneAccountHandle, handle, creationTimeMillis);
+        }
+    }
+
+    class CallInfo {
+        private CallerInfo mCallerInfo;
+        private PhoneAccountHandle mPhoneAccountHandle;
+        private Uri mHandle;
+        private long mCreationTimeMillis;
+
+        public CallInfo(CallerInfo callerInfo, PhoneAccountHandle phoneAccountHandle, Uri handle,
+                long creationTimeMillis) {
+            mCallerInfo = callerInfo;
+            mPhoneAccountHandle = phoneAccountHandle;
+            mHandle = handle;
+            mCreationTimeMillis = creationTimeMillis;
+        }
+
+        public CallInfo(Call call) {
+            mCallerInfo = call.getCallerInfo();
+            mPhoneAccountHandle = call.getTargetPhoneAccount();
+            mHandle = call.getHandle();
+            mCreationTimeMillis = call.getCreationTimeMillis();
+        }
+
+        public CallerInfo getCallerInfo() {
+            return mCallerInfo;
+        }
+
+        public PhoneAccountHandle getPhoneAccountHandle() {
+            return mPhoneAccountHandle;
+        }
+
+        public Uri getHandle() {
+            return mHandle;
+        }
+
+        public String getHandleSchemeSpecificPart() {
+            return mHandle == null ? null : mHandle.getSchemeSpecificPart();
+        }
+
+        public long getCreationTimeMillis() {
+            return mCreationTimeMillis;
+        }
+
+        public String getPhoneNumber() {
+            return mCallerInfo == null ? null : mCallerInfo.phoneNumber;
+        }
+
+        public String getName() {
+            return mCallerInfo == null ? null : mCallerInfo.name;
+        }
+    }
 
     void clearMissedCalls(UserHandle userHandle);
 
-    void showMissedCallNotification(Call call);
+    void showMissedCallNotification(CallInfo call);
 
-    void reloadFromDatabase(
-            TelecomSystem.SyncRoot lock,
-            CallsManager callsManager,
-            ContactsAsyncHelper contactsAsyncHelper,
-            CallerInfoAsyncQueryFactory callerInfoAsyncQueryFactory,
-            UserHandle userHandle);
+    void reloadAfterBootComplete(CallerInfoLookupHelper callerInfoLookupHelper,
+            CallInfoFactory callInfoFactory);
+
+    void reloadFromDatabase(CallerInfoLookupHelper callerInfoLookupHelper,
+            CallInfoFactory callInfoFactory, UserHandle userHandle);
 
     void setCurrentUserHandle(UserHandle userHandle);
 }
diff --git a/src/com/android/server/telecom/TelecomSystem.java b/src/com/android/server/telecom/TelecomSystem.java
index 24d925d..d665a82 100644
--- a/src/com/android/server/telecom/TelecomSystem.java
+++ b/src/com/android/server/telecom/TelecomSystem.java
@@ -38,7 +38,7 @@
 /**
  * Top-level Application class for Telecom.
  */
-public final class TelecomSystem {
+public class TelecomSystem {
 
     /**
      * This interface is implemented by system-instantiated components (e.g., Services and
@@ -68,6 +68,9 @@
     private static final IntentFilter USER_STARTING_FILTER =
             new IntentFilter(Intent.ACTION_USER_STARTING);
 
+    private static final IntentFilter BOOT_COMPLETE_FILTER =
+            new IntentFilter(Intent.ACTION_BOOT_COMPLETED);
+
     /** Intent filter for dialer secret codes. */
     private static final IntentFilter DIALER_SECRET_CODE_FILTER;
 
@@ -102,15 +105,19 @@
     private final ContactsAsyncHelper mContactsAsyncHelper;
     private final DialerCodeReceiver mDialerCodeReceiver;
 
+    private boolean mIsBootComplete = false;
+
     private final BroadcastReceiver mUserSwitchedReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
             Log.startSession("TSSwR.oR");
             try {
-                int userHandleId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
-                UserHandle currentUserHandle = new UserHandle(userHandleId);
-                mPhoneAccountRegistrar.setCurrentUserHandle(currentUserHandle);
-                mCallsManager.onUserSwitch(currentUserHandle);
+                synchronized (mLock) {
+                    int userHandleId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
+                    UserHandle currentUserHandle = new UserHandle(userHandleId);
+                    mPhoneAccountRegistrar.setCurrentUserHandle(currentUserHandle);
+                    mCallsManager.onUserSwitch(currentUserHandle);
+                }
             } finally {
                 Log.endSession();
             }
@@ -122,9 +129,26 @@
         public void onReceive(Context context, Intent intent) {
             Log.startSession("TSStR.oR");
             try {
-                int userHandleId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
-                UserHandle addingUserHandle = new UserHandle(userHandleId);
-                mCallsManager.onUserStarting(addingUserHandle);
+                synchronized (mLock) {
+                    int userHandleId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
+                    UserHandle addingUserHandle = new UserHandle(userHandleId);
+                    mCallsManager.onUserStarting(addingUserHandle);
+                }
+            } finally {
+                Log.endSession();
+            }
+        }
+    };
+
+    private final BroadcastReceiver mBootCompletedReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            Log.startSession("TSBCR.oR");
+            try {
+                synchronized (mLock) {
+                    mIsBootComplete = true;
+                    mCallsManager.onBootCompleted();
+                }
             } finally {
                 Log.endSession();
             }
@@ -137,7 +161,7 @@
 
     public static void setInstance(TelecomSystem instance) {
         if (INSTANCE != null) {
-            throw new RuntimeException("Attempt to set TelecomSystem.INSTANCE twice");
+            Log.w("TelecomSystem", "Attempt to set TelecomSystem.INSTANCE twice");
         }
         Log.i(TelecomSystem.class, "TelecomSystem.INSTANCE being set");
         INSTANCE = instance;
@@ -177,8 +201,7 @@
         SystemStateProvider systemStateProvider = new SystemStateProvider(mContext);
 
         mMissedCallNotifier = missedCallNotifierImplFactory
-                .makeMissedCallNotifierImpl(mContext, mPhoneAccountRegistrar,
-                        phoneNumberUtilsAdapter);
+                .makeMissedCallNotifierImpl(mContext, mPhoneAccountRegistrar);
 
         DefaultDialerManagerAdapter defaultDialerAdapter =
                 new TelecomServiceImpl.DefaultDialerManagerAdapterImpl();
@@ -208,6 +231,7 @@
 
         mContext.registerReceiver(mUserSwitchedReceiver, USER_SWITCHED_FILTER);
         mContext.registerReceiver(mUserStartingReceiver, USER_STARTING_FILTER);
+        mContext.registerReceiver(mBootCompletedReceiver, BOOT_COMPLETE_FILTER);
 
         mBluetoothPhoneServiceImpl = bluetoothPhoneServiceImplFactory.makeBluetoothPhoneServiceImpl(
                 mContext, mLock, mCallsManager, mPhoneAccountRegistrar);
@@ -264,4 +288,8 @@
     public Object getLock() {
         return mLock;
     }
+
+    public boolean isBootComplete() {
+        return mIsBootComplete;
+    }
 }
diff --git a/src/com/android/server/telecom/components/TelecomService.java b/src/com/android/server/telecom/components/TelecomService.java
index f36a892..c7fd9e0 100644
--- a/src/com/android/server/telecom/components/TelecomService.java
+++ b/src/com/android/server/telecom/components/TelecomService.java
@@ -86,10 +86,9 @@
                                 @Override
                                 public MissedCallNotifierImpl makeMissedCallNotifierImpl(
                                         Context context,
-                                        PhoneAccountRegistrar phoneAccountRegistrar,
-                                        PhoneNumberUtilsAdapter phoneNumberUtilsAdapter) {
+                                        PhoneAccountRegistrar phoneAccountRegistrar) {
                                     return new MissedCallNotifierImpl(context,
-                                            phoneAccountRegistrar, phoneNumberUtilsAdapter);
+                                            phoneAccountRegistrar);
                                 }
                             },
                             new CallerInfoAsyncQueryFactory() {
diff --git a/src/com/android/server/telecom/ui/MissedCallNotifierImpl.java b/src/com/android/server/telecom/ui/MissedCallNotifierImpl.java
index 89ef95f..9530f63 100644
--- a/src/com/android/server/telecom/ui/MissedCallNotifierImpl.java
+++ b/src/com/android/server/telecom/ui/MissedCallNotifierImpl.java
@@ -18,19 +18,16 @@
 
 import static android.Manifest.permission.READ_PHONE_STATE;
 
+import android.annotation.NonNull;
 import android.content.ComponentName;
 import android.content.ContentProvider;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
 
-import com.android.server.telecom.Call;
-import com.android.server.telecom.CallState;
-import com.android.server.telecom.CallerInfoAsyncQueryFactory;
-import com.android.server.telecom.CallsManager;
+import com.android.server.telecom.CallerInfoLookupHelper;
 import com.android.server.telecom.CallsManagerListenerBase;
 import com.android.server.telecom.Constants;
-import com.android.server.telecom.ContactsAsyncHelper;
 import com.android.server.telecom.Log;
 import com.android.server.telecom.MissedCallNotifier;
 import com.android.server.telecom.PhoneAccountRegistrar;
@@ -60,7 +57,6 @@
 import android.os.UserHandle;
 import android.provider.CallLog.Calls;
 import android.telecom.DefaultDialerManager;
-import android.telecom.DisconnectCause;
 import android.telecom.PhoneAccount;
 import android.telephony.PhoneNumberUtils;
 import android.telephony.TelephonyManager;
@@ -74,6 +70,8 @@
 import java.lang.String;
 import java.util.List;
 import java.util.Locale;
+import java.util.Objects;
+import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -84,18 +82,12 @@
  * Creates a notification for calls that the user missed (neither answered nor rejected).
  *
  * TODO: Make TelephonyManager.clearMissedCalls call into this class.
- *
- * TODO: Reduce dependencies in this implementation; remove the need to create a new Call
- *     simply to look up caller metadata, and if possible, make it unnecessary to get a
- *     direct reference to the CallsManager. Try to make this class simply handle the UI
- *     and Android-framework entanglements of missed call notification.
  */
 public class MissedCallNotifierImpl extends CallsManagerListenerBase implements MissedCallNotifier {
 
     public interface MissedCallNotifierImplFactory {
         MissedCallNotifier makeMissedCallNotifierImpl(Context context,
-                PhoneAccountRegistrar phoneAccountRegistrar,
-                PhoneNumberUtilsAdapter phoneNumberUtilsAdapter);
+                PhoneAccountRegistrar phoneAccountRegistrar);
     }
 
     public interface NotificationBuilderFactory {
@@ -120,12 +112,16 @@
         Calls.TYPE,
     };
 
-    private static final int CALL_LOG_COLUMN_ID = 0;
-    private static final int CALL_LOG_COLUMN_NUMBER = 1;
-    private static final int CALL_LOG_COLUMN_NUMBER_PRESENTATION = 2;
-    private static final int CALL_LOG_COLUMN_DATE = 3;
-    private static final int CALL_LOG_COLUMN_DURATION = 4;
-    private static final int CALL_LOG_COLUMN_TYPE = 5;
+    private static final String CALL_LOG_WHERE_CLAUSE = "type=" + Calls.MISSED_TYPE +
+            " AND new=1" +
+            " AND is_read=0";
+
+    public static final int CALL_LOG_COLUMN_ID = 0;
+    public static final int CALL_LOG_COLUMN_NUMBER = 1;
+    public static final int CALL_LOG_COLUMN_NUMBER_PRESENTATION = 2;
+    public static final int CALL_LOG_COLUMN_DATE = 3;
+    public static final int CALL_LOG_COLUMN_DURATION = 4;
+    public static final int CALL_LOG_COLUMN_TYPE = 5;
 
     private static final int MISSED_CALL_NOTIFICATION_ID = 1;
 
@@ -134,25 +130,22 @@
     private final NotificationManager mNotificationManager;
     private final NotificationBuilderFactory mNotificationBuilderFactory;
     private final ComponentName mNotificationComponent;
-    private final PhoneNumberUtilsAdapter mPhoneNumberUtilsAdapter;
     private UserHandle mCurrentUserHandle;
 
     // Used to track the number of missed calls.
     private ConcurrentMap<UserHandle, AtomicInteger> mMissedCallCounts;
 
-    public MissedCallNotifierImpl(Context context, PhoneAccountRegistrar phoneAccountRegistrar,
-            PhoneNumberUtilsAdapter phoneNumberUtilsAdapter) {
-        this(context, phoneAccountRegistrar, phoneNumberUtilsAdapter,
-                new DefaultNotificationBuilderFactory());
+    private UserHandle userToLoadAfterBootComplete;
+
+    public MissedCallNotifierImpl(Context context, PhoneAccountRegistrar phoneAccountRegistrar) {
+        this(context, phoneAccountRegistrar, new DefaultNotificationBuilderFactory());
     }
 
     public MissedCallNotifierImpl(Context context,
             PhoneAccountRegistrar phoneAccountRegistrar,
-            PhoneNumberUtilsAdapter phoneNumberUtilsAdapter,
             NotificationBuilderFactory notificationBuilderFactory) {
         mContext = context;
         mPhoneAccountRegistrar = phoneAccountRegistrar;
-        mPhoneNumberUtilsAdapter = phoneNumberUtilsAdapter;
         mNotificationManager =
                 (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
         final String notificationComponent = context.getString(R.string.notification_component);
@@ -206,7 +199,7 @@
      * @param userHandle The user that has the missed call(s).
      * @return {@code true} if the broadcast was sent. {@code false} otherwise.
      */
-    private boolean sendNotificationCustomComponent(Call call, UserHandle userHandle) {
+    private boolean sendNotificationCustomComponent(CallInfo callInfo, UserHandle userHandle) {
         if (mNotificationComponent != null) {
             int count = mMissedCallCounts.get(userHandle).get();
             Intent intent = new Intent();
@@ -215,19 +208,18 @@
             intent.setAction(TelecomManager.ACTION_SHOW_MISSED_CALLS_NOTIFICATION);
             intent.putExtra(TelecomManager.EXTRA_NOTIFICATION_COUNT, count);
             intent.putExtra(TelecomManager.EXTRA_NOTIFICATION_PHONE_NUMBER,
-                    call != null ? call.getPhoneNumber() : null);
+                    callInfo == null ? null : callInfo.getPhoneNumber());
             intent.putExtra(TelecomManager.EXTRA_CLEAR_MISSED_CALLS_INTENT,
                     createClearMissedCallsPendingIntent(userHandle));
 
 
-            if (count == 1 && call != null) {
-                final Uri handleUri = call.getHandle();
-                String handle = handleUri == null ? null : handleUri.getSchemeSpecificPart();
+            if (count == 1 && callInfo != null) {
+                String handle = callInfo.getHandleSchemeSpecificPart();
 
                 if (!TextUtils.isEmpty(handle) && !TextUtils.equals(handle,
                         mContext.getString(R.string.handle_restricted))) {
                     intent.putExtra(TelecomManager.EXTRA_CALL_BACK_INTENT,
-                            createCallBackPendingIntent(handleUri, userHandle));
+                            createCallBackPendingIntent(callInfo.getHandle(), userHandle));
                 }
             }
 
@@ -239,7 +231,8 @@
     }
 
     /**
-     * Returns the missed-call notificatino intent to send to the default dialer for the given user.     * Note, the passed in userHandle is always the non-managed user for SIM calls (multi-user
+     * Returns the missed-call notification intent to send to the default dialer for the given user.
+     * Note, the passed in userHandle is always the non-managed user for SIM calls (multi-user
      * calls). In this case we return the default dialer for the logged in user. This is never the
      * managed (work profile) dialer.
      *
@@ -268,13 +261,13 @@
         return receivers.size() > 0;
     }
 
-    private void sendNotificationThroughDefaultDialer(Call call, UserHandle userHandle) {
+    private void sendNotificationThroughDefaultDialer(CallInfo callInfo, UserHandle userHandle) {
         int count = mMissedCallCounts.get(userHandle).get();
         Intent intent = getShowMissedCallIntentForDefaultDialer(userHandle)
             .setFlags(Intent.FLAG_RECEIVER_FOREGROUND)
             .putExtra(TelecomManager.EXTRA_NOTIFICATION_COUNT, count)
             .putExtra(TelecomManager.EXTRA_NOTIFICATION_PHONE_NUMBER,
-                    call != null ? call.getPhoneNumber() : null);
+                    callInfo == null ? null : callInfo.getPhoneNumber());
 
         Log.w(this, "Showing missed calls through default dialer.");
         mContext.sendBroadcastAsUser(intent, userHandle, READ_PHONE_STATE);
@@ -286,8 +279,8 @@
      * @param call The missed call.
      */
     @Override
-    public void showMissedCallNotification(Call call) {
-        final PhoneAccountHandle phoneAccountHandle = call.getTargetPhoneAccount();
+    public void showMissedCallNotification(@NonNull CallInfo callInfo) {
+        final PhoneAccountHandle phoneAccountHandle = callInfo.getPhoneAccountHandle();
         final PhoneAccount phoneAccount =
                 mPhoneAccountRegistrar.getPhoneAccountUnchecked(phoneAccountHandle);
         UserHandle userHandle;
@@ -297,19 +290,20 @@
         } else {
             userHandle = phoneAccountHandle.getUserHandle();
         }
-        showMissedCallNotification(call, userHandle);
+        showMissedCallNotification(callInfo, userHandle);
     }
 
-    private void showMissedCallNotification(Call call, UserHandle userHandle) {
+    private void showMissedCallNotification(@NonNull CallInfo callInfo, UserHandle userHandle) {
+        Log.i(this, "showMissedCallNotification()");
         mMissedCallCounts.putIfAbsent(userHandle, new AtomicInteger(0));
         int missCallCounts = mMissedCallCounts.get(userHandle).incrementAndGet();
 
-        if (sendNotificationCustomComponent(call, userHandle)) {
+        if (sendNotificationCustomComponent(callInfo, userHandle)) {
             return;
         }
 
         if (shouldManageNotificationThroughDefaultDialer(userHandle)) {
-            sendNotificationThroughDefaultDialer(call, userHandle);
+            sendNotificationThroughDefaultDialer(callInfo, userHandle);
             return;
         }
 
@@ -320,9 +314,9 @@
         // 1 missed call: <caller name || handle>
         // More than 1 missed call: <number of calls> + "missed calls"
         if (missCallCounts == 1) {
-            expandedText = getNameForCall(call);
+            expandedText = getNameForMissedCallNotification(callInfo);
 
-            CallerInfo ci = call.getCallerInfo();
+            CallerInfo ci = callInfo.getCallerInfo();
             if (ci != null && ci.userType == CallerInfo.USER_TYPE_WORK) {
                 titleResId = R.string.notification_missedWorkCallTitle;
             } else {
@@ -341,7 +335,7 @@
         Notification.Builder publicBuilder = mNotificationBuilderFactory.getBuilder(contextForUser);
         publicBuilder.setSmallIcon(android.R.drawable.stat_notify_missed_call)
                 .setColor(mContext.getResources().getColor(R.color.theme_color))
-                .setWhen(call.getCreationTimeMillis())
+                .setWhen(callInfo.getCreationTimeMillis())
                 // Show "Phone" for notification title.
                 .setContentTitle(mContext.getText(R.string.userCallActivityLabel))
                 // Notification details shows that there are missed call(s), but does not reveal
@@ -355,7 +349,7 @@
         Notification.Builder builder = mNotificationBuilderFactory.getBuilder(contextForUser);
         builder.setSmallIcon(android.R.drawable.stat_notify_missed_call)
                 .setColor(mContext.getResources().getColor(R.color.theme_color))
-                .setWhen(call.getCreationTimeMillis())
+                .setWhen(callInfo.getCreationTimeMillis())
                 .setContentTitle(mContext.getText(titleResId))
                 .setContentText(expandedText)
                 .setContentIntent(createCallLogPendingIntent(userHandle))
@@ -366,8 +360,8 @@
                 // sensitive notification information.
                 .setPublicVersion(publicBuilder.build());
 
-        Uri handleUri = call.getHandle();
-        String handle = handleUri == null ? null : handleUri.getSchemeSpecificPart();
+        Uri handleUri = callInfo.getHandle();
+        String handle = callInfo.getHandleSchemeSpecificPart();
 
         // Add additional actions when there is only 1 missed call, like call-back and SMS.
         if (missCallCounts == 1) {
@@ -379,18 +373,20 @@
                         mContext.getString(R.string.notification_missedCall_call_back),
                         createCallBackPendingIntent(handleUri, userHandle));
 
-                if (canRespondViaSms(call)) {
+                if (canRespondViaSms(callInfo)) {
                     builder.addAction(R.drawable.ic_message_24dp,
                             mContext.getString(R.string.notification_missedCall_message),
                             createSendSmsFromNotificationPendingIntent(handleUri, userHandle));
                 }
             }
 
-            Bitmap photoIcon = call.getPhotoIcon();
+            Bitmap photoIcon = callInfo.getCallerInfo() == null ?
+                    null : callInfo.getCallerInfo().cachedPhotoIcon;
             if (photoIcon != null) {
                 builder.setLargeIcon(photoIcon);
             } else {
-                Drawable photo = call.getPhoto();
+                Drawable photo = callInfo.getCallerInfo() == null ?
+                        null : callInfo.getCallerInfo().cachedPhoto;
                 if (photo != null && photo instanceof BitmapDrawable) {
                     builder.setLargeIcon(((BitmapDrawable) photo).getBitmap());
                 }
@@ -403,7 +399,7 @@
         Notification notification = builder.build();
         configureLedOnNotification(notification);
 
-        Log.i(this, "Adding missed call notification for %s.", call);
+        Log.i(this, "Adding missed call notification for %s.", Log.pii(callInfo.getHandle()));
         long token = Binder.clearCallingIdentity();
         try {
             mNotificationManager.notifyAsUser(
@@ -440,9 +436,9 @@
     /**
      * Returns the name to use in the missed call notification.
      */
-    private String getNameForCall(Call call) {
-        String handle = call.getHandle() == null ? null : call.getHandle().getSchemeSpecificPart();
-        String name = call.getName();
+    private String getNameForMissedCallNotification(@NonNull CallInfo callInfo) {
+        String handle = callInfo.getHandleSchemeSpecificPart();
+        String name = callInfo.getName();
 
         if (!TextUtils.isEmpty(handle)) {
             String formattedNumber = PhoneNumberUtils.formatNumber(handle,
@@ -559,23 +555,34 @@
         notification.defaults |= Notification.DEFAULT_LIGHTS;
     }
 
-    private boolean canRespondViaSms(Call call) {
+    private boolean canRespondViaSms(@NonNull CallInfo callInfo) {
         // Only allow respond-via-sms for "tel:" calls.
-        return call.getHandle() != null &&
-                PhoneAccount.SCHEME_TEL.equals(call.getHandle().getScheme());
+        return callInfo.getHandle() != null &&
+                PhoneAccount.SCHEME_TEL.equals(callInfo.getHandle().getScheme());
     }
 
+    @Override
+    public void reloadAfterBootComplete(final CallerInfoLookupHelper callerInfoLookupHelper,
+            CallInfoFactory callInfoFactory) {
+        if (userToLoadAfterBootComplete != null) {
+            reloadFromDatabase(callerInfoLookupHelper,
+                    callInfoFactory, userToLoadAfterBootComplete);
+            userToLoadAfterBootComplete = null;
+        }
+    }
     /**
      * Adds the missed call notification on startup if there are unread missed calls.
      */
     @Override
-    public void reloadFromDatabase(
-            final TelecomSystem.SyncRoot lock,
-            final CallsManager callsManager,
-            final ContactsAsyncHelper contactsAsyncHelper,
-            final CallerInfoAsyncQueryFactory callerInfoAsyncQueryFactory,
-            final UserHandle userHandle) {
+    public void reloadFromDatabase(final CallerInfoLookupHelper callerInfoLookupHelper,
+            CallInfoFactory callInfoFactory, final UserHandle userHandle) {
         Log.d(this, "reloadFromDatabase()...");
+        if (TelecomSystem.getInstance() == null || !TelecomSystem.getInstance().isBootComplete()) {
+            Log.i(this, "Boot not yet complete -- call log db may not be available. Deferring " +
+                    "loading until boot complete.");
+            userToLoadAfterBootComplete = userHandle;
+            return;
+        }
 
         // instantiate query handler
         AsyncQueryHandler queryHandler = new AsyncQueryHandler(mContext.getContentResolver()) {
@@ -602,35 +609,40 @@
                                                 handleString, null);
                             }
 
-                            synchronized (lock) {
+                            callerInfoLookupHelper.startLookup(handle,
+                                    new CallerInfoLookupHelper.OnQueryCompleteListener() {
+                                        @Override
+                                        public void onCallerInfoQueryComplete(Uri queryHandle,
+                                                CallerInfo info) {
+                                            if (!Objects.equals(queryHandle, handle)) {
+                                                Log.w(MissedCallNotifierImpl.this,
+                                                        "CallerInfo query returned with " +
+                                                                "different handle.");
+                                                return;
+                                            }
+                                            if (info.contactDisplayPhotoUri == null) {
+                                                // If there is no photo, just show the notification.
+                                                CallInfo callInfo = callInfoFactory.makeCallInfo(
+                                                        info, null, handle, date);
+                                                showMissedCallNotification(callInfo, userHandle);
+                                            }
+                                        }
 
-                                // Convert the data to a call object
-                                Call call = new Call(Call.CALL_ID_UNKNOWN, mContext, callsManager,
-                                        lock, null, contactsAsyncHelper,
-                                        callerInfoAsyncQueryFactory, mPhoneNumberUtilsAdapter, null,
-                                        null, null, null, Call.CALL_DIRECTION_INCOMING, false,
-                                        false);
-                                call.setDisconnectCause(
-                                        new DisconnectCause(DisconnectCause.MISSED));
-                                call.setState(CallState.DISCONNECTED, "throw away call");
-                                call.setCreationTimeMillis(date);
-
-                                // 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, userHandle);
+                                        @Override
+                                        public void onContactPhotoQueryComplete(Uri queryHandle,
+                                                CallerInfo info) {
+                                            if (!Objects.equals(queryHandle, handle)) {
+                                                Log.w(MissedCallNotifierImpl.this,
+                                                        "CallerInfo query for photo returned " +
+                                                                "with different handle.");
+                                                return;
+                                            }
+                                            CallInfo callInfo = callInfoFactory.makeCallInfo(
+                                                    info, null, handle, date);
+                                            showMissedCallNotification(callInfo, userHandle);
+                                        }
                                     }
-                                });
-                                // Set the handle here because that is what triggers the contact
-                                // info query.
-                                call.setHandle(handle, presentation);
-                            }
+                            );
                         }
                     } finally {
                         cursor.close();
@@ -640,16 +652,11 @@
         };
 
         // 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");
-        where.append(" AND is_read=0");
-
         Uri callsUri =
                 ContentProvider.maybeAddUserId(Calls.CONTENT_URI, userHandle.getIdentifier());
         // start the query
         queryHandler.startQuery(0, null, callsUri, CALL_LOG_PROJECTION,
-                where.toString(), null, Calls.DEFAULT_SORT_ORDER);
+                CALL_LOG_WHERE_CLAUSE, null, Calls.DEFAULT_SORT_ORDER);
     }
 
     @Override
diff --git a/tests/src/com/android/server/telecom/tests/BasicCallTests.java b/tests/src/com/android/server/telecom/tests/BasicCallTests.java
index 6688ca0..1c096f9 100644
--- a/tests/src/com/android/server/telecom/tests/BasicCallTests.java
+++ b/tests/src/com/android/server/telecom/tests/BasicCallTests.java
@@ -595,7 +595,8 @@
 
         // Attempt to pull the call and verify the API call makes it through
         mInCallServiceFixtureX.mInCallAdapter.pullExternalCall(ids.mCallId);
-        verify(mConnectionServiceFixtureA.getTestDouble(), timeout(TEST_TIMEOUT).never())
+        Thread.sleep(TEST_TIMEOUT);
+        verify(mConnectionServiceFixtureA.getTestDouble(), never())
                 .pullExternalCall(ids.mCallId);
     }
 
@@ -790,7 +791,8 @@
 
         // Attempt to pull the call and verify the API call makes it through
         mInCallServiceFixtureX.mInCallAdapter.pullExternalCall(ids.mCallId);
-        verify(mConnectionServiceFixtureA.getTestDouble(), timeout(TEST_TIMEOUT).never())
+        Thread.sleep(TEST_TIMEOUT);
+        verify(mConnectionServiceFixtureA.getTestDouble(), never())
                 .pullExternalCall(ids.mConnectionId);
     }
 
diff --git a/tests/src/com/android/server/telecom/tests/CallLogManagerTest.java b/tests/src/com/android/server/telecom/tests/CallLogManagerTest.java
index e9db543..590304c 100644
--- a/tests/src/com/android/server/telecom/tests/CallLogManagerTest.java
+++ b/tests/src/com/android/server/telecom/tests/CallLogManagerTest.java
@@ -296,7 +296,7 @@
                 Integer.valueOf(CallLog.Calls.MISSED_TYPE));
         // Timeout needed because showMissedCallNotification is called from onPostExecute.
         verify(mMissedCallNotifier, timeout(TEST_TIMEOUT_MILLIS))
-                .showMissedCallNotification(fakeMissedCall);
+                .showMissedCallNotification(any(MissedCallNotifier.CallInfo.class));
     }
 
     @MediumTest
diff --git a/tests/src/com/android/server/telecom/tests/ContactsAsyncHelperTest.java b/tests/src/com/android/server/telecom/tests/ContactsAsyncHelperTest.java
index 363d613..960a170 100644
--- a/tests/src/com/android/server/telecom/tests/ContactsAsyncHelperTest.java
+++ b/tests/src/com/android/server/telecom/tests/ContactsAsyncHelperTest.java
@@ -35,6 +35,7 @@
 import static org.mockito.Matchers.anyObject;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Matchers.isNull;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.verify;
@@ -87,14 +88,15 @@
     }
 
     @SmallTest
-    public void testEmptyUri() {
+    public void testEmptyUri() throws Exception {
         ContactsAsyncHelper cah = new ContactsAsyncHelper(mNullContentResolverAdapter);
         try {
             cah.startObtainPhotoAsync(TOKEN, mContext, null, mListener, COOKIE);
         } catch (IllegalStateException e) {
             // expected to fail
         }
-        verify(mListener, timeout(TEST_TIMEOUT).never()).onImageLoadComplete(anyInt(),
+        Thread.sleep(TEST_TIMEOUT);
+        verify(mListener, never()).onImageLoadComplete(anyInt(),
                 any(Drawable.class), any(Bitmap.class), anyObject());
     }
 
diff --git a/tests/src/com/android/server/telecom/tests/MissedCallNotifierImplTest.java b/tests/src/com/android/server/telecom/tests/MissedCallNotifierImplTest.java
index 24c59ff..2663356 100644
--- a/tests/src/com/android/server/telecom/tests/MissedCallNotifierImplTest.java
+++ b/tests/src/com/android/server/telecom/tests/MissedCallNotifierImplTest.java
@@ -20,24 +20,32 @@
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.content.ComponentName;
+import android.content.ContentProvider;
 import android.content.Context;
+import android.content.IContentProvider;
 import android.content.Intent;
 import android.content.pm.ApplicationInfo;
+import android.database.Cursor;
 import android.net.Uri;
+import android.os.Handler;
+import android.os.ICancellationSignal;
+import android.os.Looper;
 import android.os.UserHandle;
+import android.provider.CallLog;
 import android.telecom.PhoneAccount;
-import android.telecom.PhoneAccount.Builder;
 import android.telecom.PhoneAccountHandle;
 import android.telecom.TelecomManager;
 import android.telephony.TelephonyManager;
 import android.test.suitebuilder.annotation.SmallTest;
 
-import com.android.server.telecom.Call;
+import com.android.internal.telephony.CallerInfo;
+import com.android.server.telecom.CallerInfoLookupHelper;
 import com.android.server.telecom.Constants;
 import com.android.server.telecom.MissedCallNotifier;
 import com.android.server.telecom.PhoneAccountRegistrar;
 import com.android.server.telecom.PhoneNumberUtilsAdapterImpl;
 import com.android.server.telecom.TelecomBroadcastIntentProcessor;
+import com.android.server.telecom.TelecomSystem;
 import com.android.server.telecom.components.TelecomBroadcastReceiver;
 import com.android.server.telecom.ui.MissedCallNotifierImpl;
 import com.android.server.telecom.ui.MissedCallNotifierImpl.NotificationBuilderFactory;
@@ -48,20 +56,65 @@
 
 import java.util.Arrays;
 import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
 
 import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
 import static org.mockito.Matchers.isNull;
 import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 public class MissedCallNotifierImplTest extends TelecomTestCase {
 
+    private static class MockMissedCallCursorBuilder {
+        private class CallLogRow {
+            String number;
+            int presentation;
+            long date;
+
+            public CallLogRow(String number, int presentation, long date) {
+                this.number = number;
+                this.presentation = presentation;
+                this.date = date;
+            }
+        }
+
+        private List<CallLogRow> mRows;
+
+        MockMissedCallCursorBuilder() {
+            mRows = new LinkedList<>();
+            mRows.add(null);
+        }
+
+        public MockMissedCallCursorBuilder addEntry(String number, int presentation, long date) {
+            mRows.add(new CallLogRow(number, presentation, date));
+            return this;
+        }
+
+        public Cursor build() {
+            Cursor c = mock(Cursor.class);
+            when(c.moveToNext()).thenAnswer(unused -> {
+                mRows.remove(0);
+                return mRows.size() > 0;
+            });
+            when(c.getString(MissedCallNotifierImpl.CALL_LOG_COLUMN_NUMBER))
+                    .thenAnswer(unused -> mRows.get(0).number);
+            when(c.getInt(MissedCallNotifierImpl.CALL_LOG_COLUMN_NUMBER_PRESENTATION))
+                    .thenAnswer(unused -> mRows.get(0).presentation);
+            when(c.getLong(MissedCallNotifierImpl.CALL_LOG_COLUMN_DATE))
+                    .thenAnswer(unused -> mRows.get(0).date);
+            return c;
+        }
+    }
+
     private static final Uri TEL_CALL_HANDLE = Uri.parse("tel:+11915552620");
     private static final Uri SIP_CALL_HANDLE = Uri.parse("sip:testaddress@testdomain.com");
     private static final String CALLER_NAME = "Fake Name";
@@ -79,6 +132,7 @@
     private static final UserHandle PRIMARY_USER = UserHandle.of(0);
     private static final UserHandle SECONARY_USER = UserHandle.of(12);
     private static final int NO_CAPABILITY = 0;
+    private static final int TEST_TIMEOUT = 1000;
 
     @Mock
     private NotificationManager mNotificationManager;
@@ -89,6 +143,8 @@
     @Mock
     private TelecomManager mTelecomManager;
 
+    @Mock TelecomSystem mTelecomSystem;
+
     @Override
     public void setUp() throws Exception {
         super.setUp();
@@ -114,6 +170,12 @@
         mComponentContextFixture.setTelecomManager(mTelecomManager);
     }
 
+    @Override
+    public void tearDown() throws Exception {
+        TelecomSystem.setInstance(null);
+        when(mTelecomSystem.isBootComplete()).thenReturn(false);
+    }
+
     @SmallTest
     public void testCancelNotificationInPrimaryUser() {
         cancelNotificationTestInternal(PRIMARY_USER);
@@ -133,8 +195,8 @@
         MissedCallNotifier missedCallNotifier = makeMissedCallNotifier(fakeBuilderFactory,
                 PRIMARY_USER);
         PhoneAccount phoneAccount = makePhoneAccount(userHandle, NO_CAPABILITY);
-        Call fakeCall = makeFakeCall(TEL_CALL_HANDLE, CALLER_NAME, CALL_TIMESTAMP,
-                phoneAccount.getAccountHandle());
+        MissedCallNotifier.CallInfo fakeCall = makeFakeCallInfo(TEL_CALL_HANDLE, CALLER_NAME,
+                CALL_TIMESTAMP, phoneAccount.getAccountHandle());
 
         missedCallNotifier.showMissedCallNotification(fakeCall);
         missedCallNotifier.clearMissedCalls(userHandle);
@@ -160,14 +222,14 @@
         }
 
         PhoneAccount phoneAccount = makePhoneAccount(PRIMARY_USER, NO_CAPABILITY);
-        Call fakeCall = makeFakeCall(TEL_CALL_HANDLE, CALLER_NAME, CALL_TIMESTAMP,
-                phoneAccount.getAccountHandle());
+        MissedCallNotifier.CallInfo fakeCall = makeFakeCallInfo(TEL_CALL_HANDLE, CALLER_NAME,
+                CALL_TIMESTAMP, phoneAccount.getAccountHandle());
 
         MissedCallNotifierImpl.NotificationBuilderFactory fakeBuilderFactory =
                 makeNotificationBuilderFactory(builders);
 
         MissedCallNotifier missedCallNotifier = new MissedCallNotifierImpl(mContext,
-                mPhoneAccountRegistrar, new PhoneNumberUtilsAdapterImpl(), fakeBuilderFactory);
+                mPhoneAccountRegistrar, fakeBuilderFactory);
 
         missedCallNotifier.showMissedCallNotification(fakeCall);
         missedCallNotifier.showMissedCallNotification(fakeCall);
@@ -254,8 +316,8 @@
         MissedCallNotifier missedCallNotifier = makeMissedCallNotifier(fakeBuilderFactory,
                 currentUser);
 
-        Call fakeCall = makeFakeCall(TEL_CALL_HANDLE, CALLER_NAME, CALL_TIMESTAMP,
-                phoneAccount.getAccountHandle());
+        MissedCallNotifier.CallInfo fakeCall = makeFakeCallInfo(TEL_CALL_HANDLE, CALLER_NAME,
+                CALL_TIMESTAMP, phoneAccount.getAccountHandle());
         missedCallNotifier.showMissedCallNotification(fakeCall);
 
         ArgumentCaptor<Notification> notificationArgumentCaptor = ArgumentCaptor.forClass(
@@ -316,11 +378,11 @@
                 makeNotificationBuilderFactory(builder1);
 
         MissedCallNotifier missedCallNotifier = new MissedCallNotifierImpl(mContext,
-                mPhoneAccountRegistrar, new PhoneNumberUtilsAdapterImpl(), fakeBuilderFactory);
+                mPhoneAccountRegistrar, fakeBuilderFactory);
         PhoneAccount phoneAccount = makePhoneAccount(PRIMARY_USER, NO_CAPABILITY);
 
-        Call fakeCall =
-                makeFakeCall(SIP_CALL_HANDLE, CALLER_NAME, CALL_TIMESTAMP,
+        MissedCallNotifier.CallInfo fakeCall =
+                makeFakeCallInfo(SIP_CALL_HANDLE, CALLER_NAME, CALL_TIMESTAMP,
                 phoneAccount.getAccountHandle());
         missedCallNotifier.showMissedCallNotification(fakeCall);
 
@@ -344,6 +406,135 @@
                 smsIntent, PendingIntent.FLAG_NO_CREATE));
     }
 
+    @SmallTest
+    public void testLoadOneCallFromDb() throws Exception {
+        CallerInfoLookupHelper mockCallerInfoLookupHelper = mock(CallerInfoLookupHelper.class);
+        MissedCallNotifier.CallInfoFactory mockCallInfoFactory =
+                mock(MissedCallNotifier.CallInfoFactory.class);
+
+        Uri queryUri = ContentProvider.maybeAddUserId(CallLog.Calls.CONTENT_URI,
+                PRIMARY_USER.getIdentifier());
+        IContentProvider cp = getContentProviderForUser(PRIMARY_USER.getIdentifier());
+
+        Cursor mockMissedCallsCursor = new MockMissedCallCursorBuilder()
+                .addEntry(TEL_CALL_HANDLE.getSchemeSpecificPart(),
+                        CallLog.Calls.PRESENTATION_ALLOWED, CALL_TIMESTAMP)
+                .build();
+
+        when(cp.query(anyString(), eq(queryUri), any(String[].class), anyString(), any
+                (String[].class), anyString(), any(ICancellationSignal.class)))
+                .thenReturn(mockMissedCallsCursor);
+
+        PhoneAccount phoneAccount = makePhoneAccount(PRIMARY_USER, NO_CAPABILITY);
+        MissedCallNotifier.CallInfo fakeCallInfo = makeFakeCallInfo(TEL_CALL_HANDLE,
+                CALLER_NAME, CALL_TIMESTAMP, phoneAccount.getAccountHandle());
+        when(mockCallInfoFactory.makeCallInfo(any(CallerInfo.class),
+                any(PhoneAccountHandle.class), any(Uri.class), eq(CALL_TIMESTAMP)))
+                .thenReturn(fakeCallInfo);
+
+        Notification.Builder builder1 = makeNotificationBuilder("builder1");
+        MissedCallNotifierImpl.NotificationBuilderFactory fakeBuilderFactory =
+                makeNotificationBuilderFactory(builder1);
+
+        MissedCallNotifier missedCallNotifier = new MissedCallNotifierImpl(mContext,
+                mPhoneAccountRegistrar, fakeBuilderFactory);
+
+        // AsyncQueryHandler used in reloadFromDatabase interacts poorly with the below
+        // timeout-verify, so run this in a new handler to mitigate that.
+        Handler h = new Handler(Looper.getMainLooper());
+        h.post(() -> missedCallNotifier.reloadFromDatabase(
+                mockCallerInfoLookupHelper, mockCallInfoFactory, PRIMARY_USER));
+        waitForHandlerAction(h, TEST_TIMEOUT);
+
+        // TelecomSystem.getInstance returns null in this test, so we expect that nothing will
+        // happen.
+        verify(mockCallerInfoLookupHelper, never()).startLookup(any(Uri.class),
+                any(CallerInfoLookupHelper.OnQueryCompleteListener.class));
+        // Simulate a boot-complete
+        TelecomSystem.setInstance(mTelecomSystem);
+        when(mTelecomSystem.isBootComplete()).thenReturn(true);
+        h.post(() -> missedCallNotifier.reloadAfterBootComplete(mockCallerInfoLookupHelper,
+                mockCallInfoFactory));
+        waitForHandlerAction(h, TEST_TIMEOUT);
+
+        Uri escapedHandle = Uri.fromParts(PhoneAccount.SCHEME_TEL,
+                TEL_CALL_HANDLE.getSchemeSpecificPart(), null);
+        ArgumentCaptor<CallerInfoLookupHelper.OnQueryCompleteListener> listenerCaptor =
+                ArgumentCaptor.forClass(CallerInfoLookupHelper.OnQueryCompleteListener.class);
+        verify(mockCallerInfoLookupHelper, timeout(TEST_TIMEOUT)).startLookup(eq(escapedHandle),
+                listenerCaptor.capture());
+
+        CallerInfo ci = new CallerInfo();
+        listenerCaptor.getValue().onCallerInfoQueryComplete(escapedHandle, ci);
+        verify(mockCallInfoFactory).makeCallInfo(eq(ci), isNull(PhoneAccountHandle.class),
+                eq(escapedHandle), eq(CALL_TIMESTAMP));
+    }
+
+    @SmallTest
+    public void testLoadTwoCallsFromDb() throws Exception {
+        TelecomSystem.setInstance(mTelecomSystem);
+        when(mTelecomSystem.isBootComplete()).thenReturn(true);
+        CallerInfoLookupHelper mockCallerInfoLookupHelper = mock(CallerInfoLookupHelper.class);
+        MissedCallNotifier.CallInfoFactory mockCallInfoFactory =
+                mock(MissedCallNotifier.CallInfoFactory.class);
+
+        Cursor mockMissedCallsCursor = new MockMissedCallCursorBuilder()
+                .addEntry(TEL_CALL_HANDLE.getSchemeSpecificPart(),
+                        CallLog.Calls.PRESENTATION_ALLOWED, CALL_TIMESTAMP)
+                .addEntry(SIP_CALL_HANDLE.getSchemeSpecificPart(),
+                        CallLog.Calls.PRESENTATION_ALLOWED, CALL_TIMESTAMP)
+                .build();
+
+        Uri queryUri = ContentProvider.maybeAddUserId(CallLog.Calls.CONTENT_URI,
+                PRIMARY_USER.getIdentifier());
+        IContentProvider cp = getContentProviderForUser(PRIMARY_USER.getIdentifier());
+
+        when(cp.query(anyString(), eq(queryUri), any(String[].class), anyString(), any
+                (String[].class), anyString(), any(ICancellationSignal.class)))
+                .thenReturn(mockMissedCallsCursor);
+
+        PhoneAccount phoneAccount = makePhoneAccount(PRIMARY_USER, NO_CAPABILITY);
+        MissedCallNotifier.CallInfo fakeCallInfo = makeFakeCallInfo(TEL_CALL_HANDLE,
+                CALLER_NAME, CALL_TIMESTAMP, phoneAccount.getAccountHandle());
+        when(mockCallInfoFactory.makeCallInfo(any(CallerInfo.class),
+                any(PhoneAccountHandle.class), any(Uri.class), eq(CALL_TIMESTAMP)))
+                .thenReturn(fakeCallInfo);
+
+        Notification.Builder builder1 = makeNotificationBuilder("builder1");
+        MissedCallNotifierImpl.NotificationBuilderFactory fakeBuilderFactory =
+                makeNotificationBuilderFactory(builder1);
+
+        MissedCallNotifier missedCallNotifier = new MissedCallNotifierImpl(mContext,
+                mPhoneAccountRegistrar, fakeBuilderFactory);
+
+        // AsyncQueryHandler used in reloadFromDatabase interacts poorly with the below
+        // timeout-verify, so run this in a new handler to mitigate that.
+        Handler h = new Handler(Looper.getMainLooper());
+        h.post(() -> missedCallNotifier.reloadFromDatabase(
+                mockCallerInfoLookupHelper, mockCallInfoFactory, PRIMARY_USER));
+        waitForHandlerAction(h, TEST_TIMEOUT);
+
+        Uri escapedTelHandle = Uri.fromParts(PhoneAccount.SCHEME_TEL,
+                TEL_CALL_HANDLE.getSchemeSpecificPart(), null);
+        Uri escapedSipHandle = Uri.fromParts(PhoneAccount.SCHEME_SIP,
+                SIP_CALL_HANDLE.getSchemeSpecificPart(), null);
+
+        ArgumentCaptor<CallerInfoLookupHelper.OnQueryCompleteListener> listenerCaptor =
+                ArgumentCaptor.forClass(CallerInfoLookupHelper.OnQueryCompleteListener.class);
+        verify(mockCallerInfoLookupHelper, timeout(TEST_TIMEOUT)).startLookup(eq(escapedTelHandle),
+                listenerCaptor.capture());
+        verify(mockCallerInfoLookupHelper, timeout(TEST_TIMEOUT)).startLookup(eq(escapedSipHandle),
+                listenerCaptor.capture());
+
+        CallerInfo ci = new CallerInfo();
+        listenerCaptor.getAllValues().get(0).onCallerInfoQueryComplete(escapedTelHandle, ci);
+        listenerCaptor.getAllValues().get(1).onCallerInfoQueryComplete(escapedSipHandle, ci);
+
+        // Verify that two notifications were generated, both with the same id.
+        verify(mNotificationManager, times(2)).notifyAsUser(isNull(String.class), eq(1),
+                any(Notification.class), eq(PRIMARY_USER));
+    }
+
     private Notification.Builder makeNotificationBuilder(String label) {
         Notification.Builder builder = spy(new Notification.Builder(mContext));
         Notification notification = mock(Notification.class);
@@ -353,13 +544,14 @@
         return builder;
     }
 
-    private Call makeFakeCall(Uri handle, String name, long timestamp,
+    private MissedCallNotifier.CallInfo makeFakeCallInfo(Uri handle, String name, long timestamp,
             PhoneAccountHandle phoneAccountHandle) {
-        Call fakeCall = mock(Call.class);
+        MissedCallNotifier.CallInfo fakeCall = mock(MissedCallNotifier.CallInfo.class);
         when(fakeCall.getHandle()).thenReturn(handle);
+        when(fakeCall.getHandleSchemeSpecificPart()).thenReturn(handle.getSchemeSpecificPart());
         when(fakeCall.getName()).thenReturn(name);
         when(fakeCall.getCreationTimeMillis()).thenReturn(timestamp);
-        when(fakeCall.getTargetPhoneAccount()).thenReturn(phoneAccountHandle);
+        when(fakeCall.getPhoneAccountHandle()).thenReturn(phoneAccountHandle);
         return fakeCall;
     }
 
@@ -375,7 +567,7 @@
     private MissedCallNotifier makeMissedCallNotifier(
             NotificationBuilderFactory fakeBuilderFactory, UserHandle currentUser) {
         MissedCallNotifier missedCallNotifier = new MissedCallNotifierImpl(mContext,
-                mPhoneAccountRegistrar, new PhoneNumberUtilsAdapterImpl(), fakeBuilderFactory);
+                mPhoneAccountRegistrar, fakeBuilderFactory);
         missedCallNotifier.setCurrentUserHandle(currentUser);
         return missedCallNotifier;
     }
@@ -391,4 +583,9 @@
                 .thenReturn(phoneAccount);
         return phoneAccount;
     }
+
+    private IContentProvider getContentProviderForUser(int userId) {
+        return mContext.getContentResolver().acquireProvider(userId + "@call_log");
+    }
+
 }
diff --git a/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java b/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
index 68eb0ed..d098eb9 100644
--- a/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
+++ b/tests/src/com/android/server/telecom/tests/TelecomSystemTest.java
@@ -64,6 +64,7 @@
 import com.android.server.telecom.CallAudioManager;
 import com.android.server.telecom.CallAudioRouteStateMachine;
 import com.android.server.telecom.CallerInfoAsyncQueryFactory;
+import com.android.server.telecom.CallerInfoLookupHelper;
 import com.android.server.telecom.CallsManager;
 import com.android.server.telecom.CallsManagerListenerBase;
 import com.android.server.telecom.ContactsAsyncHelper;
@@ -129,7 +130,7 @@
 
     public static class MissedCallNotifierFakeImpl extends CallsManagerListenerBase
             implements MissedCallNotifier {
-        List<com.android.server.telecom.Call> missedCallsNotified = new ArrayList<>();
+        List<CallInfo> missedCallsNotified = new ArrayList<>();
 
         @Override
         public void clearMissedCalls(UserHandle userHandle) {
@@ -137,16 +138,17 @@
         }
 
         @Override
-        public void showMissedCallNotification(com.android.server.telecom.Call call) {
+        public void showMissedCallNotification(CallInfo call) {
             missedCallsNotified.add(call);
         }
 
         @Override
-        public void reloadFromDatabase(TelecomSystem.SyncRoot lock, CallsManager callsManager,
-                ContactsAsyncHelper contactsAsyncHelper,
-                CallerInfoAsyncQueryFactory callerInfoAsyncQueryFactory, UserHandle userHandle) {
+        public void reloadAfterBootComplete(CallerInfoLookupHelper callerInfoLookupHelper,
+                CallInfoFactory callInfoFactory) { }
 
-        }
+        @Override
+        public void reloadFromDatabase(CallerInfoLookupHelper callerInfoLookupHelper,
+                CallInfoFactory callInfoFactory, UserHandle userHandle) { }
 
         @Override
         public void setCurrentUserHandle(UserHandle userHandle) {
@@ -376,8 +378,7 @@
                 new MissedCallNotifierImplFactory() {
                     @Override
                     public MissedCallNotifier makeMissedCallNotifierImpl(Context context,
-                            PhoneAccountRegistrar phoneAccountRegistrar,
-                            PhoneNumberUtilsAdapter phoneNumberUtilsAdapter) {
+                            PhoneAccountRegistrar phoneAccountRegistrar) {
                         return mMissedCallNotifier;
                     }
                 },