Implement front-end APIs for announcements.
Bug: 68045105 Test: instrumentation (none added) Change-Id: I602e8bb0c40516a732d606f745c8f7721583155f
This commit is contained in:
parent
129f8ae2e8
commit
f151a7b337
@ -177,6 +177,8 @@ java_library {
|
||||
"core/java/android/hardware/location/IContextHubClientCallback.aidl",
|
||||
"core/java/android/hardware/location/IContextHubService.aidl",
|
||||
"core/java/android/hardware/location/IContextHubTransactionCallback.aidl",
|
||||
"core/java/android/hardware/radio/IAnnouncementListener.aidl",
|
||||
"core/java/android/hardware/radio/ICloseHandle.aidl",
|
||||
"core/java/android/hardware/radio/IRadioService.aidl",
|
||||
"core/java/android/hardware/radio/ITuner.aidl",
|
||||
"core/java/android/hardware/radio/ITunerCallback.aidl",
|
||||
|
@ -1739,6 +1739,27 @@ package android.hardware.location {
|
||||
|
||||
package android.hardware.radio {
|
||||
|
||||
public final class Announcement implements android.os.Parcelable {
|
||||
method public int describeContents();
|
||||
method public android.hardware.radio.ProgramSelector getSelector();
|
||||
method public int getType();
|
||||
method public java.util.Map<java.lang.String, java.lang.String> getVendorInfo();
|
||||
method public void writeToParcel(android.os.Parcel, int);
|
||||
field public static final android.os.Parcelable.Creator<android.hardware.radio.Announcement> CREATOR;
|
||||
field public static final int TYPE_EMERGENCY = 1; // 0x1
|
||||
field public static final int TYPE_EVENT = 6; // 0x6
|
||||
field public static final int TYPE_MISC = 8; // 0x8
|
||||
field public static final int TYPE_NEWS = 5; // 0x5
|
||||
field public static final int TYPE_SPORT = 7; // 0x7
|
||||
field public static final int TYPE_TRAFFIC = 3; // 0x3
|
||||
field public static final int TYPE_WARNING = 2; // 0x2
|
||||
field public static final int TYPE_WEATHER = 4; // 0x4
|
||||
}
|
||||
|
||||
public static abstract interface Announcement.OnListUpdatedListener {
|
||||
method public abstract void onListUpdated(java.util.Collection<android.hardware.radio.Announcement>);
|
||||
}
|
||||
|
||||
public final class ProgramList implements java.lang.AutoCloseable {
|
||||
method public void addOnCompleteListener(java.util.concurrent.Executor, android.hardware.radio.ProgramList.OnCompleteListener);
|
||||
method public void addOnCompleteListener(android.hardware.radio.ProgramList.OnCompleteListener);
|
||||
@ -1833,8 +1854,11 @@ package android.hardware.radio {
|
||||
}
|
||||
|
||||
public class RadioManager {
|
||||
method public void addAnnouncementListener(java.util.Set<java.lang.Integer>, android.hardware.radio.Announcement.OnListUpdatedListener);
|
||||
method public void addAnnouncementListener(java.util.concurrent.Executor, java.util.Set<java.lang.Integer>, android.hardware.radio.Announcement.OnListUpdatedListener);
|
||||
method public int listModules(java.util.List<android.hardware.radio.RadioManager.ModuleProperties>);
|
||||
method public android.hardware.radio.RadioTuner openTuner(int, android.hardware.radio.RadioManager.BandConfig, boolean, android.hardware.radio.RadioTuner.Callback, android.os.Handler);
|
||||
method public void removeAnnouncementListener(android.hardware.radio.Announcement.OnListUpdatedListener);
|
||||
field public static final int BAND_AM = 0; // 0x0
|
||||
field public static final int BAND_AM_HD = 3; // 0x3
|
||||
field public static final int BAND_FM = 1; // 0x1
|
||||
|
20
core/java/android/hardware/radio/Announcement.aidl
Normal file
20
core/java/android/hardware/radio/Announcement.aidl
Normal file
@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Copyright (C) 2018 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 android.hardware.radio;
|
||||
|
||||
/** @hide */
|
||||
parcelable Announcement;
|
133
core/java/android/hardware/radio/Announcement.java
Normal file
133
core/java/android/hardware/radio/Announcement.java
Normal file
@ -0,0 +1,133 @@
|
||||
/**
|
||||
* Copyright (C) 2018 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 android.hardware.radio;
|
||||
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.SystemApi;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
public final class Announcement implements Parcelable {
|
||||
|
||||
/** DAB alarm, RDS emergency program type (PTY 31). */
|
||||
public static final int TYPE_EMERGENCY = 1;
|
||||
/** DAB warning. */
|
||||
public static final int TYPE_WARNING = 2;
|
||||
/** DAB road traffic, RDS TA, HD Radio transportation. */
|
||||
public static final int TYPE_TRAFFIC = 3;
|
||||
/** Weather. */
|
||||
public static final int TYPE_WEATHER = 4;
|
||||
/** News. */
|
||||
public static final int TYPE_NEWS = 5;
|
||||
/** DAB event, special event. */
|
||||
public static final int TYPE_EVENT = 6;
|
||||
/** DAB sport report, RDS sports. */
|
||||
public static final int TYPE_SPORT = 7;
|
||||
/** All others. */
|
||||
public static final int TYPE_MISC = 8;
|
||||
/** @hide */
|
||||
@IntDef(prefix = { "TYPE_" }, value = {
|
||||
TYPE_EMERGENCY,
|
||||
TYPE_WARNING,
|
||||
TYPE_TRAFFIC,
|
||||
TYPE_WEATHER,
|
||||
TYPE_NEWS,
|
||||
TYPE_EVENT,
|
||||
TYPE_SPORT,
|
||||
TYPE_MISC,
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface Type {}
|
||||
|
||||
/**
|
||||
* Listener of announcement list events.
|
||||
*/
|
||||
public interface OnListUpdatedListener {
|
||||
/**
|
||||
* An event called whenever a list of active announcements change.
|
||||
*
|
||||
* The entire list is sent each time a new announcement appears or any ends broadcasting.
|
||||
*
|
||||
* @param activeAnnouncements a full list of active announcements
|
||||
*/
|
||||
void onListUpdated(Collection<Announcement> activeAnnouncements);
|
||||
}
|
||||
|
||||
@NonNull private final ProgramSelector mSelector;
|
||||
@Type private final int mType;
|
||||
@NonNull private final Map<String, String> mVendorInfo;
|
||||
|
||||
/** @hide */
|
||||
public Announcement(@NonNull ProgramSelector selector, @Type int type,
|
||||
@NonNull Map<String, String> vendorInfo) {
|
||||
mSelector = Objects.requireNonNull(selector);
|
||||
mType = Objects.requireNonNull(type);
|
||||
mVendorInfo = Objects.requireNonNull(vendorInfo);
|
||||
}
|
||||
|
||||
private Announcement(@NonNull Parcel in) {
|
||||
mSelector = in.readTypedObject(ProgramSelector.CREATOR);
|
||||
mType = in.readInt();
|
||||
mVendorInfo = Utils.readStringMap(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeTypedObject(mSelector, 0);
|
||||
dest.writeInt(mType);
|
||||
Utils.writeStringMap(dest, mVendorInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<Announcement> CREATOR =
|
||||
new Parcelable.Creator<Announcement>() {
|
||||
public Announcement createFromParcel(Parcel in) {
|
||||
return new Announcement(in);
|
||||
}
|
||||
|
||||
public Announcement[] newArray(int size) {
|
||||
return new Announcement[size];
|
||||
}
|
||||
};
|
||||
|
||||
public @NonNull ProgramSelector getSelector() {
|
||||
return mSelector;
|
||||
}
|
||||
|
||||
public @Type int getType() {
|
||||
return mType;
|
||||
}
|
||||
|
||||
public @NonNull Map<String, String> getVendorInfo() {
|
||||
return mVendorInfo;
|
||||
}
|
||||
}
|
24
core/java/android/hardware/radio/IAnnouncementListener.aidl
Normal file
24
core/java/android/hardware/radio/IAnnouncementListener.aidl
Normal file
@ -0,0 +1,24 @@
|
||||
/**
|
||||
* Copyright (C) 2018 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 android.hardware.radio;
|
||||
|
||||
import android.hardware.radio.Announcement;
|
||||
|
||||
/** {@hide} */
|
||||
oneway interface IAnnouncementListener {
|
||||
void onListUpdated(in List<Announcement> activeAnnouncements);
|
||||
}
|
22
core/java/android/hardware/radio/ICloseHandle.aidl
Normal file
22
core/java/android/hardware/radio/ICloseHandle.aidl
Normal file
@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Copyright (C) 2018 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 android.hardware.radio;
|
||||
|
||||
/** {@hide} */
|
||||
interface ICloseHandle {
|
||||
void close();
|
||||
}
|
@ -16,6 +16,8 @@
|
||||
|
||||
package android.hardware.radio;
|
||||
|
||||
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;
|
||||
@ -30,4 +32,7 @@ interface IRadioService {
|
||||
|
||||
ITuner openTuner(int moduleId, in RadioManager.BandConfig bandConfig, boolean withAudio,
|
||||
in ITunerCallback callback);
|
||||
|
||||
ICloseHandle addAnnouncementListener(in int[] enabledTypes,
|
||||
in IAnnouncementListener listener);
|
||||
}
|
||||
|
@ -17,6 +17,7 @@
|
||||
package android.hardware.radio;
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.CallbackExecutor;
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.RequiresPermission;
|
||||
@ -40,6 +41,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@ -1713,6 +1715,68 @@ public class RadioManager {
|
||||
config != null ? config.getType() : BAND_INVALID);
|
||||
}
|
||||
|
||||
private final Map<Announcement.OnListUpdatedListener, ICloseHandle> mAnnouncementListeners =
|
||||
new HashMap<>();
|
||||
|
||||
/**
|
||||
* Adds new announcement listener.
|
||||
*
|
||||
* @param enabledAnnouncementTypes a set of announcement types to listen to
|
||||
* @param listener announcement listener
|
||||
*/
|
||||
@RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO)
|
||||
public void addAnnouncementListener(@NonNull Set<Integer> enabledAnnouncementTypes,
|
||||
@NonNull Announcement.OnListUpdatedListener listener) {
|
||||
addAnnouncementListener(cmd -> cmd.run(), enabledAnnouncementTypes, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds new announcement listener with executor.
|
||||
*
|
||||
* @param executor the executor
|
||||
* @param enabledAnnouncementTypes a set of announcement types to listen to
|
||||
* @param listener announcement listener
|
||||
*/
|
||||
@RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO)
|
||||
public void addAnnouncementListener(@NonNull @CallbackExecutor Executor executor,
|
||||
@NonNull Set<Integer> enabledAnnouncementTypes,
|
||||
@NonNull Announcement.OnListUpdatedListener listener) {
|
||||
Objects.requireNonNull(executor);
|
||||
Objects.requireNonNull(listener);
|
||||
int[] types = enabledAnnouncementTypes.stream().mapToInt(Integer::intValue).toArray();
|
||||
IAnnouncementListener listenerIface = new IAnnouncementListener.Stub() {
|
||||
public void onListUpdated(List<Announcement> activeAnnouncements) {
|
||||
executor.execute(() -> listener.onListUpdated(activeAnnouncements));
|
||||
}
|
||||
};
|
||||
synchronized (mAnnouncementListeners) {
|
||||
ICloseHandle closeHandle = null;
|
||||
try {
|
||||
closeHandle = mService.addAnnouncementListener(types, listenerIface);
|
||||
} catch (RemoteException ex) {
|
||||
ex.rethrowFromSystemServer();
|
||||
}
|
||||
Objects.requireNonNull(closeHandle);
|
||||
ICloseHandle oldCloseHandle = mAnnouncementListeners.put(listener, closeHandle);
|
||||
if (oldCloseHandle != null) Utils.close(oldCloseHandle);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes previously registered announcement listener.
|
||||
*
|
||||
* @param listener announcement listener, previously registered with
|
||||
* {@link addAnnouncementListener}
|
||||
*/
|
||||
@RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO)
|
||||
public void removeAnnouncementListener(@NonNull Announcement.OnListUpdatedListener listener) {
|
||||
Objects.requireNonNull(listener);
|
||||
synchronized (mAnnouncementListeners) {
|
||||
ICloseHandle closeHandle = mAnnouncementListeners.remove(listener);
|
||||
if (closeHandle != null) Utils.close(closeHandle);
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull private final Context mContext;
|
||||
@NonNull private final IRadioService mService;
|
||||
|
||||
|
@ -20,6 +20,7 @@ import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.os.RemoteException;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
@ -28,6 +29,8 @@ import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
final class Utils {
|
||||
private static final String TAG = "BroadcastRadio.utils";
|
||||
|
||||
static void writeStringMap(@NonNull Parcel dest, @Nullable Map<String, String> map) {
|
||||
if (map == null) {
|
||||
dest.writeInt(0);
|
||||
@ -89,4 +92,12 @@ final class Utils {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static void close(ICloseHandle handle) {
|
||||
try {
|
||||
handle.close();
|
||||
} catch (RemoteException ex) {
|
||||
ex.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,8 @@ import android.annotation.NonNull;
|
||||
import android.Manifest;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
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;
|
||||
@ -28,14 +30,18 @@ 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.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.OptionalInt;
|
||||
|
||||
public class BroadcastRadioService extends SystemService {
|
||||
private static final String TAG = "BcRadioSrv";
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
private final ServiceImpl mServiceImpl = new ServiceImpl();
|
||||
|
||||
@ -88,7 +94,7 @@ public class BroadcastRadioService extends SystemService {
|
||||
@Override
|
||||
public ITuner openTuner(int moduleId, RadioManager.BandConfig bandConfig,
|
||||
boolean withAudio, ITunerCallback callback) throws RemoteException {
|
||||
Slog.i(TAG, "openTuner(" + moduleId + ", _, " + withAudio + ", _)");
|
||||
if (DEBUG) Slog.i(TAG, "Opening module " + moduleId);
|
||||
enforcePolicyAccess();
|
||||
if (callback == null) {
|
||||
throw new IllegalArgumentException("Callback must not be empty");
|
||||
@ -101,5 +107,25 @@ public class BroadcastRadioService extends SystemService {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ICloseHandle addAnnouncementListener(int[] enabledTypes,
|
||||
IAnnouncementListener listener) {
|
||||
if (DEBUG) {
|
||||
Slog.i(TAG, "Adding announcement listener for " + Arrays.toString(enabledTypes));
|
||||
}
|
||||
Objects.requireNonNull(enabledTypes);
|
||||
Objects.requireNonNull(listener);
|
||||
enforcePolicyAccess();
|
||||
|
||||
synchronized (mLock) {
|
||||
if (!mHal2.hasAnyModules()) {
|
||||
Slog.i(TAG, "There are no HAL 2.x modules registered");
|
||||
return new AnnouncementAggregator(listener);
|
||||
}
|
||||
|
||||
return mHal2.addAnnouncementListener(enabledTypes, listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,128 @@
|
||||
/**
|
||||
* Copyright (C) 2018 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.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.Slog;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class AnnouncementAggregator extends ICloseHandle.Stub {
|
||||
private static final String TAG = "BcRadio2Srv.AnnAggr";
|
||||
|
||||
private final Object mLock = new Object();
|
||||
@NonNull private final IAnnouncementListener mListener;
|
||||
private final IBinder.DeathRecipient mDeathRecipient = new DeathRecipient();
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private final Collection<ModuleWatcher> mModuleWatchers = new ArrayList<>();
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private boolean mIsClosed = false;
|
||||
|
||||
public AnnouncementAggregator(@NonNull IAnnouncementListener listener) {
|
||||
mListener = Objects.requireNonNull(listener);
|
||||
try {
|
||||
listener.asBinder().linkToDeath(mDeathRecipient, 0);
|
||||
} catch (RemoteException ex) {
|
||||
ex.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
private class ModuleWatcher extends IAnnouncementListener.Stub {
|
||||
private @Nullable ICloseHandle mCloseHandle;
|
||||
public @NonNull List<Announcement> currentList = new ArrayList<>();
|
||||
|
||||
public void onListUpdated(List<Announcement> active) {
|
||||
currentList = Objects.requireNonNull(active);
|
||||
AnnouncementAggregator.this.onListUpdated();
|
||||
}
|
||||
|
||||
public void setCloseHandle(@NonNull ICloseHandle closeHandle) {
|
||||
mCloseHandle = Objects.requireNonNull(closeHandle);
|
||||
}
|
||||
|
||||
public void close() throws RemoteException {
|
||||
if (mCloseHandle != null) mCloseHandle.close();
|
||||
}
|
||||
}
|
||||
|
||||
private class DeathRecipient implements IBinder.DeathRecipient {
|
||||
public void binderDied() {
|
||||
try {
|
||||
close();
|
||||
} catch (RemoteException ex) {}
|
||||
}
|
||||
}
|
||||
|
||||
private void onListUpdated() {
|
||||
synchronized (mLock) {
|
||||
if (mIsClosed) {
|
||||
Slog.e(TAG, "Announcement aggregator is closed, it shouldn't receive callbacks");
|
||||
return;
|
||||
}
|
||||
List<Announcement> combined = new ArrayList<>();
|
||||
for (ModuleWatcher watcher : mModuleWatchers) {
|
||||
combined.addAll(watcher.currentList);
|
||||
}
|
||||
TunerCallback.dispatch(() -> mListener.onListUpdated(combined));
|
||||
}
|
||||
}
|
||||
|
||||
public void watchModule(@NonNull RadioModule module, @NonNull int[] enabledTypes) {
|
||||
synchronized (mLock) {
|
||||
if (mIsClosed) throw new IllegalStateException();
|
||||
|
||||
ModuleWatcher watcher = new ModuleWatcher();
|
||||
ICloseHandle closeHandle;
|
||||
try {
|
||||
closeHandle = module.addAnnouncementListener(enabledTypes, watcher);
|
||||
} catch (RemoteException ex) {
|
||||
Slog.e(TAG, "Failed to add announcement listener", ex);
|
||||
return;
|
||||
}
|
||||
watcher.setCloseHandle(closeHandle);
|
||||
mModuleWatchers.add(watcher);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws RemoteException {
|
||||
synchronized (mLock) {
|
||||
if (mIsClosed) return;
|
||||
mIsClosed = true;
|
||||
|
||||
mListener.asBinder().unlinkToDeath(mDeathRecipient, 0);
|
||||
|
||||
for (ModuleWatcher watcher : mModuleWatchers) {
|
||||
watcher.close();
|
||||
}
|
||||
mModuleWatchers.clear();
|
||||
}
|
||||
}
|
||||
}
|
@ -18,6 +18,8 @@ package com.android.server.broadcastradio.hal2;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
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;
|
||||
@ -81,6 +83,10 @@ public class BroadcastRadioService {
|
||||
return mModules.containsKey(id);
|
||||
}
|
||||
|
||||
public boolean hasAnyModules() {
|
||||
return !mModules.isEmpty();
|
||||
}
|
||||
|
||||
public ITuner openSession(int moduleId, @Nullable RadioManager.BandConfig legacyConfig,
|
||||
boolean withAudio, @NonNull ITunerCallback callback) throws RemoteException {
|
||||
Objects.requireNonNull(callback);
|
||||
@ -98,4 +104,22 @@ public class BroadcastRadioService {
|
||||
session.setConfiguration(legacyConfig);
|
||||
return session;
|
||||
}
|
||||
|
||||
public ICloseHandle addAnnouncementListener(@NonNull int[] enabledTypes,
|
||||
@NonNull IAnnouncementListener listener) {
|
||||
AnnouncementAggregator aggregator = new AnnouncementAggregator(listener);
|
||||
boolean anySupported = false;
|
||||
for (RadioModule module : mModules.values()) {
|
||||
try {
|
||||
aggregator.watchModule(module, enabledTypes);
|
||||
anySupported = true;
|
||||
} catch (UnsupportedOperationException ex) {
|
||||
Slog.v(TAG, "Announcements not supported for this module", ex);
|
||||
}
|
||||
}
|
||||
if (!anySupported) {
|
||||
Slog.i(TAG, "There are no HAL modules that support announcements");
|
||||
}
|
||||
return aggregator;
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.hardware.broadcastradio.V2_0.AmFmBandRange;
|
||||
import android.hardware.broadcastradio.V2_0.AmFmRegionConfig;
|
||||
import android.hardware.broadcastradio.V2_0.Announcement;
|
||||
import android.hardware.broadcastradio.V2_0.ProgramFilter;
|
||||
import android.hardware.broadcastradio.V2_0.ProgramIdentifier;
|
||||
import android.hardware.broadcastradio.V2_0.ProgramInfo;
|
||||
@ -266,4 +267,13 @@ class Convert {
|
||||
|
||||
return new ProgramList.Chunk(chunk.purge, chunk.complete, modified, removed);
|
||||
}
|
||||
|
||||
public static @NonNull android.hardware.radio.Announcement announcementFromHal(
|
||||
@NonNull Announcement hwAnnouncement) {
|
||||
return new android.hardware.radio.Announcement(
|
||||
programSelectorFromHal(hwAnnouncement.selector),
|
||||
hwAnnouncement.type,
|
||||
vendorInfoFromHal(hwAnnouncement.vendorInfo)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,10 @@ import android.annotation.Nullable;
|
||||
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.IAnnouncementListener;
|
||||
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;
|
||||
@ -29,7 +32,11 @@ 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;
|
||||
|
||||
class RadioModule {
|
||||
private static final String TAG = "BcRadio2Srv.module";
|
||||
@ -79,4 +86,37 @@ class RadioModule {
|
||||
|
||||
return new TunerSession(hwSession.value, cb);
|
||||
}
|
||||
|
||||
public android.hardware.radio.ICloseHandle addAnnouncementListener(@NonNull int[] enabledTypes,
|
||||
@NonNull android.hardware.radio.IAnnouncementListener listener) throws RemoteException {
|
||||
ArrayList<Byte> enabledList = new ArrayList<>();
|
||||
for (int type : enabledTypes) {
|
||||
enabledList.add((byte)type);
|
||||
}
|
||||
|
||||
MutableInt halResult = new MutableInt(Result.UNKNOWN_ERROR);
|
||||
Mutable<ICloseHandle> hwCloseHandle = new Mutable<>();
|
||||
IAnnouncementListener hwListener = new IAnnouncementListener.Stub() {
|
||||
public void onListUpdated(ArrayList<Announcement> hwAnnouncements)
|
||||
throws RemoteException {
|
||||
listener.onListUpdated(hwAnnouncements.stream().
|
||||
map(a -> Convert.announcementFromHal(a)).collect(Collectors.toList()));
|
||||
}
|
||||
};
|
||||
mService.registerAnnouncementListener(enabledList, hwListener, (result, closeHandle) -> {
|
||||
halResult.value = result;
|
||||
hwCloseHandle.value = closeHandle;
|
||||
});
|
||||
Convert.throwOnError("addAnnouncementListener", halResult.value);
|
||||
|
||||
return new android.hardware.radio.ICloseHandle.Stub() {
|
||||
public void close() {
|
||||
try {
|
||||
hwCloseHandle.value.close();
|
||||
} catch (RemoteException ex) {
|
||||
Slog.e(TAG, "Failed closing announcement listener", ex);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user