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

Commit ef5250e8 authored by Hakjun Choi's avatar Hakjun Choi
Browse files

Separate running thread of ImsResolver's handler to prevent ANR

The main thread of phone process may be blocked while processing ImsService's binder call as ther is large area of lock sharing

Bug: 276881256
Test: entire Ims Unit Test / Ims cts test
Test: e2e Ims Call regression Test
Change-Id: I1e7423fd15ef4532dbb5f59e53d024a56f074153
parent 4302da81
Loading
Loading
Loading
Loading
+9 −2
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import android.content.pm.ServiceInfo;
import android.os.AsyncResult;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.os.PersistableBundle;
@@ -130,6 +131,7 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal

    // Delay between dynamic ImsService queries.
    private static final int DELAY_DYNAMIC_QUERY_MS = 5000;
    private static final HandlerThread sHandlerThread = new HandlerThread(TAG);

    private static ImsResolver sInstance;

@@ -139,9 +141,9 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal
    public static void make(Context context, String defaultMmTelPackageName,
            String defaultRcsPackageName, int numSlots, ImsFeatureBinderRepository repo) {
        if (sInstance == null) {
            Looper looper = Looper.getMainLooper();
            sHandlerThread.start();
            sInstance = new ImsResolver(context, defaultMmTelPackageName, defaultRcsPackageName,
                    numSlots, repo, looper);
                    numSlots, repo, sHandlerThread.getLooper());
        }
    }

