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.content.Context; 20 import android.net.wifi.WifiManager; 21 import android.os.Binder; 22 import android.os.IBinder; 23 import android.os.RemoteException; 24 import android.os.WorkSource; 25 import android.util.Slog; 26 27 import com.android.internal.app.IBatteryStats; 28 29 import java.io.PrintWriter; 30 import java.util.ArrayList; 31 import java.util.List; 32 33 /** 34 * WifiLockManager maintains the list of wake locks held by different applications. 35 */ 36 public class WifiLockManager { 37 private static final String TAG = "WifiLockManager"; 38 private boolean mVerboseLoggingEnabled = false; 39 40 private final Context mContext; 41 private final IBatteryStats mBatteryStats; 42 43 private final List<WifiLock> mWifiLocks = new ArrayList<>(); 44 // some wifi lock statistics 45 private int mFullHighPerfLocksAcquired; 46 private int mFullHighPerfLocksReleased; 47 private int mFullLocksAcquired; 48 private int mFullLocksReleased; 49 private int mScanLocksAcquired; 50 private int mScanLocksReleased; 51 52 WifiLockManager(Context context, IBatteryStats batteryStats) { 53 mContext = context; 54 mBatteryStats = batteryStats; 55 } 56 57 /** 58 * Method allowing a calling app to acquire a Wifi WakeLock in the supplied mode. 59 * 60 * This method verifies that the caller has permission to make the call and that the lock mode 61 * is a valid WifiLock mode. 62 * @param lockMode int representation of the Wifi WakeLock type. 63 * @param tag String passed to WifiManager.WifiLock 64 * @param binder IBinder for the calling app 65 * @param ws WorkSource of the calling app 66 * 67 * @return true if the lock was successfully acquired, false if the lockMode was invalid. 68 */ 69 public boolean acquireWifiLock(int lockMode, String tag, IBinder binder, WorkSource ws) { 70 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null); 71 if (!isValidLockMode(lockMode)) { 72 throw new IllegalArgumentException("lockMode =" + lockMode); 73 } 74 if (ws == null || ws.size() == 0) { 75 ws = new WorkSource(Binder.getCallingUid()); 76 } else { 77 mContext.enforceCallingOrSelfPermission( 78 android.Manifest.permission.UPDATE_DEVICE_STATS, null); 79 } 80 return addLock(new WifiLock(lockMode, tag, binder, ws)); 81 } 82 83 /** 84 * Method used by applications to release a WiFi Wake lock. This method checks permissions for 85 * the caller and if allowed, releases the underlying WifiLock(s). 86 * 87 * @param binder IBinder for the calling app. 88 * @return true if the lock was released, false if the caller did not hold any locks 89 */ 90 public boolean releaseWifiLock(IBinder binder) { 91 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null); 92 return releaseLock(binder); 93 } 94 95 /** 96 * Method used to get the strongest lock type currently held by the WifiLockManager. 97 * 98 * If no locks are held, WifiManager.WIFI_MODE_NO_LOCKS_HELD is returned. 99 * 100 * @return int representing the currently held (highest power consumption) lock. 101 */ 102 public synchronized int getStrongestLockMode() { 103 if (mWifiLocks.isEmpty()) { 104 return WifiManager.WIFI_MODE_NO_LOCKS_HELD; 105 } 106 107 if (mFullHighPerfLocksAcquired > mFullHighPerfLocksReleased) { 108 return WifiManager.WIFI_MODE_FULL_HIGH_PERF; 109 } 110 111 if (mFullLocksAcquired > mFullLocksReleased) { 112 return WifiManager.WIFI_MODE_FULL; 113 } 114 115 return WifiManager.WIFI_MODE_SCAN_ONLY; 116 } 117 118 /** 119 * Method to create a WorkSource containing all active WifiLock WorkSources. 120 */ 121 public synchronized WorkSource createMergedWorkSource() { 122 WorkSource mergedWS = new WorkSource(); 123 for (WifiLock lock : mWifiLocks) { 124 mergedWS.add(lock.getWorkSource()); 125 } 126 return mergedWS; 127 } 128 129 /** 130 * Method used to update WifiLocks with a new WorkSouce. 131 * 132 * @param binder IBinder for the calling application. 133 * @param ws WorkSource to add to the existing WifiLock(s). 134 */ 135 public synchronized void updateWifiLockWorkSource(IBinder binder, WorkSource ws) { 136 // Does the caller have permission to make this call? 137 mContext.enforceCallingOrSelfPermission( 138 android.Manifest.permission.UPDATE_DEVICE_STATS, null); 139 140 // Now check if there is an active lock 141 WifiLock wl = findLockByBinder(binder); 142 if (wl == null) { 143 throw new IllegalArgumentException("Wifi lock not active"); 144 } 145 146 WorkSource newWorkSource; 147 if (ws == null || ws.size() == 0) { 148 newWorkSource = new WorkSource(Binder.getCallingUid()); 149 } else { 150 // Make a copy of the WorkSource before adding it to the WakeLock 151 newWorkSource = new WorkSource(ws); 152 } 153 154 long ident = Binder.clearCallingIdentity(); 155 try { 156 mBatteryStats.noteFullWifiLockReleasedFromSource(wl.mWorkSource); 157 wl.mWorkSource = newWorkSource; 158 mBatteryStats.noteFullWifiLockAcquiredFromSource(wl.mWorkSource); 159 } catch (RemoteException e) { 160 } finally { 161 Binder.restoreCallingIdentity(ident); 162 } 163 } 164 165 private static boolean isValidLockMode(int lockMode) { 166 if (lockMode != WifiManager.WIFI_MODE_FULL 167 && lockMode != WifiManager.WIFI_MODE_SCAN_ONLY 168 && lockMode != WifiManager.WIFI_MODE_FULL_HIGH_PERF) { 169 return false; 170 } 171 return true; 172 } 173 174 private synchronized boolean addLock(WifiLock lock) { 175 if (mVerboseLoggingEnabled) { 176 Slog.d(TAG, "addLock: " + lock); 177 } 178 179 if (findLockByBinder(lock.getBinder()) != null) { 180 if (mVerboseLoggingEnabled) { 181 Slog.d(TAG, "attempted to add a lock when already holding one"); 182 } 183 return false; 184 } 185 186 mWifiLocks.add(lock); 187 188 boolean lockAdded = false; 189 long ident = Binder.clearCallingIdentity(); 190 try { 191 mBatteryStats.noteFullWifiLockAcquiredFromSource(lock.mWorkSource); 192 switch(lock.mMode) { 193 case WifiManager.WIFI_MODE_FULL: 194 ++mFullLocksAcquired; 195 break; 196 case WifiManager.WIFI_MODE_FULL_HIGH_PERF: 197 ++mFullHighPerfLocksAcquired; 198 break; 199 case WifiManager.WIFI_MODE_SCAN_ONLY: 200 ++mScanLocksAcquired; 201 break; 202 } 203 lockAdded = true; 204 } catch (RemoteException e) { 205 } finally { 206 Binder.restoreCallingIdentity(ident); 207 } 208 return lockAdded; 209 } 210 211 private synchronized WifiLock removeLock(IBinder binder) { 212 WifiLock lock = findLockByBinder(binder); 213 if (lock != null) { 214 mWifiLocks.remove(lock); 215 lock.unlinkDeathRecipient(); 216 } 217 return lock; 218 } 219 220 private synchronized boolean releaseLock(IBinder binder) { 221 WifiLock wifiLock = removeLock(binder); 222 if (wifiLock == null) { 223 // attempting to release a lock that is not active. 224 return false; 225 } 226 227 if (mVerboseLoggingEnabled) { 228 Slog.d(TAG, "releaseLock: " + wifiLock); 229 } 230 231 long ident = Binder.clearCallingIdentity(); 232 try { 233 mBatteryStats.noteFullWifiLockReleasedFromSource(wifiLock.mWorkSource); 234 switch(wifiLock.mMode) { 235 case WifiManager.WIFI_MODE_FULL: 236 ++mFullLocksReleased; 237 break; 238 case WifiManager.WIFI_MODE_FULL_HIGH_PERF: 239 ++mFullHighPerfLocksReleased; 240 break; 241 case WifiManager.WIFI_MODE_SCAN_ONLY: 242 ++mScanLocksReleased; 243 break; 244 } 245 } catch (RemoteException e) { 246 } finally { 247 Binder.restoreCallingIdentity(ident); 248 } 249 return true; 250 } 251 252 253 private synchronized WifiLock findLockByBinder(IBinder binder) { 254 for (WifiLock lock : mWifiLocks) { 255 if (lock.getBinder() == binder) { 256 return lock; 257 } 258 } 259 return null; 260 } 261 262 protected void dump(PrintWriter pw) { 263 pw.println("Locks acquired: " + mFullLocksAcquired + " full, " 264 + mFullHighPerfLocksAcquired + " full high perf, " 265 + mScanLocksAcquired + " scan"); 266 pw.println("Locks released: " + mFullLocksReleased + " full, " 267 + mFullHighPerfLocksReleased + " full high perf, " 268 + mScanLocksReleased + " scan"); 269 pw.println(); 270 pw.println("Locks held:"); 271 for (WifiLock lock : mWifiLocks) { 272 pw.print(" "); 273 pw.println(lock); 274 } 275 } 276 277 protected void enableVerboseLogging(int verbose) { 278 if (verbose > 0) { 279 mVerboseLoggingEnabled = true; 280 } else { 281 mVerboseLoggingEnabled = false; 282 } 283 } 284 285 private class WifiLock implements IBinder.DeathRecipient { 286 String mTag; 287 int mUid; 288 IBinder mBinder; 289 int mMode; 290 WorkSource mWorkSource; 291 292 WifiLock(int lockMode, String tag, IBinder binder, WorkSource ws) { 293 mTag = tag; 294 mBinder = binder; 295 mUid = Binder.getCallingUid(); 296 mMode = lockMode; 297 mWorkSource = ws; 298 try { 299 mBinder.linkToDeath(this, 0); 300 } catch (RemoteException e) { 301 binderDied(); 302 } 303 } 304 305 protected WorkSource getWorkSource() { 306 return mWorkSource; 307 } 308 309 protected int getUid() { 310 return mUid; 311 } 312 313 protected IBinder getBinder() { 314 return mBinder; 315 } 316 317 public void binderDied() { 318 releaseLock(mBinder); 319 } 320 321 public void unlinkDeathRecipient() { 322 mBinder.unlinkToDeath(this, 0); 323 } 324 325 public String toString() { 326 return "WifiLock{" + this.mTag + " type=" + this.mMode + " uid=" + mUid + "}"; 327 } 328 } 329 } 330