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

Commit 23d8c9b5 authored by Weilin Xu's avatar Weilin Xu Committed by Android (Google) Code Review
Browse files

Merge "Add AIDL HAL support for BroadcastRadioService"

parents b9e759bb 67fa6636
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -147,7 +147,8 @@ java_library_static {
        "android.hardware.boot-V1.0-java",
        "android.hardware.boot-V1.1-java",
        "android.hardware.boot-V1.2-java",
        "android.hardware.broadcastradio-V2.0-java",
        "android.hardware.broadcastradio-V2.0-java", // HIDL
        "android.hardware.broadcastradio-V1-java", // AIDL
        "android.hardware.health-V1.0-java", // HIDL
        "android.hardware.health-V2.0-java", // HIDL
        "android.hardware.health-V2.1-java", // HIDL
+6 −4
Original line number Diff line number Diff line
@@ -21,21 +21,23 @@ import android.annotation.SuppressLint;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.radio.IRadioService;
import android.util.Slog;

import com.android.server.SystemService;

import java.util.ArrayList;

public class BroadcastRadioService extends SystemService {
    private static final String TAG = "BcRadioSrv";
    private final IRadioService mServiceImpl;

    public BroadcastRadioService(Context context) {
        super(context);
        mServiceImpl = new BroadcastRadioServiceHidl(this);
        ArrayList<String> serviceNameList = IRadioServiceAidlImpl.getServicesNames();
        mServiceImpl = serviceNameList.isEmpty() ? new IRadioServiceHidlImpl(this)
                : new IRadioServiceAidlImpl(this, serviceNameList);
    }

    @Override
    public void onStart() {
        Slog.v(TAG, "BroadcastRadioService onStart()");
        publishBinderService(Context.RADIO_SERVICE, mServiceImpl.asBinder());
    }

+124 −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.broadcastradio;

import android.hardware.broadcastradio.IBroadcastRadio;
import android.hardware.radio.IAnnouncementListener;
import android.hardware.radio.ICloseHandle;
import android.hardware.radio.IRadioService;
import android.hardware.radio.ITuner;
import android.hardware.radio.ITunerCallback;
import android.hardware.radio.RadioManager;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.IndentingPrintWriter;
import android.util.Log;

import com.android.server.utils.Slogf;

import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

/**
 * Wrapper for AIDL interface for BroadcastRadio HAL
 */
final class IRadioServiceAidlImpl extends IRadioService.Stub {
    private static final String TAG = "BcRadioSrvAidl";

    private static final List<String> SERVICE_NAMES = Arrays.asList(
            IBroadcastRadio.DESCRIPTOR + "/amfm", IBroadcastRadio.DESCRIPTOR + "/dab");

    private final com.android.server.broadcastradio.aidl.BroadcastRadioServiceImpl mHalAidl;
    private final BroadcastRadioService mService;

    /**
     * Gets names of all AIDL BroadcastRadio HAL services available.
     */
    public static ArrayList<String> getServicesNames() {
        ArrayList<String> serviceList = new ArrayList<>();
        for (int i = 0; i < SERVICE_NAMES.size(); i++) {
            IBinder serviceBinder = ServiceManager.waitForDeclaredService(SERVICE_NAMES.get(i));
            if (serviceBinder != null) {
                serviceList.add(SERVICE_NAMES.get(i));
            }
        }
        return serviceList;
    }

    IRadioServiceAidlImpl(BroadcastRadioService service, ArrayList<String> serviceList) {
        Slogf.i(TAG, "Initialize BroadcastRadioServiceAidl(%s)", service);
        mService = Objects.requireNonNull(service);
        mHalAidl =
                new com.android.server.broadcastradio.aidl.BroadcastRadioServiceImpl(serviceList);
    }

    @Override
    public List<RadioManager.ModuleProperties> listModules() {
        mService.enforcePolicyAccess();
        return mHalAidl.listModules();
    }

    @Override
    public ITuner openTuner(int moduleId, RadioManager.BandConfig bandConfig,
            boolean withAudio, ITunerCallback callback) throws RemoteException {
        if (isDebugEnabled()) {
            Slogf.d(TAG, "Opening module %d", moduleId);
        }
        mService.enforcePolicyAccess();
        if (callback == null) {
            throw new IllegalArgumentException("Callback must not be null");
        }
        return mHalAidl.openSession(moduleId, bandConfig, withAudio, callback);
    }

    @Override
    public ICloseHandle addAnnouncementListener(int[] enabledTypes,
            IAnnouncementListener listener) {
        if (isDebugEnabled()) {
            Slogf.d(TAG, "Adding announcement listener for %s", Arrays.toString(enabledTypes));
        }
        Objects.requireNonNull(enabledTypes);
        Objects.requireNonNull(listener);
        mService.enforcePolicyAccess();

        return mHalAidl.addAnnouncementListener(enabledTypes, listener);
    }

    @Override
    protected void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
        IndentingPrintWriter radioPrintWriter = new IndentingPrintWriter(printWriter);
        radioPrintWriter.printf("BroadcastRadioService\n");

        radioPrintWriter.increaseIndent();
        radioPrintWriter.printf("AIDL HAL:\n");

        radioPrintWriter.increaseIndent();
        mHalAidl.dumpInfo(radioPrintWriter);
        radioPrintWriter.decreaseIndent();

        radioPrintWriter.decreaseIndent();
    }

    private static boolean isDebugEnabled() {
        return Log.isLoggable(TAG, Log.DEBUG);
    }
}
+2 −2
Original line number Diff line number Diff line
@@ -41,7 +41,7 @@ import java.util.OptionalInt;
/**
 * Wrapper for HIDL interface for BroadcastRadio HAL
 */
final class BroadcastRadioServiceHidl extends IRadioService.Stub {
final class IRadioServiceHidlImpl extends IRadioService.Stub {
    private static final String TAG = "BcRadioSrvHidl";

    private final com.android.server.broadcastradio.hal1.BroadcastRadioService mHal1;
@@ -52,7 +52,7 @@ final class BroadcastRadioServiceHidl extends IRadioService.Stub {
    private final BroadcastRadioService mService;
    private final List<RadioManager.ModuleProperties> mV1Modules;

    BroadcastRadioServiceHidl(BroadcastRadioService service) {
    IRadioServiceHidlImpl(BroadcastRadioService service) {
        mService = Objects.requireNonNull(service);
        mHal1 = new com.android.server.broadcastradio.hal1.BroadcastRadioService(mLock);
        mV1Modules = mHal1.loadModules();
+213 −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.broadcastradio.aidl;

import android.annotation.Nullable;
import android.hardware.radio.Announcement;
import android.hardware.radio.IAnnouncementListener;
import android.hardware.radio.ICloseHandle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.IndentingPrintWriter;
import android.util.Log;

import com.android.internal.annotations.GuardedBy;
import com.android.server.utils.Slogf;

import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

/**
 * Announcement aggregator extending {@link ICloseHandle} to support broadcast radio announcement
 */
public final class AnnouncementAggregator extends ICloseHandle.Stub {
    private static final String TAG = "BcRadioAidlSrv.AnnAggr";
    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);

    private final Object mLock;
    private final IAnnouncementListener mListener;
    private final IBinder.DeathRecipient mDeathRecipient = new DeathRecipient();

    @GuardedBy("mLock")
    private final List<ModuleWatcher> mModuleWatchers = new ArrayList<>();

    @GuardedBy("mLock")
    private boolean mIsClosed;

    /**
     * Constructs Announcement aggregator with AnnouncementListener of BroadcastRadio AIDL HAL.
     */
    public AnnouncementAggregator(IAnnouncementListener listener, Object lock) {
        mListener = Objects.requireNonNull(listener, "listener cannot be null");
        mLock = Objects.requireNonNull(lock, "lock cannot be null");
        try {
            listener.asBinder().linkToDeath(mDeathRecipient, /* flags= */ 0);
        } catch (RemoteException ex) {
            ex.rethrowFromSystemServer();
        }
    }

    private final class ModuleWatcher extends IAnnouncementListener.Stub {

        @Nullable
        private ICloseHandle mCloseHandle;

        public List<Announcement> mCurrentList = new ArrayList<>();

        public void onListUpdated(List<Announcement> active) {
            if (DEBUG) {
                Slogf.d(TAG, "onListUpdate for %s", active);
            }
            mCurrentList = Objects.requireNonNull(active, "active cannot be null");
            AnnouncementAggregator.this.onListUpdated();
        }

        public void setCloseHandle(ICloseHandle closeHandle) {
            if (DEBUG) {
                Slogf.d(TAG, "Set close handle %s", closeHandle);
            }
            mCloseHandle = Objects.requireNonNull(closeHandle, "closeHandle cannot be null");
        }

        public void close() throws RemoteException {
            if (DEBUG) {
                Slogf.d(TAG, "Close module watcher.");
            }
            if (mCloseHandle != null) mCloseHandle.close();
        }

        public void dumpInfo(IndentingPrintWriter pw) {
            pw.printf("ModuleWatcher:\n");

            pw.increaseIndent();
            pw.printf("Close handle: %s\n", mCloseHandle);
            pw.printf("Current announcement list: %s\n", mCurrentList);
            pw.decreaseIndent();
        }
    }

    private class DeathRecipient implements IBinder.DeathRecipient {
        public void binderDied() {
            try {
                close();
            } catch (RemoteException ex) {
                Slogf.e(TAG, ex, "Cannot close Announcement aggregator for DeathRecipient");
            }
        }
    }

    private void onListUpdated() {
        if (DEBUG) {
            Slogf.d(TAG, "onListUpdated()");
        }
        synchronized (mLock) {
            if (mIsClosed) {
                Slogf.e(TAG, "Announcement aggregator is closed, it shouldn't receive callbacks");
                return;
            }
            List<Announcement> combined = new ArrayList<>(mModuleWatchers.size());
            for (int i = 0; i < mModuleWatchers.size(); i++) {
                combined.addAll(mModuleWatchers.get(i).mCurrentList);
            }
            try {
                mListener.onListUpdated(combined);
            } catch (RemoteException ex) {
                Slogf.e(TAG, ex, "mListener.onListUpdated() failed");
            }
        }
    }

    /**
     * Watches the given RadioModule by adding Announcement Listener to it
     */
    public void watchModule(RadioModule radioModule, int[] enabledTypes) {
        if (DEBUG) {
            Slogf.d(TAG, "Watch module for %s with enabled types %s",
                    radioModule, Arrays.toString(enabledTypes));
        }
        synchronized (mLock) {
            if (mIsClosed) {
                throw new IllegalStateException("Failed to watch module"
                        + "since announcement aggregator has already been closed");
            }

            ModuleWatcher watcher = new ModuleWatcher();
            ICloseHandle closeHandle;
            try {
                closeHandle = radioModule.addAnnouncementListener(watcher, enabledTypes);
            } catch (RemoteException ex) {
                Slogf.e(TAG, ex, "Failed to add announcement listener");
                return;
            }
            watcher.setCloseHandle(closeHandle);
            mModuleWatchers.add(watcher);
        }
    }

    @Override
    public void close() throws RemoteException {
        if (DEBUG) {
            Slogf.d(TAG, "Close watchModule");
        }
        synchronized (mLock) {
            if (mIsClosed) {
                Slogf.w(TAG, "Announcement aggregator has already been closed.");
                return;
            }

            mListener.asBinder().unlinkToDeath(mDeathRecipient, /* flags= */ 0);

            for (int i = 0; i < mModuleWatchers.size(); i++) {
                ModuleWatcher moduleWatcher = mModuleWatchers.get(i);
                try {
                    moduleWatcher.close();
                } catch (Exception e) {
                    Slogf.e(TAG, "Failed to close module watcher %s: %s",
                            moduleWatcher, e);
                }
            }
            mModuleWatchers.clear();

            mIsClosed = true;
        }
    }

    @Override
    protected void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
        IndentingPrintWriter announcementPrintWriter = new IndentingPrintWriter(printWriter);
        announcementPrintWriter.printf("AnnouncementAggregator\n");

        announcementPrintWriter.increaseIndent();
        synchronized (mLock) {
            announcementPrintWriter.printf("Is session closed? %s\n", mIsClosed ? "Yes" : "No");
            announcementPrintWriter.printf("Module Watchers [%d]:\n", mModuleWatchers.size());

            announcementPrintWriter.increaseIndent();
            for (int i = 0; i < mModuleWatchers.size(); i++) {
                mModuleWatchers.get(i).dumpInfo(announcementPrintWriter);
            }
            announcementPrintWriter.decreaseIndent();

        }
        announcementPrintWriter.decreaseIndent();
    }

}
Loading