Merge "Add location information with business caller ID." into ub-contactsdialer-a-dev
diff --git a/InCallUI/src/com/android/incallui/Call.java b/InCallUI/src/com/android/incallui/Call.java
index e14db9b..d1cb623 100644
--- a/InCallUI/src/com/android/incallui/Call.java
+++ b/InCallUI/src/com/android/incallui/Call.java
@@ -20,6 +20,7 @@
 import com.android.contacts.common.testing.NeededForTesting;
 import com.android.dialer.util.IntentUtil;
 import com.android.incallui.Call.LogState;
+import com.android.incallui.util.TelecomCallUtil;
 
 import android.content.Context;
 import android.hardware.camera2.CameraCharacteristics;
@@ -522,14 +523,7 @@
     }
 
     public String getNumber() {
-        if (mTelecomCall == null) {
-            return null;
-        }
-        if (mTelecomCall.getDetails().getGatewayInfo() != null) {
-            return mTelecomCall.getDetails().getGatewayInfo()
-                    .getOriginalAddress().getSchemeSpecificPart();
-        }
-        return getHandle() == null ? null : getHandle().getSchemeSpecificPart();
+        return TelecomCallUtil.getNumber(mTelecomCall);
     }
 
     public void blockCall() {
@@ -738,9 +732,7 @@
      * repeated calls to isEmergencyNumber.
      */
     private void updateEmergencyCallState() {
-        Uri handle = mTelecomCall.getDetails().getHandle();
-        mIsEmergencyCall = PhoneNumberUtils.isEmergencyNumber(
-                handle == null ? "" : handle.getSchemeSpecificPart());
+        mIsEmergencyCall = TelecomCallUtil.isEmergencyCall(mTelecomCall);
     }
 
     private void setModifyToVideoState(int newVideoState) {
diff --git a/InCallUI/src/com/android/incallui/CallCardPresenter.java b/InCallUI/src/com/android/incallui/CallCardPresenter.java
index 67747b8..9c47a93 100644
--- a/InCallUI/src/com/android/incallui/CallCardPresenter.java
+++ b/InCallUI/src/com/android/incallui/CallCardPresenter.java
@@ -537,7 +537,15 @@
     }
 
     private void onContactInfoComplete(String callId, ContactCacheEntry entry, boolean isPrimary) {
-        updateContactEntry(entry, isPrimary);
+        final boolean entryMatchesExistingCall =
+                (isPrimary && mPrimary != null && TextUtils.equals(callId,  mPrimary.getId())) ||
+                (!isPrimary && mSecondary != null && TextUtils.equals(callId, mSecondary.getId()));
+        if (entryMatchesExistingCall) {
+            updateContactEntry(entry, isPrimary);
+        } else {
+            Log.w(this, "Dropping stale contact lookup info for " + callId);
+        }
+
         if (entry.name != null) {
             Log.d(TAG, "Contact found: " + entry);
         }
diff --git a/InCallUI/src/com/android/incallui/CallList.java b/InCallUI/src/com/android/incallui/CallList.java
index 4f9e0af..bed1d60 100644
--- a/InCallUI/src/com/android/incallui/CallList.java
+++ b/InCallUI/src/com/android/incallui/CallList.java
@@ -25,6 +25,7 @@
 import com.android.contacts.common.testing.NeededForTesting;
 import com.android.dialer.database.FilteredNumberAsyncQueryHandler;
 import com.android.dialer.logging.Logger;
+import com.android.incallui.util.TelecomCallUtil;
 
 import com.google.common.base.Preconditions;
 import com.google.common.collect.Maps;
@@ -85,61 +86,11 @@
     CallList() {
     }
 
-    public void onCallAdded(android.telecom.Call telecomCall, final String countryIso) {
+    public void onCallAdded(final android.telecom.Call telecomCall) {
         Trace.beginSection("onCallAdded");
         final Call call = new Call(telecomCall);
         Log.d(this, "onCallAdded: callState=" + call.getState());
 
-        // Check if call should be blocked.
-        if (!call.isEmergencyCall() && call.getState() == Call.State.INCOMING) {
-            final AtomicBoolean hasTimedOut = new AtomicBoolean(false);
-            // Proceed with call if query is slow.
-            // Call may be blocked later when FilteredQueryHandler returns.
-            final Handler handler = new Handler();
-            final Runnable runnable = new Runnable() {
-                public void run() {
-                    hasTimedOut.set(true);
-                    onCallAddedInternal(call);
-                }
-            };
-            handler.postDelayed(runnable, BLOCK_QUERY_TIMEOUT_MS);
-            if (mFilteredQueryHandler.startBlockedQuery(
-                    new FilteredNumberAsyncQueryHandler.OnCheckBlockedListener() {
-                        @Override
-                        public void onCheckComplete(final Integer id) {
-                            if (!hasTimedOut.get()) {
-                                handler.removeCallbacks(runnable);
-                            }
-                            if (id == null) {
-                                if (!hasTimedOut.get()) {
-                                    onCallAddedInternal(call);
-                                }
-                            } else {
-                                mFilteredQueryHandler.incrementFilteredCount(id);
-                                call.blockCall();
-                                Log.d(this, "onCallAdded: "
-                                        + Log.pii(call.getNumber()) + " blocked.");
-
-                                // Call back to the presenter so it can update the call log.
-                                InCallPresenter.getInstance().onCallBlocked(
-                                        call.getNumber(), call.getTimeAddedMs());
-                            }
-                        }
-                    }, null, call.getNumber(), countryIso)) {
-                Log.d(this, "onCallAdded: invalid number "
-                        + call.getNumber() + ", skipping block checking");
-                if (!hasTimedOut.get()) {
-                    handler.removeCallbacks(runnable);
-                    onCallAddedInternal(call);
-                }
-            }
-        } else {
-            onCallAddedInternal(call);
-        }
-        Trace.endSection();
-    }
-
-    private void onCallAddedInternal(Call call) {
         if (call.getState() == Call.State.INCOMING ||
                 call.getState() == Call.State.CALL_WAITING) {
             onIncoming(call, call.getCannedSmsResponses());
@@ -148,6 +99,7 @@
         }
 
         call.logCallInitiationType();
+        Trace.endSection();
     }
 
     public void onCallRemoved(android.telecom.Call telecomCall) {
diff --git a/InCallUI/src/com/android/incallui/DialpadFragment.java b/InCallUI/src/com/android/incallui/DialpadFragment.java
index abe8fb9..371f5c5 100644
--- a/InCallUI/src/com/android/incallui/DialpadFragment.java
+++ b/InCallUI/src/com/android/incallui/DialpadFragment.java
@@ -361,7 +361,7 @@
     public boolean onKey(View v, int keyCode, KeyEvent event) {
         Log.d(this, "onKey:  keyCode " + keyCode + ", view " + v);
 
-        if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
+        if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER || keyCode == KeyEvent.KEYCODE_ENTER) {
             int viewId = v.getId();
             if (mDisplayMap.containsKey(viewId)) {
                 switch (event.getAction()) {
diff --git a/InCallUI/src/com/android/incallui/InCallPresenter.java b/InCallUI/src/com/android/incallui/InCallPresenter.java
index d61a0ba..59876c2 100644
--- a/InCallUI/src/com/android/incallui/InCallPresenter.java
+++ b/InCallUI/src/com/android/incallui/InCallPresenter.java
@@ -50,6 +50,7 @@
 import com.android.dialer.calllog.CallLogAsyncTaskUtil.OnCallLogQueryFinishedListener;
 import com.android.dialer.database.FilteredNumberAsyncQueryHandler;
 import com.android.dialer.database.FilteredNumberAsyncQueryHandler.OnCheckBlockedListener;
+import com.android.incallui.util.TelecomCallUtil;
 import com.android.incalluibind.ObjectFactory;
 import com.google.common.base.Preconditions;
 
@@ -57,6 +58,7 @@
 import java.util.List;
 import java.util.Locale;
 import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.CopyOnWriteArrayList;
 
@@ -75,6 +77,8 @@
     private static final String EXTRA_FIRST_TIME_SHOWN =
             "com.android.incallui.intent.extra.FIRST_TIME_SHOWN";
 
+    private static final long BLOCK_QUERY_TIMEOUT_MS = 1000;
+
     private static final Bundle EMPTY_EXTRAS = new Bundle();
 
     private static InCallPresenter sInCallPresenter;
@@ -195,6 +199,13 @@
         private String mNumber;
         private long mTimeAddedMs;
 
+        private Runnable mTimeoutRunnable = new Runnable() {
+            @Override
+            public void run() {
+                unregister();
+            }
+        };
+
         public BlockedNumberContentObserver(Handler handler, String number, long timeAddedMs) {
             super(handler);
 
@@ -220,17 +231,13 @@
             if (mContext != null) {
                 mContext.getContentResolver().registerContentObserver(
                         CallLog.CONTENT_URI, true, this);
-                mHandler.postDelayed(new Runnable() {
-                    @Override
-                    public void run() {
-                        unregister();
-                    }
-                }, TIMEOUT_MS);
+                mHandler.postDelayed(mTimeoutRunnable, TIMEOUT_MS);
             }
         }
 
         private void unregister() {
             if (mContext != null) {
+                mHandler.removeCallbacks(mTimeoutRunnable);
                 mContext.getContentResolver().unregisterContentObserver(this);
             }
         }
@@ -343,6 +350,8 @@
      */
     public void tearDown() {
         Log.d(this, "tearDown");
+        mCallList.clearOnDisconnect();
+
         mServiceConnected = false;
         attemptCleanup();
 
@@ -479,21 +488,85 @@
         bringToForeground(showDialpad);
     }
 
-    /**
-     * TODO: Consider listening to CallList callbacks to do this instead of receiving a direct
-     * method invocation from InCallService.
-     */
     public void onCallAdded(final android.telecom.Call call) {
+        // Check if call should be blocked.
+        if (!TelecomCallUtil.isEmergencyCall(call)
+                && call.getState() == android.telecom.Call.STATE_RINGING) {
+            maybeBlockCall(call);
+        } else {
+            mCallList.onCallAdded(call);
+        }
+
         // Since a call has been added we are no longer waiting for Telecom to send us a call.
         setBoundAndWaitingForOutgoingCall(false, null);
         call.registerCallback(mCallCallback);
     }
 
     /**
-     * TODO: Consider listening to CallList callbacks to do this instead of receiving a direct
-     * method invocation from InCallService.
+     * Checks whether a call should be blocked, and blocks it if so. Otherwise, it adds the call
+     * to the CallList so it can proceed as normal. There is a timeout, so if the function for
+     * checking whether a function is blocked does not return in a reasonable time, we proceed
+     * with adding the call anyways.
      */
+    private void maybeBlockCall(final android.telecom.Call call) {
+        final String countryIso = GeoUtil.getCurrentCountryIso(mContext);
+        final String number = TelecomCallUtil.getNumber(call);
+        final long timeAdded = System.currentTimeMillis();
+
+        // Though AtomicBoolean's can be scary, don't fear, as in this case it is only used on the
+        // main UI thread. It is needed so we can change its value within different scopes, since
+        // that cannot be done with a final boolean.
+        final AtomicBoolean hasTimedOut = new AtomicBoolean(false);
+
+        final Handler handler = new Handler();
+
+        // Proceed if the query is slow; the call may still be blocked after the query returns.
+        final Runnable runnable = new Runnable() {
+            public void run() {
+                hasTimedOut.set(true);
+                mCallList.onCallAdded(call);
+            }
+        };
+        handler.postDelayed(runnable, BLOCK_QUERY_TIMEOUT_MS);
+
+        OnCheckBlockedListener onCheckBlockedListener = new OnCheckBlockedListener() {
+            @Override
+            public void onCheckComplete(final Integer id) {
+                if (!hasTimedOut.get()) {
+                    handler.removeCallbacks(runnable);
+                }
+                if (id == null) {
+                    if (!hasTimedOut.get()) {
+                        mCallList.onCallAdded(call);
+                    }
+                } else {
+                    call.reject(false, null);
+                    Log.d(this, "checkForBlockedCall: " + Log.pii(number) + " blocked.");
+
+                    mFilteredQueryHandler.incrementFilteredCount(id);
+
+                    // Register observer to update the call log.
+                    // BlockedNumberContentObserver will unregister after successful log or timeout.
+                    BlockedNumberContentObserver contentObserver =
+                            new BlockedNumberContentObserver(new Handler(), number, timeAdded);
+                    contentObserver.register();
+                }
+            }
+        };
+
+        boolean isInvalidNumber = mFilteredQueryHandler.startBlockedQuery(
+                onCheckBlockedListener, null, number, countryIso);
+        if (isInvalidNumber) {
+            Log.d(this, "checkForBlockedCall: invalid number, skipping block checking");
+            if (!hasTimedOut.get()) {
+                handler.removeCallbacks(runnable);
+                mCallList.onCallAdded(call);
+            }
+        }
+    }
+
     public void onCallRemoved(android.telecom.Call call) {
+        mCallList.onCallRemoved(call);
         call.unregisterCallback(mCallCallback);
     }
 
@@ -583,14 +656,6 @@
         }
     }
 
-    public void onCallBlocked(String number, long timeAddedMs) {
-        BlockedNumberContentObserver contentObserver =
-                new BlockedNumberContentObserver(new Handler(), number, timeAddedMs);
-
-        // BlockedNumberContentObserver will unregister after successful log or timeout.
-        contentObserver.register();
-    }
-
     /**
      * Given the call list, return the state in which the in-call screen should be.
      */
diff --git a/InCallUI/src/com/android/incallui/InCallServiceImpl.java b/InCallUI/src/com/android/incallui/InCallServiceImpl.java
index ee2ca1b..11202e7 100644
--- a/InCallUI/src/com/android/incallui/InCallServiceImpl.java
+++ b/InCallUI/src/com/android/incallui/InCallServiceImpl.java
@@ -23,11 +23,8 @@
 import android.telecom.CallAudioState;
 import android.telecom.InCallService;
 
-import com.android.contacts.common.GeoUtil;
 import com.android.dialer.database.FilteredNumberAsyncQueryHandler;
 
-import java.util.Locale;
-
 /**
  * Used to receive updates about calls from the Telecom component.  This service is bound to
  * Telecom while there exist calls which potentially require UI. This includes ringing (incoming),
@@ -48,14 +45,11 @@
 
     @Override
     public void onCallAdded(Call call) {
-        final String countryIso = GeoUtil.getCurrentCountryIso(getApplicationContext());
-        CallList.getInstance().onCallAdded(call, countryIso);
         InCallPresenter.getInstance().onCallAdded(call);
     }
 
     @Override
     public void onCallRemoved(Call call) {
-        CallList.getInstance().onCallRemoved(call);
         InCallPresenter.getInstance().onCallRemoved(call);
     }
 
@@ -82,8 +76,6 @@
         InCallPresenter.getInstance().onServiceBind();
         InCallPresenter.getInstance().maybeStartRevealAnimation(intent);
         TelecomAdapter.getInstance().setInCallService(this);
-        CallList.getInstance().setFilteredNumberQueryHandler(
-                new FilteredNumberAsyncQueryHandler(getContentResolver()));
 
         return super.onBind(intent);
     }
@@ -102,7 +94,6 @@
         Log.v(this, "tearDown");
         // Tear down the InCall system
         TelecomAdapter.getInstance().clearInCallService();
-        CallList.getInstance().clearOnDisconnect();
         InCallPresenter.getInstance().tearDown();
     }
 
diff --git a/InCallUI/src/com/android/incallui/StatusBarNotifier.java b/InCallUI/src/com/android/incallui/StatusBarNotifier.java
index f46b0d3..d1509b8 100644
--- a/InCallUI/src/com/android/incallui/StatusBarNotifier.java
+++ b/InCallUI/src/com/android/incallui/StatusBarNotifier.java
@@ -272,7 +272,7 @@
          * Fire off the notification
          */
         Notification notification = builder.build();
-        if (mCurrentNotification != notificationType) {
+        if (mCurrentNotification != notificationType && mCurrentNotification != NOTIFICATION_NONE) {
             Log.i(this, "Previous notification already showing - cancelling "
                     + mCurrentNotification);
             mNotificationManager.cancel(mCurrentNotification);
diff --git a/InCallUI/src/com/android/incallui/util/TelecomCallUtil.java b/InCallUI/src/com/android/incallui/util/TelecomCallUtil.java
new file mode 100644
index 0000000..53ecc29
--- /dev/null
+++ b/InCallUI/src/com/android/incallui/util/TelecomCallUtil.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2015 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.incallui.util;
+
+import android.net.Uri;
+import android.telecom.Call;
+import android.telephony.PhoneNumberUtils;
+
+/**
+ * Class to provide a standard interface for obtaining information from the underlying
+ * android.telecom.Call. Much of this should be obtained through the incall.Call, but
+ * on occasion we need to interact with the telecom.Call directly (eg. call blocking,
+ * before the incall.Call has been created).
+ */
+public class TelecomCallUtil {
+
+    // Whether the call handle is an emergency number.
+    public static boolean isEmergencyCall(Call call) {
+        Uri handle = call.getDetails().getHandle();
+        return PhoneNumberUtils.isEmergencyNumber(
+                handle == null ? "" : handle.getSchemeSpecificPart());
+    }
+
+    public static String getNumber(Call call) {
+        if (call == null) {
+            return null;
+        }
+        if (call.getDetails().getGatewayInfo() != null) {
+            return call.getDetails().getGatewayInfo()
+                    .getOriginalAddress().getSchemeSpecificPart();
+        }
+        Uri handle = getHandle(call);
+        return handle == null ? null : handle.getSchemeSpecificPart();
+    }
+
+    public static Uri getHandle(Call call) {
+        return call == null ? null : call.getDetails().getHandle();
+    }
+}