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

Commit 920a6762 authored by Kyunglyul Hyun's avatar Kyunglyul Hyun
Browse files

Bind provider services when necessary

The media router service bound media route provider
services only when there was a discovery request.

This prevented output switcher to work when there are only
legacy media router clients (i.e. apps that didn't enable MR2)

This CL makes the media router service binds media route provider
services when the MediaRouter2Manager#startScan is called
so that the output switcher can show cast sessions created by
the providers.

This CL also changes communication between the media router service and
the provider services as cast sessions can be created/released even when the
media router service is disconnected from media route provider.

Bug: 176774510
Test: manually

Change-Id: I481e1aa8f9dcc136cbf5788b9f66540708665af2
parent 680d09ed
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -26,9 +26,9 @@ import android.os.Bundle;
 */
oneway interface IMediaRoute2ProviderServiceCallback {
    // TODO: Change it to updateRoutes?
    void updateState(in MediaRoute2ProviderInfo providerInfo);
    void notifyProviderUpdated(in MediaRoute2ProviderInfo providerInfo);
    void notifySessionCreated(long requestId, in RoutingSessionInfo sessionInfo);
    void notifySessionUpdated(in RoutingSessionInfo sessionInfo);
    void notifySessionsUpdated(in List<RoutingSessionInfo> sessionInfo);
    void notifySessionReleased(in RoutingSessionInfo sessionInfo);
    void notifyRequestFailed(long requestId, int reason);
}
+32 −10
Original line number Diff line number Diff line
@@ -141,6 +141,7 @@ public abstract class MediaRoute2ProviderService extends Service {
    private final Object mSessionLock = new Object();
    private final Object mRequestIdsLock = new Object();
    private final AtomicBoolean mStatePublishScheduled = new AtomicBoolean(false);
    private final AtomicBoolean mSessionUpdateScheduled = new AtomicBoolean(false);
    private MediaRoute2ProviderServiceStub mStub;
    private IMediaRoute2ProviderServiceCallback mRemoteCallback;
    private volatile MediaRoute2ProviderInfo mProviderInfo;
@@ -287,16 +288,8 @@ public abstract class MediaRoute2ProviderService extends Service {
                Log.w(TAG, "notifySessionUpdated: Ignoring unknown session info.");
                return;
            }

            if (mRemoteCallback == null) {
                return;
            }
            try {
                mRemoteCallback.notifySessionUpdated(sessionInfo);
            } catch (RemoteException ex) {
                Log.w(TAG, "Failed to notify session info changed.");
            }
        }
        scheduleUpdateSessions();
    }

    /**
@@ -479,6 +472,7 @@ public abstract class MediaRoute2ProviderService extends Service {
    void setCallback(IMediaRoute2ProviderServiceCallback callback) {
        mRemoteCallback = callback;
        schedulePublishState();
        scheduleUpdateSessions();
    }

    void schedulePublishState() {
@@ -497,12 +491,40 @@ public abstract class MediaRoute2ProviderService extends Service {
        }

        try {
            mRemoteCallback.updateState(mProviderInfo);
            mRemoteCallback.notifyProviderUpdated(mProviderInfo);
        } catch (RemoteException ex) {
            Log.w(TAG, "Failed to publish provider state.", ex);
        }
    }

    void scheduleUpdateSessions() {
        if (mSessionUpdateScheduled.compareAndSet(false, true)) {
            mHandler.post(this::updateSessions);
        }
    }

    private void updateSessions() {
        if (!mSessionUpdateScheduled.compareAndSet(true, false)) {
            return;
        }

        if (mRemoteCallback == null) {
            return;
        }

        List<RoutingSessionInfo> sessions;
        synchronized (mSessionLock) {
            sessions = new ArrayList<>(mSessionInfo.values());
        }

        try {
            mRemoteCallback.notifySessionsUpdated(sessions);
        } catch (RemoteException ex) {
            Log.w(TAG, "Failed to notify session info changed.");
        }

    }

    /**
     * Adds a requestId in the request ID list whose max size is {@link #MAX_REQUEST_IDS_SIZE}.
     * When the max size is reached, the first element is removed (FIFO).
+1 −1
Original line number Diff line number Diff line
@@ -47,7 +47,7 @@ abstract class MediaRoute2Provider {
        mUniqueId = componentName.flattenToShortString();
    }

    public void setCallback(MediaRoute2ProviderServiceProxy.Callback callback) {
    public void setCallback(Callback callback) {
        mCallback = callback;
    }

+70 −38
Original line number Diff line number Diff line
@@ -16,6 +16,10 @@

package com.android.server.media;

import static android.media.MediaRoute2ProviderService.REQUEST_ID_NONE;

import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;

import android.annotation.NonNull;
import android.content.ComponentName;
import android.content.Context;
@@ -43,6 +47,7 @@ import com.android.internal.annotations.GuardedBy;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

@@ -64,6 +69,7 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider
    private Connection mActiveConnection;
    private boolean mConnectionReady;

    private boolean mIsManagerScanning;
    private RouteDiscoveryPreference mLastDiscoveryPreference = null;

    @GuardedBy("mLock")
@@ -86,6 +92,13 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider
        pw.println(prefix + "  mConnectionReady=" + mConnectionReady);
    }

    public void setManagerScanning(boolean managerScanning) {
        if (mIsManagerScanning != managerScanning) {
            mIsManagerScanning = managerScanning;
            updateBinding();
        }
    }

    @Override
    public void requestCreateSession(long requestId, String packageName, String routeId,
            Bundle sessionHints) {
@@ -209,7 +222,8 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider
            // Bind when there is a discovery preference or an active route session.
            return (mLastDiscoveryPreference != null
                    && !mLastDiscoveryPreference.getPreferredFeatures().isEmpty())
                    || !getSessionInfos().isEmpty();
                    || !getSessionInfos().isEmpty()
                    || mIsManagerScanning;
        }
        return false;
    }
@@ -311,13 +325,12 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider
        }
    }

    private void onProviderStateUpdated(Connection connection,
            MediaRoute2ProviderInfo providerInfo) {
    private void onProviderUpdated(Connection connection, MediaRoute2ProviderInfo providerInfo) {
        if (mActiveConnection != connection) {
            return;
        }
        if (DEBUG) {
            Slog.d(TAG, this + ": State changed ");
            Slog.d(TAG, this + ": updated");
        }
        setAndNotifyProviderState(providerInfo);
    }
@@ -350,40 +363,44 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider
        mCallback.onSessionCreated(this, requestId, newSession);
    }

    private void onSessionUpdated(Connection connection, RoutingSessionInfo updatedSession) {
        if (mActiveConnection != connection) {
            return;
    private int findSessionByIdLocked(RoutingSessionInfo session) {
        for (int i = 0; i < mSessionInfos.size(); i++) {
            if (TextUtils.equals(mSessionInfos.get(i).getId(), session.getId())) {
                return i;
            }
        if (updatedSession == null) {
            Slog.w(TAG, "onSessionUpdated: Ignoring null session sent from "
                    + mComponentName);
            return;
        }
        return -1;
    }

        updatedSession = assignProviderIdForSession(updatedSession);

        boolean found = false;
        synchronized (mLock) {
            for (int i = 0; i < mSessionInfos.size(); i++) {
                if (mSessionInfos.get(i).getId().equals(updatedSession.getId())) {
                    mSessionInfos.set(i, updatedSession);
                    found = true;
                    break;
                }
    private void onSessionsUpdated(Connection connection, List<RoutingSessionInfo> sessions) {
        if (mActiveConnection != connection) {
            return;
        }

            if (!found) {
                for (RoutingSessionInfo releasingSession : mReleasingSessions) {
                    if (TextUtils.equals(releasingSession.getId(), updatedSession.getId())) {
                        return;
        int targetIndex = 0;
        synchronized (mLock) {
            for (RoutingSessionInfo session : sessions) {
                if (session == null) continue;
                session = assignProviderIdForSession(session);

                int sourceIndex = findSessionByIdLocked(session);
                if (sourceIndex < 0) {
                    mSessionInfos.add(targetIndex++, session);
                    dispatchSessionCreated(REQUEST_ID_NONE, session);
                } else if (sourceIndex < targetIndex) {
                    Slog.w(TAG, "Ignoring duplicate session ID: " + session.getId());
                } else {
                    mSessionInfos.set(sourceIndex, session);
                    Collections.swap(mSessionInfos, sourceIndex, targetIndex++);
                    dispatchSessionUpdated(session);
                }
            }
                Slog.w(TAG, "onSessionUpdated: Matching session info not found");
                return;
            for (int i = mSessionInfos.size() - 1; i >= targetIndex; i--) {
                RoutingSessionInfo releasedSession = mSessionInfos.remove(i);
                dispatchSessionReleased(releasedSession);
            }
        }

        mCallback.onSessionUpdated(this, updatedSession);
    }

    private void onSessionReleased(Connection connection, RoutingSessionInfo releaedSession) {
@@ -424,6 +441,21 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider
        mCallback.onSessionReleased(this, releaedSession);
    }

    private void dispatchSessionCreated(long requestId, RoutingSessionInfo session) {
        mHandler.sendMessage(
                obtainMessage(mCallback::onSessionCreated, this, requestId, session));
    }

    private void dispatchSessionUpdated(RoutingSessionInfo session) {
        mHandler.sendMessage(
                obtainMessage(mCallback::onSessionUpdated, this, session));
    }

    private void dispatchSessionReleased(RoutingSessionInfo session) {
        mHandler.sendMessage(
                obtainMessage(mCallback::onSessionReleased, this, session));
    }

    private RoutingSessionInfo assignProviderIdForSession(RoutingSessionInfo sessionInfo) {
        return new RoutingSessionInfo.Builder(sessionInfo)
                .setOwnerPackageName(mComponentName.getPackageName())
@@ -436,7 +468,7 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider
            return;
        }

        if (requestId == MediaRoute2ProviderService.REQUEST_ID_NONE) {
        if (requestId == REQUEST_ID_NONE) {
            Slog.w(TAG, "onRequestFailed: Ignoring requestId REQUEST_ID_NONE");
            return;
        }
@@ -561,16 +593,16 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider
            mHandler.post(() -> onConnectionDied(Connection.this));
        }

        void postProviderStateUpdated(MediaRoute2ProviderInfo providerInfo) {
            mHandler.post(() -> onProviderStateUpdated(Connection.this, providerInfo));
        void postProviderUpdated(MediaRoute2ProviderInfo providerInfo) {
            mHandler.post(() -> onProviderUpdated(Connection.this, providerInfo));
        }

        void postSessionCreated(long requestId, RoutingSessionInfo sessionInfo) {
            mHandler.post(() -> onSessionCreated(Connection.this, requestId, sessionInfo));
        }

        void postSessionUpdated(RoutingSessionInfo sessionInfo) {
            mHandler.post(() -> onSessionUpdated(Connection.this, sessionInfo));
        void postSessionsUpdated(List<RoutingSessionInfo> sessionInfo) {
            mHandler.post(() -> onSessionsUpdated(Connection.this, sessionInfo));
        }

        void postSessionReleased(RoutingSessionInfo sessionInfo) {
@@ -595,10 +627,10 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider
        }

        @Override
        public void updateState(MediaRoute2ProviderInfo providerInfo) {
        public void notifyProviderUpdated(MediaRoute2ProviderInfo providerInfo) {
            Connection connection = mConnectionRef.get();
            if (connection != null) {
                connection.postProviderStateUpdated(providerInfo);
                connection.postProviderUpdated(providerInfo);
            }
        }

@@ -611,10 +643,10 @@ final class MediaRoute2ProviderServiceProxy extends MediaRoute2Provider
        }

        @Override
        public void notifySessionUpdated(RoutingSessionInfo sessionInfo) {
        public void notifySessionsUpdated(List<RoutingSessionInfo> sessionInfo) {
            Connection connection = mConnectionRef.get();
            if (connection != null) {
                connection.postSessionUpdated(sessionInfo);
                connection.postSessionsUpdated(sessionInfo);
            }
        }

+10 −0
Original line number Diff line number Diff line
@@ -2156,6 +2156,8 @@ class MediaRouter2ServiceImpl {
            List<RouterRecord> routerRecords = getRouterRecords();
            List<ManagerRecord> managerRecords = getManagerRecords();

            boolean shouldBindProviders = false;

            if (service.mPowerManager.isInteractive()) {
                boolean isManagerScanning = managerRecords.stream().anyMatch(manager ->
                        manager.mIsScanning && service.mActivityManager
@@ -2166,6 +2168,7 @@ class MediaRouter2ServiceImpl {
                    discoveryPreferences = routerRecords.stream()
                            .map(record -> record.mDiscoveryPreference)
                            .collect(Collectors.toList());
                    shouldBindProviders = true;
                } else {
                    discoveryPreferences = routerRecords.stream().filter(record ->
                            service.mActivityManager.getPackageImportance(record.mPackageName)
@@ -2175,6 +2178,13 @@ class MediaRouter2ServiceImpl {
                }
            }

            for (MediaRoute2Provider provider : mRouteProviders) {
                if (provider instanceof MediaRoute2ProviderServiceProxy) {
                    ((MediaRoute2ProviderServiceProxy) provider)
                            .setManagerScanning(shouldBindProviders);
                }
            }

            synchronized (service.mLock) {
                RouteDiscoveryPreference newPreference =
                        new RouteDiscoveryPreference.Builder(discoveryPreferences).build();