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