1 /**
2  * Copyright (C) 2018 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.radio.Announcement;
22 import android.hardware.radio.IAnnouncementListener;
23 import android.hardware.radio.ICloseHandle;
24 import android.os.IBinder;
25 import android.os.RemoteException;
26 import android.util.Slog;
27 
28 import com.android.internal.annotations.GuardedBy;
29 
30 import java.util.ArrayList;
31 import java.util.Collection;
32 import java.util.List;
33 import java.util.Objects;
34 
35 public class AnnouncementAggregator extends ICloseHandle.Stub {
36     private static final String TAG = "BcRadio2Srv.AnnAggr";
37 
38     private final Object mLock = new Object();
39     @NonNull private final IAnnouncementListener mListener;
40     private final IBinder.DeathRecipient mDeathRecipient = new DeathRecipient();
41 
42     @GuardedBy("mLock")
43     private final Collection<ModuleWatcher> mModuleWatchers = new ArrayList<>();
44 
45     @GuardedBy("mLock")
46     private boolean mIsClosed = false;
47 
AnnouncementAggregator(@onNull IAnnouncementListener listener)48     public AnnouncementAggregator(@NonNull IAnnouncementListener listener) {
49         mListener = Objects.requireNonNull(listener);
50         try {
51             listener.asBinder().linkToDeath(mDeathRecipient, 0);
52         } catch (RemoteException ex) {
53             ex.rethrowFromSystemServer();
54         }
55     }
56 
57     private class ModuleWatcher extends IAnnouncementListener.Stub {
58         private @Nullable ICloseHandle mCloseHandle;
59         public @NonNull List<Announcement> currentList = new ArrayList<>();
60 
onListUpdated(List<Announcement> active)61         public void onListUpdated(List<Announcement> active) {
62             currentList = Objects.requireNonNull(active);
63             AnnouncementAggregator.this.onListUpdated();
64         }
65 
setCloseHandle(@onNull ICloseHandle closeHandle)66         public void setCloseHandle(@NonNull ICloseHandle closeHandle) {
67             mCloseHandle = Objects.requireNonNull(closeHandle);
68         }
69 
close()70         public void close() throws RemoteException {
71             if (mCloseHandle != null) mCloseHandle.close();
72         }
73     }
74 
75     private class DeathRecipient implements IBinder.DeathRecipient {
binderDied()76         public void binderDied() {
77             try {
78                 close();
79             } catch (RemoteException ex) {}
80         }
81     }
82 
onListUpdated()83     private void onListUpdated() {
84         synchronized (mLock) {
85             if (mIsClosed) {
86                 Slog.e(TAG, "Announcement aggregator is closed, it shouldn't receive callbacks");
87                 return;
88             }
89             List<Announcement> combined = new ArrayList<>();
90             for (ModuleWatcher watcher : mModuleWatchers) {
91                 combined.addAll(watcher.currentList);
92             }
93             try {
94                 mListener.onListUpdated(combined);
95             } catch (RemoteException ex) {
96                 Slog.e(TAG, "mListener.onListUpdated() failed: ", ex);
97             }
98         }
99     }
100 
watchModule(@onNull RadioModule module, @NonNull int[] enabledTypes)101     public void watchModule(@NonNull RadioModule module, @NonNull int[] enabledTypes) {
102         synchronized (mLock) {
103             if (mIsClosed) throw new IllegalStateException();
104 
105             ModuleWatcher watcher = new ModuleWatcher();
106             ICloseHandle closeHandle;
107             try {
108                 closeHandle = module.addAnnouncementListener(enabledTypes, watcher);
109             } catch (RemoteException ex) {
110                 Slog.e(TAG, "Failed to add announcement listener", ex);
111                 return;
112             }
113             watcher.setCloseHandle(closeHandle);
114             mModuleWatchers.add(watcher);
115         }
116     }
117 
118     @Override
close()119     public void close() throws RemoteException {
120         synchronized (mLock) {
121             if (mIsClosed) return;
122             mIsClosed = true;
123 
124             mListener.asBinder().unlinkToDeath(mDeathRecipient, 0);
125 
126             for (ModuleWatcher watcher : mModuleWatchers) {
127                 watcher.close();
128             }
129             mModuleWatchers.clear();
130         }
131     }
132 }
133