1 /*
2  * Copyright (C) 2016 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.wifi;
18 
19 import android.annotation.Nullable;
20 import android.os.BatteryStatsManager;
21 import android.os.Binder;
22 import android.os.Handler;
23 import android.os.IBinder;
24 import android.os.Looper;
25 import android.os.RemoteException;
26 import android.os.WorkSource;
27 import android.util.Log;
28 
29 import com.android.server.wifi.proto.WifiStatsLog;
30 
31 import java.io.PrintWriter;
32 import java.util.ArrayList;
33 import java.util.List;
34 
35 /**
36  * WifiMulticastLockManager tracks holders of multicast locks and
37  * triggers enabling and disabling of filtering.
38  */
39 public class WifiMulticastLockManager {
40     private static final String TAG = "WifiMulticastLockManager";
41     private final List<Multicaster> mMulticasters = new ArrayList<>();
42     private int mMulticastEnabled = 0;
43     private int mMulticastDisabled = 0;
44     private final Handler mHandler;
45     private boolean mVerboseLoggingEnabled = false;
46     private final BatteryStatsManager mBatteryStats;
47     private final ActiveModeWarden mActiveModeWarden;
48 
49     /** Delegate for handling state change events for multicast filtering. */
50     public interface FilterController {
51         /** Called when multicast filtering should be enabled */
startFilteringMulticastPackets()52         void startFilteringMulticastPackets();
53 
54         /** Called when multicast filtering should be disabled */
stopFilteringMulticastPackets()55         void stopFilteringMulticastPackets();
56     }
57 
WifiMulticastLockManager( ActiveModeWarden activeModeWarden, BatteryStatsManager batteryStats, Looper looper)58     public WifiMulticastLockManager(
59             ActiveModeWarden activeModeWarden,
60             BatteryStatsManager batteryStats,
61             Looper looper) {
62         mBatteryStats = batteryStats;
63         mActiveModeWarden = activeModeWarden;
64         mHandler = new Handler(looper);
65 
66         mActiveModeWarden.registerPrimaryClientModeManagerChangedCallback(
67                 new PrimaryClientModeManagerChangedCallback());
68     }
69 
70     private class Multicaster implements IBinder.DeathRecipient {
71         String mTag;
72         int mUid;
73         IBinder mBinder;
74 
Multicaster(String tag, IBinder binder)75         Multicaster(String tag, IBinder binder) {
76             mTag = tag;
77             mUid = Binder.getCallingUid();
78             mBinder = binder;
79             try {
80                 mBinder.linkToDeath(this, 0);
81             } catch (RemoteException e) {
82                 binderDied();
83             }
84         }
85 
86         @Override
binderDied()87         public void binderDied() {
88             mHandler.post(() -> {
89                 Log.e(TAG, "Multicaster binderDied");
90                 synchronized (mMulticasters) {
91                     int i = mMulticasters.indexOf(this);
92                     if (i != -1) {
93                         removeMulticasterLocked(i, mUid, mTag);
94                     }
95                 }
96             });
97         }
98 
unlinkDeathRecipient()99         void unlinkDeathRecipient() {
100             mBinder.unlinkToDeath(this, 0);
101         }
102 
getUid()103         public int getUid() {
104             return mUid;
105         }
106 
getTag()107         public String getTag() {
108             return mTag;
109         }
110 
toString()111         public String toString() {
112             return "Multicaster{" + mTag + " uid=" + mUid  + "}";
113         }
114     }
115 
dump(PrintWriter pw)116     protected void dump(PrintWriter pw) {
117         pw.println("mMulticastEnabled " + mMulticastEnabled);
118         pw.println("mMulticastDisabled " + mMulticastDisabled);
119         pw.println("Multicast Locks held:");
120         synchronized (mMulticasters) {
121             for (Multicaster l : mMulticasters) {
122                 pw.print("    ");
123                 pw.println(l);
124             }
125         }
126     }
127 
enableVerboseLogging(boolean verboseEnabled)128     protected void enableVerboseLogging(boolean verboseEnabled) {
129         mVerboseLoggingEnabled = verboseEnabled;
130     }
131 
132     /** Start filtering if  no multicasters exist. */
initializeFiltering()133     public void initializeFiltering() {
134         synchronized (mMulticasters) {
135             // if anybody had requested filters be off, leave off
136             if (mMulticasters.size() == 0) {
137                 mActiveModeWarden.getPrimaryClientModeManager()
138                         .getMcastLockManagerFilterController()
139                         .startFilteringMulticastPackets();
140             }
141         }
142     }
143 
144     /**
145      * Acquire a multicast lock.
146      * @param binder a binder used to ensure caller is still alive
147      * @param tag string name of the caller.
148      */
acquireLock(IBinder binder, String tag)149     public void acquireLock(IBinder binder, String tag) {
150         synchronized (mMulticasters) {
151             mMulticastEnabled++;
152             mMulticasters.add(new Multicaster(tag, binder));
153             // Note that we could call stopFilteringMulticastPackets only when
154             // our new size == 1 (first call), but this function won't
155             // be called often and by making the stopPacket call each
156             // time we're less fragile and self-healing.
157             mActiveModeWarden.getPrimaryClientModeManager()
158                     .getMcastLockManagerFilterController()
159                     .stopFilteringMulticastPackets();
160         }
161 
162         int uid = Binder.getCallingUid();
163         final long ident = Binder.clearCallingIdentity();
164         mBatteryStats.reportWifiMulticastEnabled(new WorkSource(uid));
165         WifiStatsLog.write_non_chained(
166                 WifiStatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED, uid, null,
167                 WifiStatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED__STATE__ON, tag);
168         Binder.restoreCallingIdentity(ident);
169     }
170 
171     /** Releases a multicast lock */
releaseLock(String tag)172     public void releaseLock(String tag) {
173         int uid = Binder.getCallingUid();
174         synchronized (mMulticasters) {
175             mMulticastDisabled++;
176             int size = mMulticasters.size();
177             for (int i = size - 1; i >= 0; i--) {
178                 Multicaster m = mMulticasters.get(i);
179                 if ((m != null) && (m.getUid() == uid) && (m.getTag().equals(tag))) {
180                     removeMulticasterLocked(i, uid, tag);
181                     break;
182                 }
183             }
184         }
185     }
186 
removeMulticasterLocked(int i, int uid, String tag)187     private void removeMulticasterLocked(int i, int uid, String tag) {
188         Multicaster removed = mMulticasters.remove(i);
189 
190         if (removed != null) {
191             removed.unlinkDeathRecipient();
192         }
193         if (mMulticasters.size() == 0) {
194             mActiveModeWarden.getPrimaryClientModeManager()
195                     .getMcastLockManagerFilterController()
196                     .startFilteringMulticastPackets();
197         }
198 
199         final long ident = Binder.clearCallingIdentity();
200         mBatteryStats.reportWifiMulticastDisabled(new WorkSource(uid));
201         WifiStatsLog.write_non_chained(
202                 WifiStatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED, uid, null,
203                 WifiStatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED__STATE__OFF, tag);
204         Binder.restoreCallingIdentity(ident);
205     }
206 
207     /** Returns whether multicast should be allowed (filtering disabled). */
isMulticastEnabled()208     public boolean isMulticastEnabled() {
209         synchronized (mMulticasters) {
210             return mMulticasters.size() > 0;
211         }
212     }
213 
214     private class PrimaryClientModeManagerChangedCallback
215             implements ActiveModeWarden.PrimaryClientModeManagerChangedCallback {
216 
217         @Override
onChange( @ullable ConcreteClientModeManager prevPrimaryClientModeManager, @Nullable ConcreteClientModeManager newPrimaryClientModeManager)218         public void onChange(
219                 @Nullable ConcreteClientModeManager prevPrimaryClientModeManager,
220                 @Nullable ConcreteClientModeManager newPrimaryClientModeManager) {
221             if (prevPrimaryClientModeManager != null) {
222                 // no longer primary => start filtering out multicast packets
223                 prevPrimaryClientModeManager.getMcastLockManagerFilterController()
224                         .startFilteringMulticastPackets();
225             }
226             if (newPrimaryClientModeManager != null
227                     && isMulticastEnabled()) { // this call is synchronized
228                 // new primary and multicast enabled => stop filtering out multicast packets
229                 newPrimaryClientModeManager.getMcastLockManagerFilterController()
230                         .stopFilteringMulticastPackets();
231             }
232         }
233     }
234 }
235