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