15b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu/*
25b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu * Copyright (C) 2016 The Android Open Source Project
35b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu *
45b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu * Licensed under the Apache License, Version 2.0 (the "License");
55b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu * you may not use this file except in compliance with the License.
65b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu * You may obtain a copy of the License at
75b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu *
85b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu *      http://www.apache.org/licenses/LICENSE-2.0
95b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu *
105b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu * Unless required by applicable law or agreed to in writing, software
115b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu * distributed under the License is distributed on an "AS IS" BASIS,
125b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
135b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu * See the License for the specific language governing permissions and
145b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu * limitations under the License
155b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu */
165b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu
175b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liupackage com.android.server.telecom;
185b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu
19f2373e177b6e122b84dd1a22320629d0280de1c6Shigeru Muraiimport android.annotation.Nullable;
205b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liuimport android.content.Context;
215b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liuimport android.graphics.Bitmap;
225b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liuimport android.graphics.drawable.Drawable;
235b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liuimport android.net.Uri;
245b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liuimport android.os.Handler;
255b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liuimport android.os.Looper;
26a3eccfee788c3ac3c831a443b085b141b39bb63dBrad Ebingerimport android.telecom.Log;
27a3eccfee788c3ac3c831a443b085b141b39bb63dBrad Ebingerimport android.telecom.Logging.Runnable;
28a3eccfee788c3ac3c831a443b085b141b39bb63dBrad Ebingerimport android.telecom.Logging.Session;
295b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liuimport android.text.TextUtils;
305b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu
315b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liuimport com.android.internal.annotations.VisibleForTesting;
325b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liuimport com.android.internal.telephony.CallerInfo;
335b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liuimport com.android.internal.telephony.CallerInfoAsyncQuery;
345b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu
355b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liuimport java.io.InputStream;
365b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liuimport java.util.HashMap;
375b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liuimport java.util.LinkedList;
385b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liuimport java.util.List;
395b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liuimport java.util.Map;
405b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu
415b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liupublic class CallerInfoLookupHelper {
425b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu    public interface OnQueryCompleteListener {
435b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu        /**
445b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu         * Called when the query returns with the caller info
455b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu         * @param info
465b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu         * @return true if the value should be cached, false otherwise.
475b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu         */
48f2373e177b6e122b84dd1a22320629d0280de1c6Shigeru Murai        void onCallerInfoQueryComplete(Uri handle, @Nullable CallerInfo info);
495b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu        void onContactPhotoQueryComplete(Uri handle, CallerInfo info);
505b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu    }
515b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu
525b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu    private static class CallerInfoQueryInfo {
535b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu        public CallerInfo callerInfo;
545b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu        public List<OnQueryCompleteListener> listeners;
555b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu        public boolean imageQueryPending = false;
565b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu
575b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu        public CallerInfoQueryInfo() {
585b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu            listeners = new LinkedList<>();
595b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu        }
605b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu    }
61f2373e177b6e122b84dd1a22320629d0280de1c6Shigeru Murai
625b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu    private final Map<Uri, CallerInfoQueryInfo> mQueryEntries = new HashMap<>();
635b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu
645b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu    private final CallerInfoAsyncQueryFactory mCallerInfoAsyncQueryFactory;
655b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu    private final ContactsAsyncHelper mContactsAsyncHelper;
665b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu    private final Context mContext;
67465982730ff480ff3037cbc22c9dcae005346971Hall Liu    private final TelecomSystem.SyncRoot mLock;
685b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu    private final Handler mHandler = new Handler(Looper.getMainLooper());
695b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu
705b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu    public CallerInfoLookupHelper(Context context,
715b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu            CallerInfoAsyncQueryFactory callerInfoAsyncQueryFactory,
72465982730ff480ff3037cbc22c9dcae005346971Hall Liu            ContactsAsyncHelper contactsAsyncHelper,
73465982730ff480ff3037cbc22c9dcae005346971Hall Liu            TelecomSystem.SyncRoot lock) {
745b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu        mCallerInfoAsyncQueryFactory = callerInfoAsyncQueryFactory;
755b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu        mContactsAsyncHelper = contactsAsyncHelper;
765b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu        mContext = context;
77465982730ff480ff3037cbc22c9dcae005346971Hall Liu        mLock = lock;
785b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu    }
795b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu
805b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu    public void startLookup(final Uri handle, OnQueryCompleteListener listener) {
815b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu        if (handle == null) {
82f2373e177b6e122b84dd1a22320629d0280de1c6Shigeru Murai            listener.onCallerInfoQueryComplete(handle, null);
835b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu            return;
845b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu        }
855b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu
865b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu        final String number = handle.getSchemeSpecificPart();
875b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu        if (TextUtils.isEmpty(number)) {
88f2373e177b6e122b84dd1a22320629d0280de1c6Shigeru Murai            listener.onCallerInfoQueryComplete(handle, null);
895b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu            return;
905b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu        }
915b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu
925b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu        synchronized (mLock) {
935b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu            if (mQueryEntries.containsKey(handle)) {
945b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                CallerInfoQueryInfo info = mQueryEntries.get(handle);
955b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                if (info.callerInfo != null) {
965b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                    Log.i(this, "Caller info already exists for handle %s; using cached value",
975b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                            Log.piiHandle(handle));
985b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                    listener.onCallerInfoQueryComplete(handle, info.callerInfo);
995b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                    if (!info.imageQueryPending && (info.callerInfo.cachedPhoto != null ||
1005b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                            info.callerInfo.cachedPhotoIcon != null)) {
1015b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                        listener.onContactPhotoQueryComplete(handle, info.callerInfo);
1025b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                    } else if (info.imageQueryPending) {
10336b4ee2a5ee416b359f167604900c35ffe100ba2Hall Liu                        Log.i(this, "There is a pending photo query for handle %s. " +
1045b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                                "Adding to listeners for this query.", Log.piiHandle(handle));
1055b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                        info.listeners.add(listener);
1065b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                    }
1075b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                } else {
1085b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                    Log.i(this, "There is a previously incomplete query for handle %s. Adding to " +
1095b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                            "listeners for this query.", Log.piiHandle(handle));
1105b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                    info.listeners.add(listener);
1115b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                    return;
1125b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                }
1135b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu            } else {
1145b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                CallerInfoQueryInfo info = new CallerInfoQueryInfo();
1155b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                info.listeners.add(listener);
1165b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                mQueryEntries.put(handle, info);
1175b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu            }
1185b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu        }
1195b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu
120f5e066609d0acc7ff53752bd6b9e3f2634bd0746Brad Ebinger        mHandler.post(new Runnable("CILH.sL", mLock) {
1215b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu            @Override
1225b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu            public void loggedRun() {
1235b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                Session continuedSession = Log.createSubsession();
1245b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                try {
1255b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                    CallerInfoAsyncQuery query = mCallerInfoAsyncQueryFactory.startQuery(
1265b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                            0, mContext, number,
1275b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                            makeCallerInfoQueryListener(handle), continuedSession);
1285b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                    if (query == null) {
1295b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                        Log.w(this, "Lookup failed for %s.", Log.piiHandle(handle));
1305b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                        Log.cancelSubsession(continuedSession);
1315b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                    }
1325b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                } catch (Throwable t) {
1335b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                    Log.cancelSubsession(continuedSession);
1345b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                    throw t;
1355b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                }
1365b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu            }
1375b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu        }.prepare());
1385b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu    }
1395b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu
1405b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu    private CallerInfoAsyncQuery.OnQueryCompleteListener makeCallerInfoQueryListener(
1415b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu            final Uri handle) {
1425b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu        return (token, cookie, ci) -> {
1435b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu            synchronized (mLock) {
1445b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                Log.continueSession((Session) cookie, "CILH.oQC");
1455b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                try {
1465b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                    if (mQueryEntries.containsKey(handle)) {
14736b4ee2a5ee416b359f167604900c35ffe100ba2Hall Liu                        Log.i(CallerInfoLookupHelper.this, "CI query for handle %s has completed;" +
14836b4ee2a5ee416b359f167604900c35ffe100ba2Hall Liu                                " notifying all listeners.", Log.piiHandle(handle));
1495b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                        CallerInfoQueryInfo info = mQueryEntries.get(handle);
1505b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                        for (OnQueryCompleteListener l : info.listeners) {
1515b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                            l.onCallerInfoQueryComplete(handle, ci);
1525b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                        }
1535b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                        if (ci.contactDisplayPhotoUri == null) {
15436b4ee2a5ee416b359f167604900c35ffe100ba2Hall Liu                            Log.i(CallerInfoLookupHelper.this, "There is no photo for this " +
15536b4ee2a5ee416b359f167604900c35ffe100ba2Hall Liu                                    "contact, skipping photo query");
1565b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                            mQueryEntries.remove(handle);
1575b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                        } else {
1585b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                            info.callerInfo = ci;
1595b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                            info.imageQueryPending = true;
1605b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                            startPhotoLookup(handle, ci.contactDisplayPhotoUri);
1615b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                        }
1625b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                    } else {
1635b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                        Log.i(CallerInfoLookupHelper.this, "CI query for handle %s has completed," +
16413a96cec066e0275cc07c7f8cafc57585c735833Brad Ebinger                                " but there are no listeners left.", Log.piiHandle(handle));
1655b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                    }
1665b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                } finally {
1675b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                    Log.endSession();
1685b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                }
1695b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu            }
1705b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu        };
1715b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu    }
1725b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu
1735b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu    private void startPhotoLookup(final Uri handle, final Uri contactPhotoUri) {
174f5e066609d0acc7ff53752bd6b9e3f2634bd0746Brad Ebinger        mHandler.post(new Runnable("CILH.sPL", mLock) {
1755b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu            @Override
1765b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu            public void loggedRun() {
1775b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                Session continuedSession = Log.createSubsession();
1785b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                try {
1795b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                    mContactsAsyncHelper.startObtainPhotoAsync(
1805b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                            0, mContext, contactPhotoUri,
1815b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                            makeContactPhotoListener(handle), continuedSession);
1825b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                } catch (Throwable t) {
1835b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                    Log.cancelSubsession(continuedSession);
1845b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                    throw t;
1855b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                }
1865b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu            }
1875b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu        }.prepare());
1885b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu    }
1895b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu
1905b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu    private ContactsAsyncHelper.OnImageLoadCompleteListener makeContactPhotoListener(
1915b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu            final Uri handle) {
1925b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu        return (token, photo, photoIcon, cookie) -> {
1935b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu            synchronized (mLock) {
1945b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                Log.continueSession((Session) cookie, "CLIH.oILC");
1955b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                try {
1965b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                    if (mQueryEntries.containsKey(handle)) {
1975b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                        CallerInfoQueryInfo info = mQueryEntries.get(handle);
1985b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                        if (info.callerInfo == null) {
1995b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                            Log.w(CallerInfoLookupHelper.this, "Photo query finished, but the " +
2005b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                                    "CallerInfo object previously looked up was not cached.");
20136b4ee2a5ee416b359f167604900c35ffe100ba2Hall Liu                            mQueryEntries.remove(handle);
2025b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                            return;
2035b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                        }
2045b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                        info.callerInfo.cachedPhoto = photo;
2055b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                        info.callerInfo.cachedPhotoIcon = photoIcon;
2065b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                        for (OnQueryCompleteListener l : info.listeners) {
2075b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                            l.onContactPhotoQueryComplete(handle, info.callerInfo);
2085b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                        }
2095b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                        mQueryEntries.remove(handle);
2105b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                    } else {
2115b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                        Log.i(CallerInfoLookupHelper.this, "Photo query for handle %s has" +
21213a96cec066e0275cc07c7f8cafc57585c735833Brad Ebinger                                " completed, but there are no listeners left.",
21313a96cec066e0275cc07c7f8cafc57585c735833Brad Ebinger                                Log.piiHandle(handle));
2145b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                    }
2155b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                } finally {
2165b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                    Log.endSession();
2175b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu                }
2185b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu            }
2195b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu        };
2205b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu    }
2215b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu
2225b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu    @VisibleForTesting
2235b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu    public Map<Uri, CallerInfoQueryInfo> getCallerInfoEntries() {
2245b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu        return mQueryEntries;
2255b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu    }
2265b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu
2275b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu    @VisibleForTesting
2285b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu    public Handler getHandler() {
2295b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu        return mHandler;
2305b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu    }
2315b70c1c2ef08f06bacd4156b266e8ce5b3ccdc07Hall Liu}
232