@@ -630,8 +632,13 @@ public class ImsResolver implements ImsServiceController.ImsServiceControllerCal

    /**
     * Needs to be called after the constructor to kick off the process of binding to ImsServices.
     * Should be run on the handler thread of ImsResolver
     */
    public void initialize() {
        mHandler.post(()-> initializeInternal());
    }

    private void initializeInternal() {
        mEventLog.log("Initializing");
        Log.i(TAG, "Initializing cache.");
        PhoneConfigurationManager.registerForMultiSimConfigChange(mHandler,
+12 −1
Original line number Diff line number Diff line
@@ -956,7 +956,7 @@ public class ImsResolverTest extends ImsTestBase {
        carrierFeatures.add(new ImsFeatureConfiguration.FeatureSlotPair(1, ImsFeature.FEATURE_RCS));
        // Assume that there is a CarrierConfig change that kicks off query to carrier service.
        sendCarrierConfigChanged(1, 1);
        setupDynamicQueryFeatures(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 2);
        setupDynamicQueryFeaturesMultiSim(TEST_CARRIER_DEFAULT_NAME, carrierFeatures, 2);
        verify(carrierController).changeImsServiceFeatures(eq(carrierFeatures),
                        any(SparseIntArray.class));
        deviceFeatureSet = convertToHashSet(deviceFeatures, 0);
@@ -2021,6 +2021,7 @@ public class ImsResolverTest extends ImsTestBase {
     */
    private void startBindCarrierConfigAlreadySet() {
        mTestImsResolver.initialize();
        processAllMessages();
        ArgumentCaptor<BroadcastReceiver> receiversCaptor =
                ArgumentCaptor.forClass(BroadcastReceiver.class);
        verify(mMockContext, times(3)).registerReceiver(receiversCaptor.capture(), any());
@@ -2042,6 +2043,7 @@ public class ImsResolverTest extends ImsTestBase {
     */
    private void startBindNoCarrierConfig(int numSlots) {
        mTestImsResolver.initialize();
        processAllMessages();
        ArgumentCaptor<BroadcastReceiver> receiversCaptor =
                ArgumentCaptor.forClass(BroadcastReceiver.class);
        verify(mMockContext, times(3)).registerReceiver(receiversCaptor.capture(), any());
@@ -2068,6 +2070,15 @@ public class ImsResolverTest extends ImsTestBase {
        processAllMessages();
    }

    private void setupDynamicQueryFeaturesMultiSim(ComponentName name,
            HashSet<ImsFeatureConfiguration.FeatureSlotPair> features, int times) {
        processAllFutureMessages();
        // ensure that startQuery was called
        verify(mMockQueryManager, times(times)).startQuery(eq(name), any(String.class));
        mDynamicQueryListener.onComplete(name, features);
        processAllMessages();
    }

    private void setupDynamicQueryFeaturesFailure(ComponentName name, int times) {
        processAllMessages();
        // ensure that startQuery was called
+89 −0
Original line number Diff line number Diff line
@@ -21,6 +21,8 @@ import static org.junit.Assert.fail;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.MessageQueue;
import android.testing.TestableLooper;

import androidx.test.InstrumentationRegistry;
@@ -29,6 +31,7 @@ import com.android.internal.telephony.TelephonyTest;

import org.mockito.MockitoAnnotations;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
@@ -38,6 +41,22 @@ import java.util.concurrent.TimeUnit;
 * Helper class to load Mockito Resources into a test.
 */
public class ImsTestBase {
    private static final Field MESSAGE_QUEUE_FIELD;
    private static final Field MESSAGE_WHEN_FIELD;
    private static final Field MESSAGE_NEXT_FIELD;

    static {
        try {
            MESSAGE_QUEUE_FIELD = MessageQueue.class.getDeclaredField("mMessages");
            MESSAGE_QUEUE_FIELD.setAccessible(true);
            MESSAGE_WHEN_FIELD = Message.class.getDeclaredField("when");
            MESSAGE_WHEN_FIELD.setAccessible(true);
            MESSAGE_NEXT_FIELD = Message.class.getDeclaredField("next");
            MESSAGE_NEXT_FIELD.setAccessible(true);
        } catch (NoSuchFieldException e) {
            throw new RuntimeException("Failed to initialize TelephonyTest", e);
        }
    }

    protected Context mContext;
    protected List<TestableLooper> mTestableLoopers = new ArrayList<>();
@@ -111,6 +130,52 @@ public class ImsTestBase {
        }
    }

    /**
     * @return The longest delay from all the message queues.
     */
    private long getLongestDelay() {
        long delay = 0;
        for (TestableLooper looper : mTestableLoopers) {
            MessageQueue queue = looper.getLooper().getQueue();
            try {
                Message msg = (Message) MESSAGE_QUEUE_FIELD.get(queue);
                while (msg != null) {
                    delay = Math.max(msg.getWhen(), delay);
                    msg = (Message) MESSAGE_NEXT_FIELD.get(msg);
                }
            } catch (IllegalAccessException e) {
                throw new RuntimeException("Access failed in TelephonyTest", e);
            }
        }
        return delay;
    }

    /**
     * @return {@code true} if there are any messages in the queue.
     */
    private boolean messagesExist() {
        for (TestableLooper looper : mTestableLoopers) {
            MessageQueue queue = looper.getLooper().getQueue();
            try {
                Message msg = (Message) MESSAGE_QUEUE_FIELD.get(queue);
                if (msg != null) return true;
            } catch (IllegalAccessException e) {
                throw new RuntimeException("Access failed in TelephonyTest", e);
            }
        }
        return false;
    }

    /**
     * Handle all messages including the delayed messages.
     */
    public void processAllFutureMessages() {
        while (messagesExist()) {
            moveTimeForward(getLongestDelay());
            processAllMessages();
        }
    }

    /**
     * Check if there are any messages to be processed in any monitored TestableLooper
     * Delayed messages to be handled at a later time will be ignored
@@ -123,4 +188,28 @@ public class ImsTestBase {
        }
        return true;
    }

    /**
     * Effectively moves time forward by reducing the time of all messages
     * for all monitored TestableLoopers
     * @param milliSeconds number of milliseconds to move time forward by
     */
    public void moveTimeForward(long milliSeconds) {
        for (TestableLooper looper : mTestableLoopers) {
            MessageQueue queue = looper.getLooper().getQueue();
            try {
                Message msg = (Message) MESSAGE_QUEUE_FIELD.get(queue);
                while (msg != null) {
                    long updatedWhen = msg.getWhen() - milliSeconds;
                    if (updatedWhen < 0) {
                        updatedWhen = 0;
                    }
                    MESSAGE_WHEN_FIELD.set(msg, updatedWhen);
                    msg = (Message) MESSAGE_NEXT_FIELD.get(msg);
                }
            } catch (IllegalAccessException e) {
                throw new RuntimeException("Access failed in TelephonyTest", e);
            }
        }
    }
}