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

Commit 4e762c22 authored by Alex Dadukin's avatar Alex Dadukin Committed by Automerger Merge Worker
Browse files

Merge "Extract wired routes logic into a separate class." into udc-dev am: b766473c

parents 11ed11b9 b766473c
Loading
Loading
Loading
Loading
+193 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2023 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.media;

import static android.media.MediaRoute2Info.FEATURE_LIVE_AUDIO;
import static android.media.MediaRoute2Info.FEATURE_LIVE_VIDEO;
import static android.media.MediaRoute2Info.FEATURE_LOCAL_PLAYBACK;
import static android.media.MediaRoute2Info.TYPE_BUILTIN_SPEAKER;
import static android.media.MediaRoute2Info.TYPE_DOCK;
import static android.media.MediaRoute2Info.TYPE_HDMI;
import static android.media.MediaRoute2Info.TYPE_USB_DEVICE;
import static android.media.MediaRoute2Info.TYPE_WIRED_HEADPHONES;
import static android.media.MediaRoute2Info.TYPE_WIRED_HEADSET;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.media.AudioManager;
import android.media.AudioRoutesInfo;
import android.media.IAudioRoutesObserver;
import android.media.IAudioService;
import android.media.MediaRoute2Info;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Slog;

import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;

import java.util.Objects;

/**
 * Controls device routes.
 *
 * <p>A device route is a system wired route, for example, built-in speaker, wired
 * headsets and headphones, dock, hdmi, or usb devices.
 *
 * <p>Thread safe.
 *
 * @see SystemMediaRoute2Provider
 */
/* package */ final class DeviceRouteController {

    private static final String TAG = "WiredRoutesController";

    private static final String DEVICE_ROUTE_ID = "DEVICE_ROUTE";

    @NonNull
    private final Context mContext;
    @NonNull
    private final AudioManager mAudioManager;
    @NonNull
    private final IAudioService mAudioService;

    @NonNull
    private final OnDeviceRouteChangedListener mOnDeviceRouteChangedListener;
    @NonNull
    private final AudioRoutesObserver mAudioRoutesObserver = new AudioRoutesObserver();

    private int mDeviceVolume;
    private MediaRoute2Info mDeviceRoute;

    /* package */ static DeviceRouteController createInstance(@NonNull Context context,
            @NonNull OnDeviceRouteChangedListener onDeviceRouteChangedListener) {
        AudioManager audioManager = context.getSystemService(AudioManager.class);
        IAudioService audioService = IAudioService.Stub.asInterface(
                ServiceManager.getService(Context.AUDIO_SERVICE));

        return new DeviceRouteController(context,
                audioManager,
                audioService,
                onDeviceRouteChangedListener);
    }

    @VisibleForTesting
    /* package */ DeviceRouteController(@NonNull Context context,
            @NonNull AudioManager audioManager,
            @NonNull IAudioService audioService,
            @NonNull OnDeviceRouteChangedListener onDeviceRouteChangedListener) {
        Objects.requireNonNull(context);
        Objects.requireNonNull(audioManager);
        Objects.requireNonNull(audioService);
        Objects.requireNonNull(onDeviceRouteChangedListener);

        mContext = context;
        mOnDeviceRouteChangedListener = onDeviceRouteChangedListener;

        mAudioManager = audioManager;
        mAudioService = audioService;

        AudioRoutesInfo newAudioRoutes = null;
        try {
            newAudioRoutes = mAudioService.startWatchingRoutes(mAudioRoutesObserver);
        } catch (RemoteException e) {
            Slog.w(TAG, "Cannot connect to audio service to start listen to routes", e);
        }

        mDeviceRoute = createRouteFromAudioInfo(newAudioRoutes);
    }

    @NonNull
    /* package */ synchronized MediaRoute2Info getDeviceRoute() {
        return mDeviceRoute;
    }

    /* package */ synchronized boolean updateVolume(int volume) {
        if (mDeviceVolume == volume) {
            return false;
        }

        mDeviceVolume = volume;
        mDeviceRoute = new MediaRoute2Info.Builder(mDeviceRoute)
                .setVolume(volume)
                .build();

        return true;
    }

    private MediaRoute2Info createRouteFromAudioInfo(@Nullable AudioRoutesInfo newRoutes) {
        int name = R.string.default_audio_route_name;
        int type = TYPE_BUILTIN_SPEAKER;

        if (newRoutes != null) {
            if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HEADPHONES) != 0) {
                type = TYPE_WIRED_HEADPHONES;
                name = com.android.internal.R.string.default_audio_route_name_headphones;
            } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HEADSET) != 0) {
                type = TYPE_WIRED_HEADSET;
                name = com.android.internal.R.string.default_audio_route_name_headphones;
            } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_DOCK_SPEAKERS) != 0) {
                type = TYPE_DOCK;
                name = com.android.internal.R.string.default_audio_route_name_dock_speakers;
            } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HDMI) != 0) {
                type = TYPE_HDMI;
                name = com.android.internal.R.string.default_audio_route_name_external_device;
            } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_USB) != 0) {
                type = TYPE_USB_DEVICE;
                name = com.android.internal.R.string.default_audio_route_name_usb;
            }
        }

        synchronized (this) {
            return new MediaRoute2Info.Builder(
                    DEVICE_ROUTE_ID, mContext.getResources().getText(name).toString())
                    .setVolumeHandling(mAudioManager.isVolumeFixed()
                            ? MediaRoute2Info.PLAYBACK_VOLUME_FIXED
                            : MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE)
                    .setVolume(mDeviceVolume)
                    .setVolumeMax(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC))
                    .setType(type)
                    .addFeature(FEATURE_LIVE_AUDIO)
                    .addFeature(FEATURE_LIVE_VIDEO)
                    .addFeature(FEATURE_LOCAL_PLAYBACK)
                    .setConnectionState(MediaRoute2Info.CONNECTION_STATE_CONNECTED)
                    .build();
        }
    }

    private void notifyDeviceRouteUpdate(@NonNull MediaRoute2Info deviceRoute) {
        mOnDeviceRouteChangedListener.onDeviceRouteChanged(deviceRoute);
    }

    /* package */ interface OnDeviceRouteChangedListener {
        void onDeviceRouteChanged(@NonNull MediaRoute2Info deviceRoute);
    }

    private class AudioRoutesObserver extends IAudioRoutesObserver.Stub {

        @Override
        public void dispatchAudioRoutesChanged(AudioRoutesInfo newAudioRoutes) {
            MediaRoute2Info deviceRoute = createRouteFromAudioInfo(newAudioRoutes);
            synchronized (DeviceRouteController.this) {
                mDeviceRoute = deviceRoute;
            }
            notifyDeviceRouteUpdate(deviceRoute);
        }
    }

}
+29 −91
Original line number Original line Diff line number Diff line
@@ -16,25 +16,12 @@


