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