Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3b90d48e authored by Ceci Wu's avatar Ceci Wu Committed by Patrick Tsai
Browse files

Query geo description in worker thread to prevent ANR

Currently geo description of a caller is queried in the main thread, for
both InCallUI and framework CallerInfo. Since it is a potentially
time-consuming operation especially when the system is under heavy
loading, the main thread might be blocked for a long time. As a result,
the ANR happens.

The solution to to move the query to worker thread to free the main
thread and enhance the performance.

Besides, to ensure listeners are notified with complete CallInfo, we
need to delay callbacks until EVENT_END_OF_QUEUE.

Change-Id: Ibe444fc0f98d07e7ebbe266987e60ede334a3216
parent 082b1186
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -576,7 +576,7 @@ public class CallerInfo {
     * @return a geographical description string for the specified number.
     * @see com.android.i18n.phonenumbers.PhoneNumberOfflineGeocoder
     */
    private static String getGeoDescription(Context context, String number) {
    public static String getGeoDescription(Context context, String number) {
        if (VDBG) Rlog.v(TAG, "getGeoDescription('" + number + "')...");

        if (TextUtils.isEmpty(number)) {
+66 −25
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.ContactsContract.PhoneLookup;
@@ -35,6 +36,9 @@ import android.text.TextUtils;
import android.telephony.Rlog;
import android.telephony.SubscriptionManager;

import java.util.ArrayList;
import java.util.List;

/**
 * Helper class to make it easier to run asynchronous caller-id lookup queries.
 * @see CallerInfo
@@ -50,6 +54,7 @@ public class CallerInfoAsyncQuery {
    private static final int EVENT_END_OF_QUEUE = 3;
    private static final int EVENT_EMERGENCY_NUMBER = 4;
    private static final int EVENT_VOICEMAIL_NUMBER = 5;
    private static final int EVENT_GET_GEO_DESCRIPTION = 6;

    private CallerInfoAsyncQueryHandler mHandler;

@@ -79,6 +84,7 @@ public class CallerInfoAsyncQuery {
        public Object cookie;
        public int event;
        public String number;
        public String geoDescription;

        public int subId;
    }
@@ -142,6 +148,7 @@ public class CallerInfoAsyncQuery {
        private Context mContext;
        private Uri mQueryUri;
        private CallerInfo mCallerInfo;
        private List<Runnable> mPendingListenerCallbacks = new ArrayList<>();

        /**
         * Our own query worker thread.
@@ -206,11 +213,32 @@ public class CallerInfoAsyncQuery {

                            reply.sendToTarget();

                            break;
                        case EVENT_GET_GEO_DESCRIPTION:
                            handleGeoDescription(msg);
                            break;
                        default:
                    }
                }
            }

            private void handleGeoDescription(Message msg) {
                WorkerArgs args = (WorkerArgs) msg.obj;
                CookieWrapper cw = (CookieWrapper) args.cookie;
                if (!TextUtils.isEmpty(cw.number) && cw.cookie != null && mContext != null) {
                    final long startTimeMillis = SystemClock.elapsedRealtime();
                    cw.geoDescription = CallerInfo.getGeoDescription(mContext, cw.number);
                    final long duration = SystemClock.elapsedRealtime() - startTimeMillis;
                    if (duration > 500) {
                        if (DBG) Rlog.d(LOG_TAG, "[handleGeoDescription]" +
                                "Spends long time to retrieve Geo description: " + duration);
                    }
                }
                Message reply = args.handler.obtainMessage(msg.what);
                reply.obj = args;
                reply.arg1 = msg.arg1;
                reply.sendToTarget();
            }
        }


@@ -256,6 +284,11 @@ public class CallerInfoAsyncQuery {
            }

            if (cw.event == EVENT_END_OF_QUEUE) {
                for (Runnable r : mPendingListenerCallbacks) {
                    r.run();
                }
                mPendingListenerCallbacks.clear();

                release();
                if (cursor != null) {
                    cursor.close();
@@ -263,6 +296,18 @@ public class CallerInfoAsyncQuery {
                return;
            }

            // If the cw.event == EVENT_GET_GEO_DESCRIPTION, means it would not be the 1st
            // time entering the onQueryComplete(), mCallerInfo should not be null.
            if (cw.event == EVENT_GET_GEO_DESCRIPTION) {
                if (mCallerInfo != null) {
                    mCallerInfo.geoDescription = cw.geoDescription;
                }
                // notify that we can clean up the queue after this.
                CookieWrapper endMarker = new CookieWrapper();
                endMarker.event = EVENT_END_OF_QUEUE;
                startQuery(token, endMarker, null, null, null, null, null);
            }

            // check the token and if needed, create the callerinfo object.
            if (mCallerInfo == null) {
                if ((mContext == null) || (mQueryUri == null)) {
@@ -293,34 +338,24 @@ public class CallerInfoAsyncQuery {
                                + mCallerInfo);
                    }

                    // Final step: look up the geocoded description.
                    if (ENABLE_UNKNOWN_NUMBER_GEO_DESCRIPTION) {
                        // Note we do this only if we *don't* have a valid name (i.e. if
                        // no contacts matched the phone number of the incoming call),
                        // since that's the only case where the incoming-call UI cares
                        // about this field.
                        //
                        // (TODO: But if we ever want the UI to show the geoDescription
                        // even when we *do* match a contact, we'll need to either call
                        // updateGeoDescription() unconditionally here, or possibly add a
                        // new parameter to CallerInfoAsyncQuery.startQuery() to force
                        // the geoDescription field to be populated.)

                        if (TextUtils.isEmpty(mCallerInfo.name)) {
                            // Actually when no contacts match the incoming phone number,
                            // the CallerInfo object is totally blank here (i.e. no name
                            // *or* phoneNumber).  So we need to pass in cw.number as
                            // a fallback number.
                            mCallerInfo.updateGeoDescription(mContext, cw.number);
                        }
                    }

                    // Use the number entered by the user for display.
                    if (!TextUtils.isEmpty(cw.number)) {
                        mCallerInfo.phoneNumber = PhoneNumberUtils.formatNumber(cw.number,
                                mCallerInfo.normalizedNumber,
                                CallerInfo.getCurrentCountryIso(mContext));
                    }

                    // This condition refer to the google default code for geo.
                    // If the number exists in Contacts, the CallCard would never show
                    // the geo description, so it would be unnecessary to query it.
                    if (ENABLE_UNKNOWN_NUMBER_GEO_DESCRIPTION) {
                        if (TextUtils.isEmpty(mCallerInfo.name)) {
                            if (DBG) Rlog.d(LOG_TAG, "start querying geo description");
                            cw.event = EVENT_GET_GEO_DESCRIPTION;
                            startQuery(token, cw, null, null, null, null, null);
                            return;
                        }
                    }
                }

                if (DBG) Rlog.d(LOG_TAG, "constructing CallerInfo object for token: " + token);
@@ -333,9 +368,15 @@ public class CallerInfoAsyncQuery {

            //notify the listener that the query is complete.
            if (cw.listener != null) {
                Rlog.d(LOG_TAG, "notifying listener: " + cw.listener.getClass().toString() +
                             " for token: " + token + mCallerInfo);
                mPendingListenerCallbacks.add(new Runnable() {
                    @Override
                    public void run() {
                        if (DBG) Rlog.d(LOG_TAG, "notifying listener: "
                                + cw.listener.getClass().toString() + " for token: " + token
                                + mCallerInfo);
                        cw.listener.onQueryComplete(token, cw.cookie, mCallerInfo);
                    }
                });
            } else {
                Rlog.w(LOG_TAG, "There is no listener to notify for this query.");
            }