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.IndentingPrintWriter; 33 import android.util.Slog; 34 35 import com.android.internal.annotations.GuardedBy; 36 37 import java.util.Collection; 38 import java.util.HashMap; 39 import java.util.Map; 40 import java.util.Objects; 41 import java.util.stream.Collectors; 42 43 public class BroadcastRadioService { 44 private static final String TAG = "BcRadio2Srv"; 45 46 private final Object mLock; 47 48 @GuardedBy("mLock") 49 private int mNextModuleId = 0; 50 51 @GuardedBy("mLock") 52 private final Map<String, Integer> mServiceNameToModuleIdMap = new HashMap<>(); 53 54 // Map from module ID to RadioModule created by mServiceListener.onRegistration(). 55 @GuardedBy("mLock") 56 private final Map<Integer, RadioModule> mModules = new HashMap<>(); 57 58 private IServiceNotification.Stub mServiceListener = new IServiceNotification.Stub() { 59 @Override 60 public void onRegistration(String fqName, String serviceName, boolean preexisting) { 61 Slog.v(TAG, "onRegistration(" + fqName + ", " + serviceName + ", " + preexisting + ")"); 62 Integer moduleId; 63 synchronized (mLock) { 64 // If the service has been registered before, reuse its previous module ID. 65 moduleId = mServiceNameToModuleIdMap.get(serviceName); 66 boolean newService = false; 67 if (moduleId == null) { 68 newService = true; 69 moduleId = mNextModuleId; 70 } 71 72 RadioModule module = RadioModule.tryLoadingModule(moduleId, serviceName, mLock); 73 if (module == null) { 74 return; 75 } 76 Slog.v(TAG, "loaded broadcast radio module " + moduleId + ": " + serviceName 77 + " (HAL 2.0)"); 78 RadioModule prevModule = mModules.put(moduleId, module); 79 if (prevModule != null) { 80 prevModule.closeSessions(RadioTuner.ERROR_HARDWARE_FAILURE); 81 } 82 83 if (newService) { 84 mServiceNameToModuleIdMap.put(serviceName, moduleId); 85 mNextModuleId++; 86 } 87 88 try { 89 module.getService().linkToDeath(mDeathRecipient, moduleId); 90 } catch (RemoteException ex) { 91 // Service has already died, so remove its entry from mModules. 92 mModules.remove(moduleId); 93 } 94 } 95 } 96 }; 97 98 private DeathRecipient mDeathRecipient = new DeathRecipient() { 99 @Override 100 public void serviceDied(long cookie) { 101 Slog.v(TAG, "serviceDied(" + cookie + ")"); 102 synchronized (mLock) { 103 int moduleId = (int) cookie; 104 RadioModule prevModule = mModules.remove(moduleId); 105 if (prevModule != null) { 106 prevModule.closeSessions(RadioTuner.ERROR_HARDWARE_FAILURE); 107 } 108 109 for (Map.Entry<String, Integer> entry : mServiceNameToModuleIdMap.entrySet()) { 110 if (entry.getValue() == moduleId) { 111 Slog.i(TAG, "service " + entry.getKey() 112 + " died; removed RadioModule with ID " + moduleId); 113 return; 114 } 115 } 116 } 117 } 118 }; 119 BroadcastRadioService(int nextModuleId, Object lock)120 public BroadcastRadioService(int nextModuleId, Object lock) { 121 mNextModuleId = nextModuleId; 122 mLock = lock; 123 try { 124 IServiceManager manager = IServiceManager.getService(); 125 if (manager == null) { 126 Slog.e(TAG, "failed to get HIDL Service Manager"); 127 return; 128 } 129 manager.registerForNotifications(IBroadcastRadio.kInterfaceName, "", mServiceListener); 130 } catch (RemoteException ex) { 131 Slog.e(TAG, "failed to register for service notifications: ", ex); 132 } 133 } 134 listModules()135 public @NonNull Collection<RadioManager.ModuleProperties> listModules() { 136 Slog.v(TAG, "List HIDL 2.0 modules"); 137 synchronized (mLock) { 138 return mModules.values().stream().map(module -> module.mProperties) 139 .collect(Collectors.toList()); 140 } 141 } 142 hasModule(int id)143 public boolean hasModule(int id) { 144 synchronized (mLock) { 145 return mModules.containsKey(id); 146 } 147 } 148 hasAnyModules()149 public boolean hasAnyModules() { 150 synchronized (mLock) { 151 return !mModules.isEmpty(); 152 } 153 } 154 openSession(int moduleId, @Nullable RadioManager.BandConfig legacyConfig, boolean withAudio, @NonNull ITunerCallback callback)155 public ITuner openSession(int moduleId, @Nullable RadioManager.BandConfig legacyConfig, 156 boolean withAudio, @NonNull ITunerCallback callback) throws RemoteException { 157 Slog.v(TAG, "Open HIDL 2.0 session"); 158 Objects.requireNonNull(callback); 159 160 if (!withAudio) { 161 throw new IllegalArgumentException("Non-audio sessions not supported with HAL 2.0"); 162 } 163 164 RadioModule module = null; 165 synchronized (mLock) { 166 module = mModules.get(moduleId); 167 if (module == null) { 168 throw new IllegalArgumentException("Invalid module ID"); 169 } 170 } 171 172 TunerSession tunerSession = module.openSession(callback); 173 if (legacyConfig != null) { 174 tunerSession.setConfiguration(legacyConfig); 175 } 176 return tunerSession; 177 } 178 addAnnouncementListener(@onNull int[] enabledTypes, @NonNull IAnnouncementListener listener)179 public ICloseHandle addAnnouncementListener(@NonNull int[] enabledTypes, 180 @NonNull IAnnouncementListener listener) { 181 Slog.v(TAG, "Add announcementListener"); 182 AnnouncementAggregator aggregator = new AnnouncementAggregator(listener, mLock); 183 boolean anySupported = false; 184 synchronized (mLock) { 185 for (RadioModule module : mModules.values()) { 186 try { 187 aggregator.watchModule(module, enabledTypes); 188 anySupported = true; 189 } catch (UnsupportedOperationException ex) { 190 Slog.v(TAG, "Announcements not supported for this module", ex); 191 } 192 } 193 } 194 if (!anySupported) { 195 Slog.i(TAG, "There are no HAL modules that support announcements"); 196 } 197 return aggregator; 198 } 199 200 /** 201 * Dump state of broadcastradio service for HIDL HAL 2.0. 202 * 203 * @param pw The file to which BroadcastRadioService state is dumped. 204 */ dumpInfo(IndentingPrintWriter pw)205 public void dumpInfo(IndentingPrintWriter pw) { 206 synchronized (mLock) { 207 pw.printf("Next module id available: %d\n", mNextModuleId); 208 pw.printf("ServiceName to module id map:\n"); 209 pw.increaseIndent(); 210 for (Map.Entry<String, Integer> entry : mServiceNameToModuleIdMap.entrySet()) { 211 pw.printf("Service name: %s, module id: %d\n", entry.getKey(), entry.getValue()); 212 } 213 pw.decreaseIndent(); 214 pw.printf("Radio modules:\n"); 215 pw.increaseIndent(); 216 for (Map.Entry<Integer, RadioModule> moduleEntry : mModules.entrySet()) { 217 pw.printf("Module id=%d:\n", moduleEntry.getKey()); 218 pw.increaseIndent(); 219 moduleEntry.getValue().dumpInfo(pw); 220 pw.decreaseIndent(); 221 } 222 pw.decreaseIndent(); 223 } 224 } 225 } 226