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

Commit ec0e997c authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Automerger Merge Worker
Browse files

Merge "New MessageRouter interface to replace Handler." into sc-v2-dev am: 6154a878

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/14980848

Change-Id: I70e44c393266951519dfbbb200656e57602d9e37
parents 3a2e18b0 6154a878
Loading
Loading
Loading
Loading
+198 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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.systemui.util.concurrency;

/**
 * Allows triggering methods based on a passed in id or message, generally on another thread.
 *
 * Messages sent on to this router must be processed in order. That is to say, if three
 * messages are sent with no delay, they must be processed in the order they were sent. Moreover,
 * if messages are sent with various delays, they must be processed in order of their delay.
 *
 *  Messages can be passed by either a simple integer or an instance of a class. Unique integers are
 *  considered unique messages. Unique message classes (not instances) are considered unique
 *  messages. You can use message classes to pass extra data for processing to subscribers.
 *
 *  <pre>
 *      // Three messages with three unique integer messages.
 *      // They can be subscribed to independently.
 *      router.sendMessage(0);
 *      router.sendMessage(1);
 *      router.sendMessage(2);
 *
 *      // Three messages with two unique message classes.
 *      // The first and third messages will be delivered to the same subscribers.
 *      router.sendMessage(new Foo(0));
 *      router.sendMessage(new Bar(1));
 *      router.sendMessage(new Foo(2));
 *  </pre>
 *
 * The number of unique ids and message types used should be relatively constrained. Construct
 * a custom message-class and put unique, per-message data inside of it.
 */
public interface MessageRouter {
    /**
     * Alerts any listeners subscribed to the passed in id.
     *
     * The number of unique ids used should be relatively constrained - used to identify the type
     * of message being sent. If unique information needs to be passed with each call, use
     * {@link #sendMessage(Object)}.
     *
     * @param id An identifier for the message
     */
    default void sendMessage(int id) {
        sendMessageDelayed(id, 0);
    }

    /**
     * Alerts any listeners subscribed to the passed in message.
     *
     * The number of message types used should be relatively constrained. If no unique information
     * needs to be passed in, you can simply use {@link #sendMessage(int)}} which takes an integer
     * instead of a unique class type.
     *
     * The class of the passed in object will be used to router the message.
     *
     * @param data A message containing extra data for processing.
     */
    default void sendMessage(Object data) {
        sendMessageDelayed(data, 0);
    }

    /**
     * Alerts any listeners subscribed to the passed in id in the future.
     *
     * The number of unique ids used should be relatively constrained - used to identify the type
     * of message being sent. If unique information needs to be passed with each call, use
     * {@link #sendMessageDelayed(Object, long)}.
     *
     * @param id An identifier for the message
     * @param delayMs Number of milliseconds to wait before alerting.
     */
    void sendMessageDelayed(int id, long delayMs);


    /**
     * Alerts any listeners subscribed to the passed in message in the future.
     *
     * The number of message types used should be relatively constrained. If no unique information
     * needs to be passed in, you can simply use {@link #sendMessageDelayed(int, long)} which takes
     * an integer instead of a unique class type.
     *
     * @param data A message containing extra data for processing.
     * @param delayMs Number of milliseconds to wait before alerting.
     */
    void sendMessageDelayed(Object data, long delayMs);

    /**
     * Cancel all unprocessed messages for a given id.
     *
     * If a message has multiple listeners and one of those listeners has been alerted, the other
     * listeners that follow it may also be alerted. This is only guaranteed to cancel messages
     * that are still queued.
     *
     * @param id The message id to cancel.
     */
    void cancelMessages(int id);

    /**
     * Cancel all unprocessed messages for a given message type.
     *
     * If a message has multiple listeners and one of those listeners has been alerted, the other
     * listeners that follow it may also be alerted. This is only guaranteed to cancel messages
     * that are still queued.
     *
     * @param messageType The class of the message to cancel
     */
    <T> void cancelMessages(Class<T> messageType);

    /**
     * Add a listener for a message that does not handle any extra data.
     *
     * See also {@link #subscribeTo(Class, DataMessageListener)}.
     *
     * @param id The message id to listener for.
     * @param listener
     */
    void subscribeTo(int id, SimpleMessageListener listener);

