1 /** 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.broadcastradio.hal2; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.hardware.broadcastradio.V2_0.IBroadcastRadio; 22 import android.hardware.radio.IAnnouncementListener; 23 import android.hardware.radio.ICloseHandle; 24 import android.hardware.radio.ITuner; 25 import android.hardware.radio.ITunerCallback; 26 import android.hardware.radio.RadioManager; 27 import android.hardware.radio.RadioTuner; 28 import android.hidl.manager.V1_0.IServiceManager; 29 import android.hidl.manager.V1_0.IServiceNotification; 30 import android.os.IHwBinder.DeathRecipient; 31 import android.os.RemoteException; 32 import android.util.Slog; 33 34 import com.android.internal.annotations.GuardedBy; 35 36 import java.util.Collection; 37 import java.util.HashMap; 38 import java.util.Map; 39 import java.util.Objects; 40 import java.util.stream.Collectors; 41 42 public class BroadcastRadioService { 43 private static final String TAG = "BcRadio2Srv"; 44 45 private final Object mLock = new Object(); 46 47 @GuardedBy("mLock") 48 private int mNextModuleId = 0; 49 50 @GuardedBy("mLock") 51 private final Map<String, Integer> mServiceNameToModuleIdMap = new HashMap<>(); 52 53 // Map from module ID to RadioModule created by mServiceListener.onRegistration(). 54 @GuardedBy("mLock") 55 private final Map<Integer, RadioModule> mModules = new HashMap<>(); 56 57 private IServiceNotification.Stub mServiceListener = new IServiceNotification.Stub() { 58 @Override 59 public void onRegistration(String fqName, String serviceName, boolean preexisting) { 60 Slog.v(TAG, "onRegistration(" + fqName + ", " + serviceName + ", " + preexisting + ")"); 61 Integer moduleId; 62 synchronized (mLock) { 63 // If the service has been registered before, reuse its previous module ID. 64 moduleId = mServiceNameToModuleIdMap.get(serviceName); 65 boolean newService = false; 66 if (moduleId == null) { 67 newService = true; 68 moduleId = mNextModuleId; 69 } 70 71 RadioModule module = RadioModule.tryLoadingModule(moduleId, serviceName); 72 if (module == null) { 73 return; 74 } 75 Slog.v(TAG, "loaded broadcast radio module " + moduleId + ": " + serviceName 76 + " (HAL 2.0)"); 77 RadioModule prevModule = mModules.put(moduleId, module); 78 if (prevModule != null) { 79 prevModule.closeSessions(RadioTuner.ERROR_HARDWARE_FAILURE); 80 } 81 82 if (newService) { 83 mServiceNameToModuleIdMap.put(serviceName, moduleId); 84 mNextModuleId++; 85 } 86 87 try { 88 module.getService().linkToDeath(mDeathRecipient, moduleId); 89 } catch (RemoteException ex) { 90 // Service has already died, so remove its entry from mModules. 91 mModules.remove(moduleId); 92 } 93 } 94 } 95 }; 96 97 private DeathRecipient mDeathRecipient = new DeathRecipient() { 98 @Override 99 public void serviceDied(long cookie) { 100 Slog.v(TAG, "serviceDied(" + cookie + ")"); 101 synchronized (mLock) { 102 int moduleId = (int) cookie; 103 RadioModule prevModule = mModules.remove(moduleId); 104 if (prevModule != null) { 105 prevModule.closeSessions(RadioTuner.ERROR_HARDWARE_FAILURE); 106 } 107 108 for (Map.Entry<String, Integer> entry : mServiceNameToModuleIdMap.entrySet()) { 109 if (entry.getValue() == moduleId) { 110 Slog.i(TAG, "service " + entry.getKey() 111 + " died; removed RadioModule with ID " + moduleId); 112 return; 113 } 114 } 115 } 116 } 117 }; 118 BroadcastRadioService(int nextModuleId)119 public BroadcastRadioService(int nextModuleId) { 120 mNextModuleId = nextModuleId; 121 try { 122 IServiceManager manager = IServiceManager.getService(); 123 if (manager == null) { 124 Slog.e(TAG, "failed to get HIDL Service Manager"); 125 return; 126 } 127 manager.registerForNotifications(IBroadcastRadio.kInterfaceName, "", mServiceListener); 128 } catch (RemoteException ex) { 129 Slog.e(TAG, "failed to register for service notifications: ", ex); 130 } 131 } 132 listModules()133 public @NonNull Collection<RadioManager.ModuleProperties> listModules() { 134 synchronized (mLock) { 135 return mModules.values().stream().map(module -> module.mProperties) 136 .collect(Collectors.toList()); 137 } 138 } 139 hasModule(int id)140 public boolean hasModule(int id) { 141 synchronized (mLock) { 142 return mModules.containsKey(id); 143 } 144 } 145 hasAnyModules()146 public boolean hasAnyModules() { 147 synchronized (mLock) { 148 return !mModules.isEmpty(); 149 } 150 } 151 openSession(int moduleId, @Nullable RadioManager.BandConfig legacyConfig, boolean withAudio, @NonNull ITunerCallback callback)152 public ITuner openSession(int moduleId, @Nullable RadioManager.BandConfig legacyConfig, 153 boolean withAudio, @NonNull ITunerCallback callback) throws RemoteException { 154 Objects.requireNonNull(callback); 155 156 if (!withAudio) { 157 throw new IllegalArgumentException("Non-audio sessions not supported with HAL 2.x"); 158 } 159 160 RadioModule module = null; 161 synchronized (mLock) { 162 module = mModules.get(moduleId); 163 if (module == null) { 164 throw new IllegalArgumentException("Invalid module ID"); 165 } 166 } 167 168 TunerSession tunerSession = module.openSession(callback); 169 if (legacyConfig != null) { 170 tunerSession.setConfiguration(legacyConfig); 171 } 172 return tunerSession; 173 } 174 addAnnouncementListener(@onNull int[] enabledTypes, @NonNull IAnnouncementListener listener)175 public ICloseHandle addAnnouncementListener(@NonNull int[] enabledTypes, 176 @NonNull IAnnouncementListener listener) { 177 AnnouncementAggregator aggregator = new AnnouncementAggregator(listener); 178 boolean anySupported = false; 179 synchronized (mLock) { 180 for (RadioModule module : mModules.values()) { 181 try { 182 aggregator.watchModule(module, enabledTypes); 183 anySupported = true; 184 } catch (UnsupportedOperationException ex) { 185 Slog.v(TAG, "Announcements not supported for this module", ex); 186 } 187 } 188 } 189 if (!anySupported) { 190 Slog.i(TAG, "There are no HAL modules that support announcements"); 191 } 192 return aggregator; 193 } 194 } 195