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