• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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