    /**
     * Add a listener for a message of a specific type.
     *
     * See also {@link #subscribeTo(Class, DataMessageListener)}.
     *
     * @param messageType The class of message to listen for.
     * @param listener
     */
    <T> void subscribeTo(Class<T> messageType, DataMessageListener<T> listener);

    /**
     * Remove a listener for a specific message.
     *
     * See also {@link #unsubscribeFrom(Class, DataMessageListener)}
     *
     * @param id The message id to stop listening for.
     * @param listener The listener to remove.
     */
    void unsubscribeFrom(int id, SimpleMessageListener listener);

    /**
     * Remove a listener for a specific message.
     *
     * See also {@link #unsubscribeFrom(int, SimpleMessageListener)}.
     *
     * @param messageType The class of message to stop listening for.
     * @param listener The listener to remove.
     */
    <T> void unsubscribeFrom(Class<T> messageType, DataMessageListener<T> listener);

    /**
     * Remove a listener for all messages that it is subscribed to.
     *
     * See also {@link #unsubscribeFrom(DataMessageListener)}.
     *
     * @param listener The listener to remove.
     */
    void unsubscribeFrom(SimpleMessageListener listener);

    /**
     * Remove a listener for all messages that it is subscribed to.
     *
     * See also {@link #unsubscribeFrom(SimpleMessageListener)}.
     *
     * @param listener The listener to remove.
     */
    <T> void unsubscribeFrom(DataMessageListener<T> listener);

    /**
     * A Listener interface for when no extra data is expected or desired.
     */
    interface SimpleMessageListener {
        /** */
        void onMessage(int id);
    }

    /**
     * A Listener interface for when extra data is expected or desired.
     *
     * @param <T>
     */
    interface DataMessageListener<T> {
        /** */
        void onMessage(T data);
    }
}
+186 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 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.systemui.util.concurrency;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Implementation of {@link MessageRouter}.
 */
public class MessageRouterImpl implements MessageRouter {

    private final DelayableExecutor mDelayableExecutor;

    private final Map<Integer, List<Runnable>> mIdMessageCancelers = new HashMap<>();
    private final Map<Class<Object>, List<Runnable>> mDataMessageCancelers = new HashMap<>();
    private final Map<Integer, List<SimpleMessageListener>> mSimpleMessageListenerMap =
            new HashMap<>();
    private final Map<Class<?>, List<DataMessageListener<Object>>> mDataMessageListenerMap =
            new HashMap<>();

    public MessageRouterImpl(DelayableExecutor delayableExecutor) {
        mDelayableExecutor = delayableExecutor;
    }

    @Override
    public void sendMessageDelayed(int id, long delayMs) {
        addCanceler(id, mDelayableExecutor.executeDelayed(() -> onMessage(id), delayMs));
    }

    @Override
    public void sendMessageDelayed(Object data, long delayMs) {
        addCanceler((Class<Object>) data.getClass(), mDelayableExecutor.executeDelayed(
                () -> onMessage(data), delayMs));
    }

    @Override
    public void cancelMessages(int id) {
        synchronized (mIdMessageCancelers) {
            if (mIdMessageCancelers.containsKey(id)) {
                for (Runnable canceler : mIdMessageCancelers.get(id)) {
                    canceler.run();
                }
                // Remove, don't clear, otherwise this could look like a memory leak as
                // more and more unique message ids are passed in.
                mIdMessageCancelers.remove(id);
            }
        }
    }

    @Override
    public <T> void cancelMessages(Class<T> messageType) {
        synchronized (mDataMessageCancelers) {
            if (mDataMessageCancelers.containsKey(messageType)) {
                for (Runnable canceler : mDataMessageCancelers.get(messageType)) {
                    canceler.run();
                }
                // Remove, don't clear, otherwise this could look like a memory leak as
                // more and more unique message types are passed in.
                mDataMessageCancelers.remove(messageType);
            }
        }
    }

    @Override
    public void subscribeTo(int id, SimpleMessageListener listener) {
        synchronized (mSimpleMessageListenerMap) {
            mSimpleMessageListenerMap.putIfAbsent(id, new ArrayList<>());
            mSimpleMessageListenerMap.get(id).add(listener);
        }
    }

    @Override
    public <T> void subscribeTo(Class<T> messageType, DataMessageListener<T> listener) {
        synchronized (mDataMessageListenerMap) {
            mDataMessageListenerMap.putIfAbsent(messageType, new ArrayList<>());
            mDataMessageListenerMap.get(messageType).add((DataMessageListener<Object>) listener);
        }
    }

