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