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