    @Override
    public void unsubscribeFrom(int id, SimpleMessageListener listener) {
        synchronized (mSimpleMessageListenerMap) {
            if (mSimpleMessageListenerMap.containsKey(id)) {
                mSimpleMessageListenerMap.get(id).remove(listener);
            }
        }
    }

    @Override
    public <T> void unsubscribeFrom(Class<T> messageType, DataMessageListener<T> listener) {
        synchronized (mDataMessageListenerMap) {
            if (mDataMessageListenerMap.containsKey(messageType)) {
                mDataMessageListenerMap.get(messageType).remove(listener);
            }
        }
    }

    @Override
    public void unsubscribeFrom(SimpleMessageListener listener) {
        synchronized (mSimpleMessageListenerMap) {
            for (Integer id : mSimpleMessageListenerMap.keySet()) {
                mSimpleMessageListenerMap.get(id).remove(listener);
            }
        }
    }

    @Override
    public <T> void unsubscribeFrom(DataMessageListener<T> listener) {
        synchronized (mDataMessageListenerMap) {
            for (Class<?> messageType : mDataMessageListenerMap.keySet()) {
                mDataMessageListenerMap.get(messageType).remove(listener);
            }
        }
    }

    private void addCanceler(int id, Runnable canceler) {
        synchronized (mIdMessageCancelers) {
            mIdMessageCancelers.putIfAbsent(id, new ArrayList<>());
            mIdMessageCancelers.get(id).add(canceler);
        }
    }

    private void addCanceler(Class<Object> data, Runnable canceler) {
        synchronized (mDataMessageCancelers) {
            mDataMessageCancelers.putIfAbsent(data, new ArrayList<>());
            mDataMessageCancelers.get(data).add(canceler);
        }
    }

    private void onMessage(int id) {
        synchronized (mSimpleMessageListenerMap) {
            if (mSimpleMessageListenerMap.containsKey(id)) {
                for (SimpleMessageListener listener : mSimpleMessageListenerMap.get(id)) {
                    listener.onMessage(id);
                }
            }
        }

        synchronized (mIdMessageCancelers) {
            if (mIdMessageCancelers.containsKey(id) && !mIdMessageCancelers.get(id).isEmpty()) {
                mIdMessageCancelers.get(id).remove(0);
                if (mIdMessageCancelers.get(id).isEmpty()) {
                    mIdMessageCancelers.remove(id);
                }
            }
        }
    }

    private void onMessage(Object data) {
        synchronized (mDataMessageListenerMap) {
            if (mDataMessageListenerMap.containsKey(data.getClass())) {
                for (DataMessageListener<Object> listener : mDataMessageListenerMap.get(
                        data.getClass())) {
                    listener.onMessage(data);
                }
            }
        }

        synchronized (mDataMessageCancelers) {
            if (mDataMessageCancelers.containsKey(data.getClass())
                    && !mDataMessageCancelers.get(data.getClass()).isEmpty()) {
                mDataMessageCancelers.get(data.getClass()).remove(0);
                if (mDataMessageCancelers.get(data.getClass()).isEmpty()) {
                    mDataMessageCancelers.remove(data.getClass());
                }
            }
        }
    }
}
+16 −0
Original line number Diff line number Diff line
@@ -170,4 +170,20 @@ public abstract class SysUIConcurrencyModule {
    public static Executor provideUiBackgroundExecutor() {
        return Executors.newSingleThreadExecutor();
    }

    /** */
    @Provides
    @Main
    public static MessageRouter providesMainMessageRouter(
            @Main DelayableExecutor executor) {
        return new MessageRouterImpl(executor);
    }

    /** */
    @Provides
    @Background
    public static MessageRouter providesBackgroundMessageRouter(
            @Background DelayableExecutor executor) {
        return new MessageRouterImpl(executor);
    }
}
+24 −35
Original line number Diff line number Diff line
@@ -36,7 +36,6 @@ import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.os.SystemProperties;
import android.provider.Settings;
@@ -60,6 +59,8 @@ import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSIconViewImpl;
import com.android.systemui.qs.tileimpl.QSTileImpl;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.concurrency.MessageRouter;

