| /* |
| * Copyright (C) 2016 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.server.wifi; |
| |
| import android.os.Binder; |
| import android.os.IBinder; |
| import android.os.RemoteException; |
| import android.util.Slog; |
| import android.util.StatsLog; |
| |
| import com.android.internal.app.IBatteryStats; |
| |
| import java.io.PrintWriter; |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| /** |
| * WifiMulticastLockManager tracks holders of multicast locks and |
| * triggers enabling and disabling of filtering. |
| * |
| * @hide |
| */ |
| public class WifiMulticastLockManager { |
| private static final String TAG = "WifiMulticastLockManager"; |
| private final List<Multicaster> mMulticasters = new ArrayList<>(); |
| private int mMulticastEnabled = 0; |
| private int mMulticastDisabled = 0; |
| private boolean mVerboseLoggingEnabled = false; |
| private final IBatteryStats mBatteryStats; |
| private final FilterController mFilterController; |
| |
| /** Delegate for handling state change events for multicast filtering. */ |
| public interface FilterController { |
| /** Called when multicast filtering should be enabled */ |
| void startFilteringMulticastPackets(); |
| |
| /** Called when multicast filtering should be disabled */ |
| void stopFilteringMulticastPackets(); |
| } |
| |
| public WifiMulticastLockManager(FilterController filterController, IBatteryStats batteryStats) { |
| mBatteryStats = batteryStats; |
| mFilterController = filterController; |
| } |
| |
| private class Multicaster implements IBinder.DeathRecipient { |
| String mTag; |
| int mUid; |
| IBinder mBinder; |
| |
| Multicaster(String tag, IBinder binder) { |
| mTag = tag; |
| mUid = Binder.getCallingUid(); |
| mBinder = binder; |
| try { |
| mBinder.linkToDeath(this, 0); |
| } catch (RemoteException e) { |
| binderDied(); |
| } |
| } |
| |
| @Override |
| public void binderDied() { |
| Slog.e(TAG, "Multicaster binderDied"); |
| synchronized (mMulticasters) { |
| int i = mMulticasters.indexOf(this); |
| if (i != -1) { |
| removeMulticasterLocked(i, mUid, mTag); |
| } |
| } |
| } |
| |
| void unlinkDeathRecipient() { |
| mBinder.unlinkToDeath(this, 0); |
| } |
| |
| public int getUid() { |
| return mUid; |
| } |
| |
| public String getTag() { |
| return mTag; |
| } |
| |
| public String toString() { |
| return "Multicaster{" + mTag + " uid=" + mUid + "}"; |
| } |
| } |
| |
| protected void dump(PrintWriter pw) { |
| pw.println("mMulticastEnabled " + mMulticastEnabled); |
| pw.println("mMulticastDisabled " + mMulticastDisabled); |
| pw.println("Multicast Locks held:"); |
| for (Multicaster l : mMulticasters) { |
| pw.print(" "); |
| pw.println(l); |
| } |
| } |
| |
| protected void enableVerboseLogging(int verbose) { |
| if (verbose > 0) { |
| mVerboseLoggingEnabled = true; |
| } else { |
| mVerboseLoggingEnabled = false; |
| } |
| } |
| |
| /** Start filtering if no multicasters exist. */ |
| public void initializeFiltering() { |
| synchronized (mMulticasters) { |
| // if anybody had requested filters be off, leave off |
| if (mMulticasters.size() != 0) { |
| return; |
| } else { |
| mFilterController.startFilteringMulticastPackets(); |
| } |
| } |
| } |
| |
| /** |
| * Acquire a multicast lock. |
| * @param binder a binder used to ensure caller is still alive |
| * @param tag string name of the caller. |
| */ |
| public void acquireLock(IBinder binder, String tag) { |
| synchronized (mMulticasters) { |
| mMulticastEnabled++; |
| mMulticasters.add(new Multicaster(tag, binder)); |
| // Note that we could call stopFilteringMulticastPackets only when |
| // our new size == 1 (first call), but this function won't |
| // be called often and by making the stopPacket call each |
| // time we're less fragile and self-healing. |
| mFilterController.stopFilteringMulticastPackets(); |
| } |
| |
| int uid = Binder.getCallingUid(); |
| final long ident = Binder.clearCallingIdentity(); |
| try { |
| mBatteryStats.noteWifiMulticastEnabled(uid); |
| StatsLog.write_non_chained( |
| StatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED, uid, null, |
| StatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED__STATE__ON, tag); |
| } catch (RemoteException e) { |
| } finally { |
| Binder.restoreCallingIdentity(ident); |
| } |
| } |
| |
| /** Releases a multicast lock */ |
| public void releaseLock(String tag) { |
| int uid = Binder.getCallingUid(); |
| synchronized (mMulticasters) { |
| mMulticastDisabled++; |
| int size = mMulticasters.size(); |
| for (int i = size - 1; i >= 0; i--) { |
| Multicaster m = mMulticasters.get(i); |
| if ((m != null) && (m.getUid() == uid) && (m.getTag().equals(tag))) { |
| removeMulticasterLocked(i, uid, tag); |
| break; |
| } |
| } |
| } |
| } |
| |
| private void removeMulticasterLocked(int i, int uid, String tag) { |
| Multicaster removed = mMulticasters.remove(i); |
| |
| if (removed != null) { |
| removed.unlinkDeathRecipient(); |
| } |
| if (mMulticasters.size() == 0) { |
| mFilterController.startFilteringMulticastPackets(); |
| } |
| |
| final long ident = Binder.clearCallingIdentity(); |
| try { |
| mBatteryStats.noteWifiMulticastDisabled(uid); |
| StatsLog.write_non_chained( |
| StatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED, uid, null, |
| StatsLog.WIFI_MULTICAST_LOCK_STATE_CHANGED__STATE__OFF, tag); |
| } catch (RemoteException e) { |
| } finally { |
| Binder.restoreCallingIdentity(ident); |
| } |
| } |
| |
| /** Returns whether multicast should be allowed (filterning disabled). */ |
| public boolean isMulticastEnabled() { |
| synchronized (mMulticasters) { |
| return (mMulticasters.size() > 0); |
| } |
| } |
| } |