1 /**
2  * Copyright (C) 2022 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;
18 
19 import android.Manifest;
20 import android.content.pm.PackageManager;
21 import android.hardware.radio.IAnnouncementListener;
22 import android.hardware.radio.ICloseHandle;
23 import android.hardware.radio.IRadioService;
24 import android.hardware.radio.ITuner;
25 import android.hardware.radio.ITunerCallback;
26 import android.hardware.radio.RadioManager;
27 import android.os.Binder;
28 import android.os.RemoteException;
29 import android.util.IndentingPrintWriter;
30 import android.util.Log;
31 import android.util.Slog;
32 
33 import com.android.internal.annotations.GuardedBy;
34 import com.android.internal.annotations.VisibleForTesting;
35 import com.android.server.broadcastradio.hal2.AnnouncementAggregator;
36 
37 import java.io.FileDescriptor;
38 import java.io.PrintWriter;
39 import java.util.ArrayList;
40 import java.util.Arrays;
41 import java.util.Collection;
42 import java.util.List;
43 import java.util.Objects;
44 import java.util.OptionalInt;
45 
46 /**
47  * Wrapper for HIDL interface for BroadcastRadio HAL
48  */
49 final class IRadioServiceHidlImpl extends IRadioService.Stub {
50     private static final String TAG = "BcRadioSrvHidl";
51 
52     private final com.android.server.broadcastradio.hal1.BroadcastRadioService mHal1Client;
53     private final com.android.server.broadcastradio.hal2.BroadcastRadioService mHal2Client;
54 
55     private final Object mLock = new Object();
56 
57     private final BroadcastRadioService mService;
58 
59     @GuardedBy("mLock")
60     private final List<RadioManager.ModuleProperties> mV1Modules;
61 
IRadioServiceHidlImpl(BroadcastRadioService service)62     IRadioServiceHidlImpl(BroadcastRadioService service) {
63         mService = Objects.requireNonNull(service, "broadcast radio service cannot be null");
64         mHal1Client = new com.android.server.broadcastradio.hal1.BroadcastRadioService();
65         mV1Modules = mHal1Client.loadModules();
66         OptionalInt max = mV1Modules.stream().mapToInt(RadioManager.ModuleProperties::getId).max();
67         mHal2Client = new com.android.server.broadcastradio.hal2.BroadcastRadioService(
68                 max.isPresent() ? max.getAsInt() + 1 : 0);
69     }
70 
71     @VisibleForTesting
IRadioServiceHidlImpl(BroadcastRadioService service, com.android.server.broadcastradio.hal1.BroadcastRadioService hal1, com.android.server.broadcastradio.hal2.BroadcastRadioService hal2)72     IRadioServiceHidlImpl(BroadcastRadioService service,
73             com.android.server.broadcastradio.hal1.BroadcastRadioService hal1,
74             com.android.server.broadcastradio.hal2.BroadcastRadioService hal2) {
75         mService = Objects.requireNonNull(service, "Broadcast radio service cannot be null");
76         mHal1Client = Objects.requireNonNull(hal1,
77                 "Broadcast radio service implementation for HIDL 1 HAL cannot be null");
78         mV1Modules = mHal1Client.loadModules();
79         mHal2Client = Objects.requireNonNull(hal2,
80                 "Broadcast radio service implementation for HIDL 2 HAL cannot be null");
81     }
82 
83     @Override
listModules()84     public List<RadioManager.ModuleProperties> listModules() {
85         mService.enforcePolicyAccess();
86         Collection<RadioManager.ModuleProperties> v2Modules = mHal2Client.listModules();
87         List<RadioManager.ModuleProperties> modules;
88         synchronized (mLock) {
89             modules = new ArrayList<>(mV1Modules.size() + v2Modules.size());
90             modules.addAll(mV1Modules);
91         }
92         modules.addAll(v2Modules);
93         return modules;
94     }
95 
96     @Override
openTuner(int moduleId, RadioManager.BandConfig bandConfig, boolean withAudio, ITunerCallback callback)97     public ITuner openTuner(int moduleId, RadioManager.BandConfig bandConfig,
98             boolean withAudio, ITunerCallback callback) throws RemoteException {
99         if (isDebugEnabled()) {
100             Slog.d(TAG, "Opening module " + moduleId);
101         }
102         mService.enforcePolicyAccess();
103         Objects.requireNonNull(callback, "Callback must not be null");
104         synchronized (mLock) {
105             if (mHal2Client.hasModule(moduleId)) {
106                 return mHal2Client.openSession(moduleId, bandConfig, withAudio, callback);
107             } else {
108                 return mHal1Client.openTuner(moduleId, bandConfig, withAudio, callback);
109             }
110         }
111     }
112 
113     @Override
addAnnouncementListener(int[] enabledTypes, IAnnouncementListener listener)114     public ICloseHandle addAnnouncementListener(int[] enabledTypes,
115             IAnnouncementListener listener) {
116         if (isDebugEnabled()) {
117             Slog.d(TAG, "Adding announcement listener for " + Arrays.toString(enabledTypes));
118         }
119         Objects.requireNonNull(enabledTypes, "Enabled announcement types cannot be null");
120         Objects.requireNonNull(listener, "Announcement listener cannot be null");
121         mService.enforcePolicyAccess();
122 
123         synchronized (mLock) {
124             if (!mHal2Client.hasAnyModules()) {
125                 Slog.w(TAG, "There are no HAL 2.0 modules registered");
126                 return new AnnouncementAggregator(listener, mLock);
127             }
128 
129             return mHal2Client.addAnnouncementListener(enabledTypes, listener);
130         }
131     }
132 
133     @Override
dump(FileDescriptor fd, PrintWriter pw, String[] args)134     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
135         if (mService.getContext().checkCallingOrSelfPermission(Manifest.permission.DUMP)
136                 != PackageManager.PERMISSION_GRANTED) {
137             pw.println("Permission Denial: can't dump HIDL BroadcastRadioService from "
138                     + "from pid=" + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
139                     + " without permission " + Manifest.permission.DUMP);
140             return;
141         }
142         IndentingPrintWriter radioPw = new IndentingPrintWriter(pw);
143         radioPw.printf("BroadcastRadioService\n");
144 
145         radioPw.increaseIndent();
146         radioPw.printf("HAL1 client: %s\n", mHal1Client);
147 
148         radioPw.increaseIndent();
149         synchronized (mLock) {
150             radioPw.printf("Modules of HAL1 client: %s\n", mV1Modules);
151         }
152         radioPw.decreaseIndent();
153 
154         radioPw.printf("HAL2 client:\n");
155 
156         radioPw.increaseIndent();
157         mHal2Client.dumpInfo(radioPw);
158         radioPw.decreaseIndent();
159 
160         radioPw.decreaseIndent();
161     }
162 
163 
isDebugEnabled()164     private static boolean isDebugEnabled() {
165         return Log.isLoggable(TAG, Log.DEBUG);
166     }
167 }
168