import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -110,18 +111,18 @@ public class GarbageMonitor implements Dumpable {
    private static final int DO_GARBAGE_INSPECTION = 1000;
    private static final int DO_HEAP_TRACK = 3000;

    private static final int GARBAGE_ALLOWANCE = 5;
    static final int GARBAGE_ALLOWANCE = 5;

    private static final String TAG = "GarbageMonitor";
    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);

    private final Handler mHandler;
    private final MessageRouter mMessageRouter;
    private final TrackedGarbage mTrackedGarbage;
    private final LeakReporter mLeakReporter;
    private final Context mContext;
    private final ActivityManager mAm;
    private final DelayableExecutor mDelayableExecutor;
    private MemoryTile mQSTile;
    private DumpTruck mDumpTruck;
    private final DumpTruck mDumpTruck;

    private final LongSparseArray<ProcessMemInfo> mData = new LongSparseArray<>();
    private final ArrayList<Long> mPids = new ArrayList<>();
@@ -133,13 +134,16 @@ public class GarbageMonitor implements Dumpable {
    @Inject
    public GarbageMonitor(
            Context context,
            @Background Looper bgLooper,
            @Background DelayableExecutor delayableExecutor,
            @Background MessageRouter messageRouter,
            LeakDetector leakDetector,
            LeakReporter leakReporter) {
        mContext = context.getApplicationContext();
        mAm = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);

        mHandler = new BackgroundHeapCheckHandler(bgLooper);
        mDelayableExecutor = delayableExecutor;
        mMessageRouter = messageRouter;
        mMessageRouter.subscribeTo(DO_GARBAGE_INSPECTION, this::doGarbageInspection);
        mMessageRouter.subscribeTo(DO_HEAP_TRACK, this::doHeapTrack);

        mTrackedGarbage = leakDetector.getTrackedGarbage();
        mLeakReporter = leakReporter;
@@ -158,13 +162,13 @@ public class GarbageMonitor implements Dumpable {
            return;
        }

        mHandler.sendEmptyMessage(DO_GARBAGE_INSPECTION);
        mMessageRouter.sendMessage(DO_GARBAGE_INSPECTION);
    }

    public void startHeapTracking() {
        startTrackingProcess(
                android.os.Process.myPid(), mContext.getPackageName(), System.currentTimeMillis());
        mHandler.sendEmptyMessage(DO_HEAP_TRACK);
        mMessageRouter.sendMessage(DO_HEAP_TRACK);
    }

    private boolean gcAndCheckGarbage() {
@@ -586,33 +590,18 @@ public class GarbageMonitor implements Dumpable {
        }
    }

    private class BackgroundHeapCheckHandler extends Handler {
        BackgroundHeapCheckHandler(Looper onLooper) {
            super(onLooper);
            if (Looper.getMainLooper().equals(onLooper)) {
                throw new RuntimeException(
                        "BackgroundHeapCheckHandler may not run on the ui thread");
            }
        }

        @Override
        public void handleMessage(Message m) {
            switch (m.what) {
                case DO_GARBAGE_INSPECTION:
    private void doGarbageInspection(int id) {
        if (gcAndCheckGarbage()) {
                        postDelayed(GarbageMonitor.this::reinspectGarbageAfterGc, 100);
            mDelayableExecutor.executeDelayed(this::reinspectGarbageAfterGc, 100);
        }

                    removeMessages(DO_GARBAGE_INSPECTION);
                    sendEmptyMessageDelayed(DO_GARBAGE_INSPECTION, GARBAGE_INSPECTION_INTERVAL);
                    break;
        mMessageRouter.cancelMessages(DO_GARBAGE_INSPECTION);
        mMessageRouter.sendMessageDelayed(DO_GARBAGE_INSPECTION, GARBAGE_INSPECTION_INTERVAL);
    }

                case DO_HEAP_TRACK:
    private void doHeapTrack(int id) {
        update();
                    removeMessages(DO_HEAP_TRACK);
                    sendEmptyMessageDelayed(DO_HEAP_TRACK, HEAP_TRACK_INTERVAL);
                    break;
            }
        }
        mMessageRouter.cancelMessages(DO_HEAP_TRACK);
        mMessageRouter.sendMessageDelayed(DO_HEAP_TRACK, HEAP_TRACK_INTERVAL);
    }
}
+371 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading