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

Commit 5af72432 authored by George Lu's avatar George Lu
Browse files

Revise BroadcastRadioService handling of V2 HALs

Previously, BroadcastRadioService checked for HALs (both V1 and V2)
exactly once, the first time listModules() was called. This approach
failed to handle HALs that registered after boot or that died and restarted.

This change addresses both issues by registering a listener that is
invoked when V2 HALs are registered. Additionally, the check for V1 HALs
now occurs at construction time.

Bug: 121006324
Bug: 126887436
Test: Verified on device BroadcastRadioService handles V1 HALs properly and picks up V2 HALs dynamically
Change-Id: Id9df7f53d189801f0a0dc894b2bb425d655edbd6
parent dece92b2
Loading
Loading
Loading
Loading
+14 −24
Original line number Diff line number Diff line
@@ -16,7 +16,6 @@

package com.android.server.broadcastradio;

import android.annotation.NonNull;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -26,14 +25,13 @@ import android.hardware.radio.IRadioService;
import android.hardware.radio.ITuner;
import android.hardware.radio.ITunerCallback;
import android.hardware.radio.RadioManager;
import android.os.ParcelableException;
import android.os.RemoteException;
import android.util.Slog;

import com.android.internal.util.Preconditions;
import com.android.server.SystemService;
import com.android.server.broadcastradio.hal2.AnnouncementAggregator;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
@@ -45,16 +43,20 @@ public class BroadcastRadioService extends SystemService {

    private final ServiceImpl mServiceImpl = new ServiceImpl();

    private final com.android.server.broadcastradio.hal1.BroadcastRadioService mHal1 =
            new com.android.server.broadcastradio.hal1.BroadcastRadioService();
    private final com.android.server.broadcastradio.hal2.BroadcastRadioService mHal2 =
            new com.android.server.broadcastradio.hal2.BroadcastRadioService();
    private final com.android.server.broadcastradio.hal1.BroadcastRadioService mHal1;
    private final com.android.server.broadcastradio.hal2.BroadcastRadioService mHal2;

    private final Object mLock = new Object();
    private List<RadioManager.ModuleProperties> mModules = null;
    private List<RadioManager.ModuleProperties> mV1Modules = null;

    public BroadcastRadioService(Context context) {
        super(context);

        mHal1 = new com.android.server.broadcastradio.hal1.BroadcastRadioService();
        mV1Modules = mHal1.loadModules();
        OptionalInt max = mV1Modules.stream().mapToInt(RadioManager.ModuleProperties::getId).max();
        mHal2 = new com.android.server.broadcastradio.hal2.BroadcastRadioService(
                max.isPresent() ? max.getAsInt() + 1 : 0);
    }

    @Override
@@ -62,14 +64,6 @@ public class BroadcastRadioService extends SystemService {
        publishBinderService(Context.RADIO_SERVICE, mServiceImpl);
    }

    /**
     * Finds next available index for newly loaded modules.
     */
    private static int getNextId(@NonNull List<RadioManager.ModuleProperties> modules) {
        OptionalInt max = modules.stream().mapToInt(RadioManager.ModuleProperties::getId).max();
        return max.isPresent() ? max.getAsInt() + 1 : 0;
    }

    private class ServiceImpl extends IRadioService.Stub {
        private void enforcePolicyAccess() {
            if (PackageManager.PERMISSION_GRANTED != getContext().checkCallingPermission(
@@ -81,14 +75,10 @@ public class BroadcastRadioService extends SystemService {
        @Override
        public List<RadioManager.ModuleProperties> listModules() {
            enforcePolicyAccess();
            synchronized (mLock) {
                if (mModules != null) return mModules;

                mModules = mHal1.loadModules();
                mModules.addAll(mHal2.loadModules(getNextId(mModules)));

                return mModules;
            }
            List<RadioManager.ModuleProperties> modules = new ArrayList<>();
            modules.addAll(mV1Modules);
            modules.addAll(mHal2.listModules());
            return modules;
        }

        @Override
+98 −39
Original line number Diff line number Diff line
@@ -18,20 +18,22 @@ package com.android.server.broadcastradio.hal2;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.hardware.broadcastradio.V2_0.IBroadcastRadio;
import android.hardware.radio.IAnnouncementListener;
import android.hardware.radio.ICloseHandle;
import android.hardware.radio.ITuner;
import android.hardware.radio.ITunerCallback;
import android.hardware.radio.RadioManager;
import android.hardware.broadcastradio.V2_0.IBroadcastRadio;
import android.hidl.manager.V1_0.IServiceManager;
import android.hidl.manager.V1_0.IServiceNotification;
import android.os.IHwBinder.DeathRecipient;
import android.os.RemoteException;
import android.util.Slog;

import com.android.internal.annotations.GuardedBy;

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
@@ -39,53 +41,105 @@ import java.util.stream.Collectors;
public class BroadcastRadioService {
    private static final String TAG = "BcRadio2Srv";

    private final Object mLock = new Object();

    @GuardedBy("mLock")
    private int mNextModuleId = 0;

    @GuardedBy("mLock")
    private final Map<String, Integer> mServiceNameToModuleIdMap = new HashMap<>();

    @GuardedBy("mLock")
    private final Map<Integer, RadioModule> mModules = new HashMap<>();

    private static @NonNull List<String> listByInterface(@NonNull String fqName) {
        try {
            IServiceManager manager = IServiceManager.getService();
            if (manager == null) {
                Slog.e(TAG, "Failed to get HIDL Service Manager");
                return Collections.emptyList();
    private IServiceNotification.Stub mServiceListener = new IServiceNotification.Stub() {
        @Override
        public void onRegistration(String fqName, String serviceName, boolean preexisting) {
            Slog.v(TAG, "onRegistration(" + fqName + ", " + serviceName + ", " + preexisting + ")");
            Integer moduleId;
            synchronized (mLock) {
                // If the service has been registered before, reuse its previous module ID.
                moduleId = mServiceNameToModuleIdMap.get(serviceName);
                boolean newService = false;
                if (moduleId == null) {
                    newService = true;
                    moduleId = mNextModuleId;
                }

                RadioModule module = RadioModule.tryLoadingModule(moduleId, serviceName);
                if (module == null) {
                    return;
                }
                Slog.v(TAG, "loaded broadcast radio module " + moduleId + ": " + serviceName
                        + " (HAL 2.0)");
                mModules.put(moduleId, module);

            List<String> list = manager.listByInterface(fqName);
            if (list == null) {
                Slog.e(TAG, "Didn't get interface list from HIDL Service Manager");
                return Collections.emptyList();
                if (newService) {
                    mServiceNameToModuleIdMap.put(serviceName, moduleId);
                    mNextModuleId++;
                }
            return list;

                try {
                    module.getService().linkToDeath(mDeathRecipient, moduleId);
                } catch (RemoteException ex) {
            Slog.e(TAG, "Failed fetching interface list", ex);
            return Collections.emptyList();
                    // Service has already died, so remove its entry from mModules.
                    mModules.remove(moduleId);
                }
            }
        }
    };

    public @NonNull Collection<RadioManager.ModuleProperties> loadModules(int idx) {
        Slog.v(TAG, "loadModules(" + idx + ")");
    private DeathRecipient mDeathRecipient = new DeathRecipient() {
        @Override
        public void serviceDied(long cookie) {
            Slog.v(TAG, "serviceDied(" + cookie + ")");
            synchronized (mLock) {
                int moduleId = (int) cookie;
                mModules.remove(moduleId);

        for (String serviceName : listByInterface(IBroadcastRadio.kInterfaceName)) {
            Slog.v(TAG, "checking service: " + serviceName);
                for (Map.Entry<String, Integer> entry : mServiceNameToModuleIdMap.entrySet()) {
                    if (entry.getValue() == moduleId) {
                        Slog.i(TAG, "service " + entry.getKey()
                                + " died; removed RadioModule with ID " + moduleId);
                        return;
                    }
                }
            }
        }
    };

            RadioModule module = RadioModule.tryLoadingModule(idx, serviceName);
            if (module != null) {
                Slog.i(TAG, "loaded broadcast radio module " + idx + ": " +
                        serviceName + " (HAL 2.0)");
                mModules.put(idx++, module);
    public BroadcastRadioService(int nextModuleId) {
        mNextModuleId = nextModuleId;
        try {
            IServiceManager manager = IServiceManager.getService();
            if (manager == null) {
                Slog.e(TAG, "failed to get HIDL Service Manager");
                return;
            }
            manager.registerForNotifications(IBroadcastRadio.kInterfaceName, "", mServiceListener);
        } catch (RemoteException ex) {
            Slog.e(TAG, "failed to register for service notifications: ", ex);
        }
    }

        return mModules.values().stream().map(module -> module.mProperties).
                collect(Collectors.toList());
    public @NonNull Collection<RadioManager.ModuleProperties> listModules() {
        synchronized (mLock) {
            return mModules.values().stream().map(module -> module.mProperties)
                    .collect(Collectors.toList());
        }
    }

    public boolean hasModule(int id) {
        synchronized (mLock) {
            return mModules.containsKey(id);
        }
    }

    public boolean hasAnyModules() {
        synchronized (mLock) {
            return !mModules.isEmpty();
        }
    }

    public ITuner openSession(int moduleId, @Nullable RadioManager.BandConfig legacyConfig,
        boolean withAudio, @NonNull ITunerCallback callback) throws RemoteException {
@@ -95,7 +149,10 @@ public class BroadcastRadioService {
            throw new IllegalArgumentException("Non-audio sessions not supported with HAL 2.x");
        }

        RadioModule module = mModules.get(moduleId);
        RadioModule module = null;
        synchronized (mLock) {
            module = mModules.get(moduleId);
        }
        if (module == null) {
            throw new IllegalArgumentException("Invalid module ID");
        }
@@ -111,6 +168,7 @@ public class BroadcastRadioService {
            @NonNull IAnnouncementListener listener) {
        AnnouncementAggregator aggregator = new AnnouncementAggregator(listener);
        boolean anySupported = false;
        synchronized (mLock) {
            for (RadioModule module : mModules.values()) {
                try {
                    aggregator.watchModule(module, enabledTypes);
@@ -119,6 +177,7 @@ public class BroadcastRadioService {
                    Slog.v(TAG, "Announcements not supported for this module", ex);
                }
            }
        }
        if (!anySupported) {
            Slog.i(TAG, "There are no HAL modules that support announcements");
        }
+5 −4
Original line number Diff line number Diff line
@@ -20,8 +20,6 @@ import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.hardware.radio.ITuner;
import android.hardware.radio.RadioManager;
import android.hardware.broadcastradio.V2_0.AmFmRegionConfig;
import android.hardware.broadcastradio.V2_0.Announcement;
import android.hardware.broadcastradio.V2_0.DabTableEntry;
@@ -30,13 +28,12 @@ import android.hardware.broadcastradio.V2_0.IBroadcastRadio;
import android.hardware.broadcastradio.V2_0.ICloseHandle;
import android.hardware.broadcastradio.V2_0.ITunerSession;
import android.hardware.broadcastradio.V2_0.Result;
import android.os.ParcelableException;
import android.hardware.radio.RadioManager;
import android.os.RemoteException;
import android.util.MutableInt;
import android.util.Slog;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
@@ -78,6 +75,10 @@ class RadioModule {
        }
    }

    public @NonNull IBroadcastRadio getService() {
        return mService;
    }

    public @NonNull TunerSession openSession(@NonNull android.hardware.radio.ITunerCallback userCb)
            throws RemoteException {
        TunerCallback cb = new TunerCallback(Objects.requireNonNull(userCb));