package com.android.server.media;
package com.android.server.media;


import static android.media.MediaRoute2Info.FEATURE_LIVE_AUDIO;
import static android.media.MediaRoute2Info.FEATURE_LIVE_VIDEO;
import static android.media.MediaRoute2Info.FEATURE_LOCAL_PLAYBACK;
import static android.media.MediaRoute2Info.TYPE_BUILTIN_SPEAKER;
import static android.media.MediaRoute2Info.TYPE_DOCK;
import static android.media.MediaRoute2Info.TYPE_HDMI;
import static android.media.MediaRoute2Info.TYPE_USB_DEVICE;
import static android.media.MediaRoute2Info.TYPE_WIRED_HEADPHONES;
import static android.media.MediaRoute2Info.TYPE_WIRED_HEADSET;

import android.content.BroadcastReceiver;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ComponentName;
import android.content.Context;
import android.content.Context;
import android.content.Intent;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentFilter;
import android.media.AudioManager;
import android.media.AudioManager;
import android.media.AudioRoutesInfo;
import android.media.IAudioRoutesObserver;
import android.media.IAudioService;
import android.media.MediaRoute2Info;
import android.media.MediaRoute2Info;
import android.media.MediaRoute2ProviderInfo;
import android.media.MediaRoute2ProviderInfo;
import android.media.MediaRoute2ProviderService;
import android.media.MediaRoute2ProviderService;
@@ -43,14 +30,11 @@ import android.media.RoutingSessionInfo;
import android.os.Bundle;
import android.os.Bundle;
import android.os.Handler;
import android.os.Handler;
import android.os.Looper;
import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.UserHandle;
import android.text.TextUtils;
import android.text.TextUtils;
import android.util.Log;
import android.util.Log;
import android.util.Slog;
import android.util.Slog;


import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.GuardedBy;


