• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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         for (Multicaster l : mMulticasters) {
121             pw.print("    ");
122             pw.println(l);
123         }
124     }
125 
enableVerboseLogging(boolean verboseEnabled)126     protected void enableVerboseLogging(boolean verboseEnabled) {
127         mVerboseLoggingEnabled = verboseEnabled;
128     }
129 
130     /** Start filtering if  no multicasters exist. */
initializeFiltering()131     public void initializeFiltering() {
132         synchronized (mMulticasters) {
133             // if anybody had requested filters be off, leave off
134             if (mMulticasters.size() == 0) {
135                 mActiveModeWarden.getPrimaryClientModeManager()
136                         .getMcastLockManagerFilterController()
137                         .startFilteringMulticastPackets();
138             }
139         }
140     }
141 
142     /**
143      * Acquire a multicast lock.
144      * @param binder a binder used to ensure caller is still alive
145      * @param tag string name of the caller.
146      */
acquireLock(IBinder binder, String tag)147     public void acquireLock(IBinder binder, String tag) {
148         synchronized (mMulticasters) {
149             mMulticastEnabled++;
150             mMulticasters.add(new Multicaster(tag, binder));
151             // Note that we could call stopFilteringMulticastPackets only when
152             // our new size == 1 (first call), but this function won't
153             // be called often and by making the stopPacket call each
154             // time we're less fragile and self-healing.
155             mActiveModeWarden.getPrimaryClientModeManager()
156                     .getMcastLockManagerFilterController()
157                     .stopFilteringMulticastPackets();
158         }
159 
160         int uid = Binder.getCallingUid();
161         final long ident = Binder.clearCallingIdentity();
162         mBatteryStats.reportWifiMulticastEnabled(new WorkSource(uid));
163         WifiStatsLog.write_non_chained(
164                 WifiStatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED, uid, null,
165                 WifiStatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED__STATE__ON, tag);
166         Binder.restoreCallingIdentity(ident);
167     }
168 
169     /** Releases a multicast lock */
releaseLock(String tag)170     public void releaseLock(String tag) {
171         int uid = Binder.getCallingUid();
172         synchronized (mMulticasters) {
173             mMulticastDisabled++;
174             int size = mMulticasters.size();
175             for (int i = size - 1; i >= 0; i--) {
176                 Multicaster m = mMulticasters.get(i);
177                 if ((m != null) && (m.getUid() == uid) && (m.getTag().equals(tag))) {
178                     removeMulticasterLocked(i, uid, tag);
179                     break;
180                 }
181             }
182         }
183     }
184 
removeMulticasterLocked(int i, int uid, String tag)185     private void removeMulticasterLocked(int i, int uid, String tag) {
186         Multicaster removed = mMulticasters.remove(i);
187 
188         if (removed != null) {
189             removed.unlinkDeathRecipient();
190         }
191         if (mMulticasters.size() == 0) {
192             mActiveModeWarden.getPrimaryClientModeManager()
193                     .getMcastLockManagerFilterController()
194                     .startFilteringMulticastPackets();
195         }
196 
197         final long ident = Binder.clearCallingIdentity();
198         mBatteryStats.reportWifiMulticastDisabled(new WorkSource(uid));
199         WifiStatsLog.write_non_chained(
200                 WifiStatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED, uid, null,
201                 WifiStatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED__STATE__OFF, tag);
202         Binder.restoreCallingIdentity(ident);
203     }
204 
205     /** Returns whether multicast should be allowed (filtering disabled). */
isMulticastEnabled()206     public boolean isMulticastEnabled() {
207         synchronized (mMulticasters) {
208             return mMulticasters.size() > 0;
209         }
210     }
211 
212     private class PrimaryClientModeManagerChangedCallback
213             implements ActiveModeWarden.PrimaryClientModeManagerChangedCallback {
214 
215         @Override
onChange( @ullable ConcreteClientModeManager prevPrimaryClientModeManager, @Nullable ConcreteClientModeManager newPrimaryClientModeManager)216         public void onChange(
217                 @Nullable ConcreteClientModeManager prevPrimaryClientModeManager,
218                 @Nullable ConcreteClientModeManager newPrimaryClientModeManager) {
219             if (prevPrimaryClientModeManager != null) {
220                 // no longer primary => start filtering out multicast packets
221                 prevPrimaryClientModeManager.getMcastLockManagerFilterController()
222                         .startFilteringMulticastPackets();
223             }
224             if (newPrimaryClientModeManager != null
225                     && isMulticastEnabled()) { // this call is synchronized
226                 // new primary and multicast enabled => stop filtering out multicast packets
227                 newPrimaryClientModeManager.getMcastLockManagerFilterController()
228                         .stopFilteringMulticastPackets();
229             }
230         }
231     }
232 }
233