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

Commit bda66f48 authored by George Lu's avatar George Lu Committed by Android (Google) Code Review
Browse files

Merge "Implement AIDL fanout from v2 HAL BroadcastRadioService"

parents db7c25ad b45cdc77
Loading
Loading
Loading
Loading
+5 −1
Original line number Diff line number Diff line
@@ -90,7 +90,11 @@ public class AnnouncementAggregator extends ICloseHandle.Stub {
            for (ModuleWatcher watcher : mModuleWatchers) {
                combined.addAll(watcher.currentList);
            }
            TunerCallback.dispatch(() -> mListener.onListUpdated(combined));
            try {
                mListener.onListUpdated(combined);
            } catch (RemoteException ex) {
                Slog.e(TAG, "mListener.onListUpdated() failed: ", ex);
            }
        }
    }

+8 −23
Original line number Diff line number Diff line
@@ -54,13 +54,6 @@ public class BroadcastRadioService {
    @GuardedBy("mLock")
    private final Map<Integer, RadioModule> mModules = new HashMap<>();

    // Map from module ID to TunerSession created by openSession().
    //
    // Because this service currently implements a 1 AIDL to 1 HAL policy, mTunerSessions is used to
    // enforce the "aggresive open" policy mandated for IBroadcastRadio.openSession(). In the
    // future, this solution will be replaced with a multiple-AIDL to 1 HAL implementation.
    private final Map<Integer, TunerSession> mTunerSessions = new HashMap<>();

    private IServiceNotification.Stub mServiceListener = new IServiceNotification.Stub() {
        @Override
        public void onRegistration(String fqName, String serviceName, boolean preexisting) {
@@ -81,8 +74,10 @@ public class BroadcastRadioService {
                }
                Slog.v(TAG, "loaded broadcast radio module " + moduleId + ": " + serviceName
                        + " (HAL 2.0)");
                closeTunerSessionLocked(moduleId);
                mModules.put(moduleId, module);
                RadioModule prevModule = mModules.put(moduleId, module);
                if (prevModule != null) {
                    prevModule.closeSessions(RadioTuner.ERROR_HARDWARE_FAILURE);
                }

                if (newService) {
                    mServiceNameToModuleIdMap.put(serviceName, moduleId);
@@ -105,8 +100,10 @@ public class BroadcastRadioService {
            Slog.v(TAG, "serviceDied(" + cookie + ")");
            synchronized (mLock) {
                int moduleId = (int) cookie;
                mModules.remove(moduleId);
                closeTunerSessionLocked(moduleId);
                RadioModule prevModule = mModules.remove(moduleId);
                if (prevModule != null) {
                    prevModule.closeSessions(RadioTuner.ERROR_HARDWARE_FAILURE);
                }

                for (Map.Entry<String, Integer> entry : mServiceNameToModuleIdMap.entrySet()) {
                    if (entry.getValue() == moduleId) {
@@ -166,13 +163,9 @@ public class BroadcastRadioService {
            if (module == null) {
                throw new IllegalArgumentException("Invalid module ID");
            }
            closeTunerSessionLocked(moduleId);
        }

        TunerSession tunerSession = module.openSession(callback);
        synchronized (mLock) {
            mTunerSessions.put(moduleId, tunerSession);
        }
        if (legacyConfig != null) {
            tunerSession.setConfiguration(legacyConfig);
        }
@@ -198,12 +191,4 @@ public class BroadcastRadioService {
        }
        return aggregator;
    }

    private void closeTunerSessionLocked(int moduleId) {
        TunerSession tunerSession = mTunerSessions.remove(moduleId);
        if (tunerSession != null) {
            Slog.d(TAG, "Closing previous TunerSession");
            tunerSession.close(RadioTuner.ERROR_HARDWARE_FAILURE);
        }
    }
}
+141 −12
Original line number Diff line number Diff line
@@ -26,16 +26,26 @@ import android.hardware.broadcastradio.V2_0.DabTableEntry;
import android.hardware.broadcastradio.V2_0.IAnnouncementListener;
import android.hardware.broadcastradio.V2_0.IBroadcastRadio;
import android.hardware.broadcastradio.V2_0.ICloseHandle;
import android.hardware.broadcastradio.V2_0.ITunerCallback;
import android.hardware.broadcastradio.V2_0.ITunerSession;
import android.hardware.broadcastradio.V2_0.ProgramInfo;
import android.hardware.broadcastradio.V2_0.ProgramListChunk;
import android.hardware.broadcastradio.V2_0.ProgramSelector;
import android.hardware.broadcastradio.V2_0.Result;
import android.hardware.broadcastradio.V2_0.VendorKeyValue;
import android.hardware.radio.RadioManager;
import android.os.DeadObjectException;
import android.os.RemoteException;
import android.util.MutableInt;
import android.util.Slog;

import com.android.internal.annotations.GuardedBy;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

class RadioModule {
@@ -44,8 +54,63 @@ class RadioModule {
    @NonNull private final IBroadcastRadio mService;
    @NonNull public final RadioManager.ModuleProperties mProperties;

    private final Object mLock = new Object();

    @GuardedBy("mLock")
    private ITunerSession mHalTunerSession;

    // Tracks antenna state reported by HAL (if any).
    @GuardedBy("mLock")
    private Boolean mAntennaConnected = null;

    @GuardedBy("mLock")
    private RadioManager.ProgramInfo mProgramInfo = null;

    // Callback registered with the HAL to relay callbacks to AIDL clients.
    private final ITunerCallback mHalTunerCallback = new ITunerCallback.Stub() {
        @Override
        public void onTuneFailed(int result, ProgramSelector programSelector) {
            fanoutAidlCallback(cb -> cb.onTuneFailed(result, Convert.programSelectorFromHal(
                    programSelector)));
        }

        @Override
        public void onCurrentProgramInfoChanged(ProgramInfo halProgramInfo) {
            RadioManager.ProgramInfo programInfo = Convert.programInfoFromHal(halProgramInfo);
            synchronized (mLock) {
                mProgramInfo = programInfo;
                fanoutAidlCallbackLocked(cb -> cb.onCurrentProgramInfoChanged(programInfo));
            }
        }

        @Override
        public void onProgramListUpdated(ProgramListChunk programListChunk) {
            // TODO: Cache per-AIDL client filters, send union of filters to HAL, use filters to fan
            // back out to clients.
            fanoutAidlCallback(cb -> cb.onProgramListUpdated(Convert.programListChunkFromHal(
                    programListChunk)));
        }

        @Override
        public void onAntennaStateChange(boolean connected) {
            synchronized (mLock) {
                mAntennaConnected = connected;
                fanoutAidlCallbackLocked(cb -> cb.onAntennaState(connected));
            }
        }

        @Override
        public void onParametersUpdated(ArrayList<VendorKeyValue> parameters) {
            fanoutAidlCallback(cb -> cb.onParametersUpdated(Convert.vendorInfoFromHal(parameters)));
        }
    };

    // Collection of active AIDL tuner sessions created through openSession().
    @GuardedBy("mLock")
    private final Set<TunerSession> mAidlTunerSessions = new HashSet<>();

    private RadioModule(@NonNull IBroadcastRadio service,
            @NonNull RadioManager.ModuleProperties properties) {
            @NonNull RadioManager.ModuleProperties properties) throws RemoteException {
        mProperties = Objects.requireNonNull(properties);
        mService = Objects.requireNonNull(service);
    }
@@ -81,21 +146,85 @@ class RadioModule {

    public @NonNull TunerSession openSession(@NonNull android.hardware.radio.ITunerCallback userCb)
            throws RemoteException {
        TunerCallback cb = new TunerCallback(Objects.requireNonNull(userCb));
        synchronized (mLock) {
            if (mHalTunerSession == null) {
                Mutable<ITunerSession> hwSession = new Mutable<>();
        MutableInt halResult = new MutableInt(Result.UNKNOWN_ERROR);

        synchronized (mService) {
            mService.openSession(cb, (result, session) -> {
                mService.openSession(mHalTunerCallback, (result, session) -> {
                    Convert.throwOnError("openSession", result);
                    hwSession.value = session;
                halResult.value = result;
                });
                mHalTunerSession = Objects.requireNonNull(hwSession.value);
            }
            TunerSession tunerSession = new TunerSession(this, mHalTunerSession, userCb);
            mAidlTunerSessions.add(tunerSession);

            // Propagate state to new client. Note: These callbacks are invoked while holding mLock
            // to prevent race conditions with new callbacks from the HAL.
            if (mAntennaConnected != null) {
                userCb.onAntennaState(mAntennaConnected);
            }
            if (mProgramInfo != null) {
                userCb.onCurrentProgramInfoChanged(mProgramInfo);
            }

            return tunerSession;
        }
    }

    public void closeSessions(Integer error) {
        // Copy the contents of mAidlTunerSessions into a local array because TunerSession.close()
        // must be called without mAidlTunerSessions locked because it can call
        // onTunerSessionClosed().
        TunerSession[] tunerSessions;
        synchronized (mLock) {
            tunerSessions = new TunerSession[mAidlTunerSessions.size()];
            mAidlTunerSessions.toArray(tunerSessions);
            mAidlTunerSessions.clear();
        }
        for (TunerSession tunerSession : tunerSessions) {
            tunerSession.close(error);
        }
    }

    void onTunerSessionClosed(TunerSession tunerSession) {
        synchronized (mLock) {
            mAidlTunerSessions.remove(tunerSession);
            if (mAidlTunerSessions.isEmpty() && mHalTunerSession != null) {
                Slog.v(TAG, "closing HAL tuner session");
                try {
                    mHalTunerSession.close();
                } catch (RemoteException ex) {
                    Slog.e(TAG, "mHalTunerSession.close() failed: ", ex);
                }
                mHalTunerSession = null;
            }
        }
    }

    interface AidlCallbackRunnable {
        void run(android.hardware.radio.ITunerCallback callback) throws RemoteException;
    }

        Convert.throwOnError("openSession", halResult.value);
        Objects.requireNonNull(hwSession.value);
    // Invokes runnable with each TunerSession currently open.
    void fanoutAidlCallback(AidlCallbackRunnable runnable) {
        synchronized (mLock) {
            fanoutAidlCallbackLocked(runnable);
        }
    }

        return new TunerSession(this, hwSession.value, cb);
    private void fanoutAidlCallbackLocked(AidlCallbackRunnable runnable) {
        for (TunerSession tunerSession : mAidlTunerSessions) {
            try {
                runnable.run(tunerSession.mCallback);
            } catch (DeadObjectException ex) {
                // The other side died without calling close(), so just purge it from our
                // records.
                Slog.e(TAG, "Removing dead TunerSession");
                mAidlTunerSessions.remove(tunerSession);
            } catch (RemoteException ex) {
                Slog.e(TAG, "Failed to invoke ITunerCallback: ", ex);
            }
        }
    }

    public android.hardware.radio.ICloseHandle addAnnouncementListener(@NonNull int[] enabledTypes,
+0 −76
Original line number Diff line number Diff line
/**
 * Copyright (C) 2017 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.hal2;

import android.annotation.NonNull;
import android.hardware.broadcastradio.V2_0.ITunerCallback;
import android.hardware.broadcastradio.V2_0.ProgramInfo;
import android.hardware.broadcastradio.V2_0.ProgramListChunk;
import android.hardware.broadcastradio.V2_0.ProgramSelector;
import android.hardware.broadcastradio.V2_0.VendorKeyValue;
import android.os.RemoteException;
import android.util.Slog;

import java.util.ArrayList;
import java.util.Objects;

class TunerCallback extends ITunerCallback.Stub {
    private static final String TAG = "BcRadio2Srv.cb";

    final android.hardware.radio.ITunerCallback mClientCb;

    interface RunnableThrowingRemoteException {
        void run() throws RemoteException;
    }

    TunerCallback(@NonNull android.hardware.radio.ITunerCallback clientCallback) {
        mClientCb = Objects.requireNonNull(clientCallback);
    }

    static void dispatch(RunnableThrowingRemoteException func) {
        try {
            func.run();
        } catch (RemoteException ex) {
            Slog.e(TAG, "callback call failed", ex);
        }
    }

    @Override
    public void onTuneFailed(int result, ProgramSelector selector) {
        dispatch(() -> mClientCb.onTuneFailed(result, Convert.programSelectorFromHal(selector)));
    }

    @Override
    public void onCurrentProgramInfoChanged(ProgramInfo info) {
        dispatch(() -> mClientCb.onCurrentProgramInfoChanged(Convert.programInfoFromHal(info)));
    }

    @Override
    public void onProgramListUpdated(ProgramListChunk chunk) {
        dispatch(() -> mClientCb.onProgramListUpdated(Convert.programListChunkFromHal(chunk)));
    }

    @Override
    public void onAntennaStateChange(boolean connected) {
        dispatch(() -> mClientCb.onAntennaState(connected));
    }

    @Override
    public void onParametersUpdated(ArrayList<VendorKeyValue> parameters) {
        dispatch(() -> mClientCb.onParametersUpdated(Convert.vendorInfoFromHal(parameters)));
    }
}
+10 −5
Original line number Diff line number Diff line
@@ -43,7 +43,7 @@ class TunerSession extends ITuner.Stub {

    private final RadioModule mModule;
    private final ITunerSession mHwSession;
    private final TunerCallback mCallback;
    final android.hardware.radio.ITunerCallback mCallback;
    private boolean mIsClosed = false;
    private boolean mIsMuted = false;

@@ -51,7 +51,7 @@ class TunerSession extends ITuner.Stub {
    private RadioManager.BandConfig mDummyConfig = null;

    TunerSession(@NonNull RadioModule module, @NonNull ITunerSession hwSession,
            @NonNull TunerCallback callback) {
            @NonNull android.hardware.radio.ITunerCallback callback) {
        mModule = Objects.requireNonNull(module);
        mHwSession = Objects.requireNonNull(hwSession);
        mCallback = Objects.requireNonNull(callback);
@@ -73,9 +73,14 @@ class TunerSession extends ITuner.Stub {
        synchronized (mLock) {
            if (mIsClosed) return;
            if (error != null) {
                TunerCallback.dispatch(() -> mCallback.mClientCb.onError(error));
                try {
                    mCallback.onError(error);
                } catch (RemoteException ex) {
                    Slog.w(TAG, "mCallback.onError() failed: ", ex);
                }
            }
            mIsClosed = true;
            mModule.onTunerSessionClosed(this);
        }
    }

@@ -96,7 +101,7 @@ class TunerSession extends ITuner.Stub {
            checkNotClosedLocked();
            mDummyConfig = Objects.requireNonNull(config);
            Slog.i(TAG, "Ignoring setConfiguration - not applicable for broadcastradio HAL 2.x");
            TunerCallback.dispatch(() -> mCallback.mClientCb.onConfigurationChanged(config));
            mModule.fanoutAidlCallback(cb -> cb.onConfigurationChanged(config));
        }
    }

@@ -174,7 +179,7 @@ class TunerSession extends ITuner.Stub {
    @Override
    public boolean startBackgroundScan() {
        Slog.i(TAG, "Explicit background scan trigger is not supported with HAL 2.x");
        TunerCallback.dispatch(() -> mCallback.mClientCb.onBackgroundScanComplete());
        mModule.fanoutAidlCallback(cb -> cb.onBackgroundScanComplete());
        return true;
    }