import java.util.Objects;
import java.util.Objects;
@@ -68,23 +52,21 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
            SystemMediaRoute2Provider.class.getName());
            SystemMediaRoute2Provider.class.getName());


    static final String DEFAULT_ROUTE_ID = "DEFAULT_ROUTE";
    static final String DEFAULT_ROUTE_ID = "DEFAULT_ROUTE";
    static final String DEVICE_ROUTE_ID = "DEVICE_ROUTE";
    static final String SYSTEM_SESSION_ID = "SYSTEM_SESSION";
    static final String SYSTEM_SESSION_ID = "SYSTEM_SESSION";


    private final AudioManager mAudioManager;
    private final AudioManager mAudioManager;
    private final IAudioService mAudioService;
    private final Handler mHandler;
    private final Handler mHandler;
    private final Context mContext;
    private final Context mContext;
    private final UserHandle mUser;
    private final UserHandle mUser;

    private final DeviceRouteController mDeviceRouteController;
    private final BluetoothRouteController mBtRouteProvider;
    private final BluetoothRouteController mBtRouteProvider;


    private String mSelectedRouteId;
    private String mSelectedRouteId;
    // For apps without MODIFYING_AUDIO_ROUTING permission.
    // For apps without MODIFYING_AUDIO_ROUTING permission.
    // This should be the currently selected route.
    // This should be the currently selected route.
    MediaRoute2Info mDefaultRoute;
    MediaRoute2Info mDefaultRoute;
    MediaRoute2Info mDeviceRoute;
    RoutingSessionInfo mDefaultSessionInfo;
    RoutingSessionInfo mDefaultSessionInfo;
    int mDeviceVolume;


    private final AudioManagerBroadcastReceiver mAudioReceiver =
    private final AudioManagerBroadcastReceiver mAudioReceiver =
            new AudioManagerBroadcastReceiver();
            new AudioManagerBroadcastReceiver();
@@ -93,19 +75,6 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
    @GuardedBy("mRequestLock")
    @GuardedBy("mRequestLock")
    private volatile SessionCreationRequest mPendingSessionCreationRequest;
    private volatile SessionCreationRequest mPendingSessionCreationRequest;


    final IAudioRoutesObserver.Stub mAudioRoutesObserver = new IAudioRoutesObserver.Stub() {
        @Override
        public void dispatchAudioRoutesChanged(final AudioRoutesInfo newRoutes) {
            mHandler.post(() -> {
                updateDeviceRoute(newRoutes);
                notifyProviderState();
                if (updateSessionInfosIfNeeded()) {
                    notifySessionInfoUpdated();
                }
            });
        }
    };

    SystemMediaRoute2Provider(Context context, UserHandle user) {
    SystemMediaRoute2Provider(Context context, UserHandle user) {
        super(COMPONENT_NAME);
        super(COMPONENT_NAME);
        mIsSystemRouteProvider = true;
        mIsSystemRouteProvider = true;
@@ -114,8 +83,6 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
        mHandler = new Handler(Looper.getMainLooper());
        mHandler = new Handler(Looper.getMainLooper());


        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
        mAudioService = IAudioService.Stub.asInterface(
                ServiceManager.getService(Context.AUDIO_SERVICE));


        mBtRouteProvider = BluetoothRouteController.createInstance(context, (routes) -> {
        mBtRouteProvider = BluetoothRouteController.createInstance(context, (routes) -> {
            publishProviderState();
            publishProviderState();
@@ -124,15 +91,18 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
            }
            }
        });
        });


        AudioRoutesInfo newAudioRoutes = null;
        mDeviceRouteController = DeviceRouteController.createInstance(context, (deviceRoute) -> {
        try {
            mHandler.post(() -> {
            newAudioRoutes = mAudioService.startWatchingRoutes(mAudioRoutesObserver);
                publishProviderState();
        } catch (RemoteException e) {
                if (updateSessionInfosIfNeeded()) {
                    notifySessionInfoUpdated();
                }
                }
            });
        });


        // The methods below should be called after all fields are initialized, as they
        // These methods below should be called after all fields are initialized, as they
        // access the fields inside.
        // access the fields inside.
        updateDeviceRoute(newAudioRoutes);
        updateProviderState();
        updateSessionInfosIfNeeded();
        updateSessionInfosIfNeeded();
    }
    }


