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