Home | History | Annotate | Download | only in wifi
      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