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

Commit 958f2008 authored by Nathan Harold's avatar Nathan Harold
Browse files

Lazily init Handler in SubscriptionManager

Convert to lazy initialization of the Handler inside the
OnSubscriptionsChangedListener to avoid the undocumented
requirement that it always be created inside of a thread that
has a prepared Looper.

Bug: 278814050
Test: atest FrameworksTelephonyTests
Test: atest SubscriptionManagerTest#testCreateSubscriptionChangedListenerWithoutLooper
Change-Id: I22fec4924ed7299f415e0cbf2c208e65a4f2e3d1
parent 4f51bbe2
Loading
Loading
Loading
Loading
+45 −19
Original line number Diff line number Diff line
@@ -35,6 +35,9 @@ import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.app.PendingIntent;
import android.app.PropertyInvalidatedCache;
import android.compat.Compatibility;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.Intent;
@@ -85,6 +88,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -1322,41 +1326,61 @@ public class SubscriptionManager {
     * for #onSubscriptionsChanged to be invoked.
     */
    public static class OnSubscriptionsChangedListener {
        private class OnSubscriptionsChangedListenerHandler extends Handler {
            OnSubscriptionsChangedListenerHandler() {
                super();
            }

            OnSubscriptionsChangedListenerHandler(Looper looper) {
                super(looper);
            }
        }
        /**
         * After {@link Build.VERSION_CODES.Q}, it is no longer necessary to instantiate a
         * Handler inside of the OnSubscriptionsChangedListener in all cases, so it will only
         * be done for callers that do not supply an Executor.
         */
        @ChangeId
        @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
        private static final long LAZY_INITIALIZE_SUBSCRIPTIONS_CHANGED_HANDLER = 278814050L;

        /**
         * Posted executor callback on the handler associated with a given looper.
         * The looper can be the calling thread's looper or the looper passed from the
         * constructor {@link #OnSubscriptionsChangedListener(Looper)}.
         * For backwards compatibility reasons, stashes the Looper associated with the thread
         * context in which this listener was created.
         */
        private final HandlerExecutor mExecutor;
        private final Looper mCreatorLooper;

        /**
         * @hide
         */
        public HandlerExecutor getHandlerExecutor() {
            return mExecutor;
        public Looper getCreatorLooper() {
            return mCreatorLooper;
        }

        /**
         * Create an OnSubscriptionsChangedListener.
         *
         * For callers targeting {@link Build.VERSION_CODES.P} or earlier, this can only be called
         * on a thread that already has a prepared Looper. Callers targeting Q or later should
         * subsequently use {@link SubscriptionManager#addOnSubscriptionsChangedListener(
         * Executor, OnSubscriptionsChangedListener)}.
         *
         * On OS versions prior to {@link Build.VERSION_CODES.V} callers should assume that this
         * call will fail if invoked on a thread that does not already have a prepared looper.
         */
        public OnSubscriptionsChangedListener() {
            mExecutor = new HandlerExecutor(new OnSubscriptionsChangedListenerHandler());
            mCreatorLooper = Looper.myLooper();
            if (mCreatorLooper == null
                    && !Compatibility.isChangeEnabled(
                            LAZY_INITIALIZE_SUBSCRIPTIONS_CHANGED_HANDLER)) {
                // matches the implementation of Handler
                throw new RuntimeException(
                        "Can't create handler inside thread "
                        + Thread.currentThread()
                        + " that has not called Looper.prepare()");
            }
        }

        /**
         * Allow a listener to be created with a custom looper
         * @param looper the looper that the underlining handler should run on
         * @param looper the non-null Looper that the underlining handler should run on
         * @hide
         */
        public OnSubscriptionsChangedListener(Looper looper) {
            mExecutor = new HandlerExecutor(new OnSubscriptionsChangedListenerHandler(looper));
        public OnSubscriptionsChangedListener(@NonNull Looper looper) {
            Objects.requireNonNull(looper);
            mCreatorLooper = looper;
        }

        /**
@@ -1423,7 +1447,9 @@ public class SubscriptionManager {
    @Deprecated
    public void addOnSubscriptionsChangedListener(OnSubscriptionsChangedListener listener) {
        if (listener == null) return;
        addOnSubscriptionsChangedListener(listener.mExecutor, listener);

        addOnSubscriptionsChangedListener(
                new HandlerExecutor(new Handler(listener.getCreatorLooper())), listener);
    }

    /**