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

Commit 7e72d90e authored by Jeff Sharkey's avatar Jeff Sharkey Committed by Jeff Sharkey
Browse files

BroadcastQueue: make "oneway" calls non-blocking.

When an IApplicationThread instance is hosted by the system_server
process, our various schedule-broadcast calls are dispatched as
blocking methods (with the AMS lock held!!), instead of matching the
expected "oneway" contract.

This change offers ProcessRecord.getOnewayThread() which dispatches
calls through FgThread using SameProcessApplicationThread, which
starts with initial support for broadcast-related methods, but could
be expanded in the future where needed.

Bug: 255532202
Test: atest FrameworksMockingServicesTests:BroadcastRecordTest
Test: atest FrameworksMockingServicesTests:BroadcastQueueTest
Test: atest FrameworksMockingServicesTests:BroadcastQueueModernImplTest
Change-Id: I7fb2a7353c44c29cda94e00fc87b8cff6ffc0008
parent dd09e2ad
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -349,7 +349,7 @@ class BroadcastProcessQueue {
     * Return if we know of an actively running "warm" process for this queue.
     */
    public boolean isProcessWarm() {
        return (app != null) && (app.getThread() != null) && !app.isKilled();
        return (app != null) && (app.getOnewayThread() != null) && !app.isKilled();
    }

    public int getPreferredSchedulingGroupLocked() {
+2 −2
Original line number Diff line number Diff line
@@ -736,7 +736,7 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
        if (DEBUG_BROADCAST) logv("Scheduling " + r + " to warm " + app);
        setDeliveryState(queue, app, r, index, receiver, BroadcastRecord.DELIVERY_SCHEDULED);

        final IApplicationThread thread = app.getThread();
        final IApplicationThread thread = app.getOnewayThread();
        if (thread != null) {
            try {
                if (receiver instanceof BroadcastFilter) {
@@ -777,7 +777,7 @@ class BroadcastQueueModernImpl extends BroadcastQueue {
    private void scheduleResultTo(@NonNull BroadcastRecord r) {
        if ((r.resultToApp == null) || (r.resultTo == null)) return;
        final ProcessRecord app = r.resultToApp;
        final IApplicationThread thread = app.getThread();
        final IApplicationThread thread = app.getOnewayThread();
        if (thread != null) {
            mService.mOomAdjuster.mCachedAppOptimizer.unfreezeTemporarily(
                    app, OOM_ADJ_REASON_FINISH_RECEIVER);
+19 −0
Original line number Diff line number Diff line
@@ -54,6 +54,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.procstats.ProcessState;
import com.android.internal.app.procstats.ProcessStats;
import com.android.internal.os.Zygote;
import com.android.server.FgThread;
import com.android.server.wm.WindowProcessController;
import com.android.server.wm.WindowProcessListener;

@@ -142,6 +143,13 @@ class ProcessRecord implements WindowProcessListener {
    @CompositeRWLock({"mService", "mProcLock"})
    private IApplicationThread mThread;

    /**
     * Instance of {@link #mThread} that will always meet the {@code oneway}
     * contract, possibly by using {@link SameProcessApplicationThread}.
     */
    @CompositeRWLock({"mService", "mProcLock"})
    private IApplicationThread mOnewayThread;

    /**
     * Always keep this application running?
     */
@@ -603,16 +611,27 @@ class ProcessRecord implements WindowProcessListener {
        return mThread;
    }

    @GuardedBy(anyOf = {"mService", "mProcLock"})
    IApplicationThread getOnewayThread() {
        return mOnewayThread;
    }

    @GuardedBy({"mService", "mProcLock"})
    public void makeActive(IApplicationThread thread, ProcessStatsService tracker) {
        mProfile.onProcessActive(thread, tracker);
        mThread = thread;
        if (mPid == Process.myPid()) {
            mOnewayThread = new SameProcessApplicationThread(thread, FgThread.getHandler());
        } else {
            mOnewayThread = thread;
        }
        mWindowProcessController.setThread(thread);
    }

    @GuardedBy({"mService", "mProcLock"})
    public void makeInactive(ProcessStatsService tracker) {
        mThread = null;
        mOnewayThread = null;
        mWindowProcessController.setThread(null);
        mProfile.onProcessInactive(tracker);
    }
+73 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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.server.am;

import android.annotation.NonNull;
import android.app.IApplicationThread;
import android.content.IIntentReceiver;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.res.CompatibilityInfo;
import android.os.Bundle;
import android.os.Handler;
import android.os.RemoteException;

import java.util.Objects;

/**
 * Wrapper around an {@link IApplicationThread} that delegates selected calls
 * through a {@link Handler} so they meet the {@code oneway} contract of
 * returning immediately after dispatch.
 */
public class SameProcessApplicationThread extends IApplicationThread.Default {
    private final IApplicationThread mWrapped;
    private final Handler mHandler;

    public SameProcessApplicationThread(@NonNull IApplicationThread wrapped,
            @NonNull Handler handler) {
        mWrapped = Objects.requireNonNull(wrapped);
        mHandler = Objects.requireNonNull(handler);
    }

    @Override
    public void scheduleReceiver(Intent intent, ActivityInfo info, CompatibilityInfo compatInfo,
            int resultCode, String data, Bundle extras, boolean sync, int sendingUser,
            int processState) {
        mHandler.post(() -> {
            try {
                mWrapped.scheduleReceiver(intent, info, compatInfo, resultCode, data, extras, sync,
                        sendingUser, processState);
            } catch (RemoteException e) {
                throw new RuntimeException(e);
            }
        });
    }

    @Override
    public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent, int resultCode,
            String data, Bundle extras, boolean ordered, boolean sticky, int sendingUser,
            int processState) {
        mHandler.post(() -> {
            try {
                mWrapped.scheduleRegisteredReceiver(receiver, intent, resultCode, data, extras,
                        ordered, sticky, sendingUser, processState);
            } catch (RemoteException e) {
                throw new RuntimeException(e);
            }
        });
    }
}
+4 −0
Original line number Diff line number Diff line
@@ -87,6 +87,10 @@ public class BroadcastQueueModernImplTest {
        mHandlerThread.start();

        mConstants = new BroadcastConstants(Settings.Global.BROADCAST_FG_CONSTANTS);
        mConstants.DELAY_URGENT_MILLIS = -120_000;
        mConstants.DELAY_NORMAL_MILLIS = 10_000;
        mConstants.DELAY_CACHED_MILLIS = 120_000;

        mImpl = new BroadcastQueueModernImpl(mAms, mHandlerThread.getThreadHandler(),
                mConstants, mConstants);

Loading