@@ -216,7 +186,9 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
            // The currently selected route is the default route.
            // The currently selected route is the default route.
            return;
            return;
        }
        }
        if (TextUtils.equals(routeId, mDeviceRoute.getId())) {

        MediaRoute2Info deviceRoute = mDeviceRouteController.getDeviceRoute();
        if (TextUtils.equals(routeId, deviceRoute.getId())) {
            mBtRouteProvider.transferTo(null);
            mBtRouteProvider.transferTo(null);
        } else {
        } else {
            mBtRouteProvider.transferTo(routeId);
            mBtRouteProvider.transferTo(routeId);
@@ -254,9 +226,12 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
            if (mSessionInfos.isEmpty()) {
            if (mSessionInfos.isEmpty()) {
                return null;
                return null;
            }
            }

            MediaRoute2Info deviceRoute = mDeviceRouteController.getDeviceRoute();

            RoutingSessionInfo.Builder builder = new RoutingSessionInfo.Builder(
            RoutingSessionInfo.Builder builder = new RoutingSessionInfo.Builder(
                    SYSTEM_SESSION_ID, packageName).setSystemSession(true);
                    SYSTEM_SESSION_ID, packageName).setSystemSession(true);
            builder.addSelectedRoute(mDeviceRoute.getId());
            builder.addSelectedRoute(deviceRoute.getId());
            for (MediaRoute2Info route : mBtRouteProvider.getAllBluetoothRoutes()) {
            for (MediaRoute2Info route : mBtRouteProvider.getAllBluetoothRoutes()) {
                builder.addTransferableRoute(route.getId());
                builder.addTransferableRoute(route.getId());
            }
            }
@@ -264,47 +239,12 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
        }
        }
    }
    }


    private void updateDeviceRoute(AudioRoutesInfo newRoutes) {
        int name = R.string.default_audio_route_name;
        int type = TYPE_BUILTIN_SPEAKER;
        if (newRoutes != null) {
            if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HEADPHONES) != 0) {
                type = TYPE_WIRED_HEADPHONES;
                name = com.android.internal.R.string.default_audio_route_name_headphones;
            } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HEADSET) != 0) {
                type = TYPE_WIRED_HEADSET;
                name = com.android.internal.R.string.default_audio_route_name_headphones;
            } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_DOCK_SPEAKERS) != 0) {
                type = TYPE_DOCK;
                name = com.android.internal.R.string.default_audio_route_name_dock_speakers;
            } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HDMI) != 0) {
                type = TYPE_HDMI;
                name = com.android.internal.R.string.default_audio_route_name_external_device;
            } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_USB) != 0) {
                type = TYPE_USB_DEVICE;
                name = com.android.internal.R.string.default_audio_route_name_usb;
            }
        }

        mDeviceRoute = new MediaRoute2Info.Builder(
                DEVICE_ROUTE_ID, mContext.getResources().getText(name).toString())
                .setVolumeHandling(mAudioManager.isVolumeFixed()
                        ? MediaRoute2Info.PLAYBACK_VOLUME_FIXED
                        : MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE)
                .setVolume(mDeviceVolume)
                .setVolumeMax(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC))
                .setType(type)
                .addFeature(FEATURE_LIVE_AUDIO)
                .addFeature(FEATURE_LIVE_VIDEO)
                .addFeature(FEATURE_LOCAL_PLAYBACK)
                .setConnectionState(MediaRoute2Info.CONNECTION_STATE_CONNECTED)
                .build();
        updateProviderState();
    }

    private void updateProviderState() {
    private void updateProviderState() {
        MediaRoute2ProviderInfo.Builder builder = new MediaRoute2ProviderInfo.Builder();
        MediaRoute2ProviderInfo.Builder builder = new MediaRoute2ProviderInfo.Builder();
        builder.addRoute(mDeviceRoute);

        // We must have a device route in the provider info.
        builder.addRoute(mDeviceRouteController.getDeviceRoute());

        for (MediaRoute2Info route : mBtRouteProvider.getAllBluetoothRoutes()) {
        for (MediaRoute2Info route : mBtRouteProvider.getAllBluetoothRoutes()) {
            builder.addRoute(route);
            builder.addRoute(route);
        }
        }
@@ -327,11 +267,12 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
                    SYSTEM_SESSION_ID, "" /* clientPackageName */)
                    SYSTEM_SESSION_ID, "" /* clientPackageName */)
                    .setSystemSession(true);
                    .setSystemSession(true);


            MediaRoute2Info selectedRoute = mDeviceRoute;
            MediaRoute2Info deviceRoute = mDeviceRouteController.getDeviceRoute();
            MediaRoute2Info selectedRoute = deviceRoute;
            MediaRoute2Info selectedBtRoute = mBtRouteProvider.getSelectedRoute();
            MediaRoute2Info selectedBtRoute = mBtRouteProvider.getSelectedRoute();
            if (selectedBtRoute != null) {
            if (selectedBtRoute != null) {
                selectedRoute = selectedBtRoute;
                selectedRoute = selectedBtRoute;
                builder.addTransferableRoute(mDeviceRoute.getId());
                builder.addTransferableRoute(deviceRoute.getId());
            }
            }
            mSelectedRouteId = selectedRoute.getId();
            mSelectedRouteId = selectedRoute.getId();
            mDefaultRoute = new MediaRoute2Info.Builder(DEFAULT_ROUTE_ID, selectedRoute)
            mDefaultRoute = new MediaRoute2Info.Builder(DEFAULT_ROUTE_ID, selectedRoute)
@@ -423,12 +364,9 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
        if (mBtRouteProvider.updateVolumeForDevices(devices, volume)) {
        if (mBtRouteProvider.updateVolumeForDevices(devices, volume)) {
            return;
            return;
        }
        }
        if (mDeviceVolume != volume) {

            mDeviceVolume = volume;
        mDeviceRouteController.updateVolume(volume);
            mDeviceRoute = new MediaRoute2Info.Builder(mDeviceRoute)

                    .setVolume(volume)
                    .build();
        }
        publishProviderState();
        publishProviderState();
    }
    }


+371 −0

File added.

Preview size limit exceeded, changes collapsed.