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