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.app.ActivityManager; 20 import android.content.Context; 21 import android.net.wifi.WifiManager; 22 import android.os.BatteryStatsManager; 23 import android.os.Binder; 24 import android.os.Handler; 25 import android.os.IBinder; 26 import android.os.RemoteException; 27 import android.os.WorkSource; 28 import android.os.WorkSource.WorkChain; 29 import android.util.Log; 30 import android.util.Pair; 31 import android.util.SparseArray; 32 33 import com.android.server.wifi.proto.WifiStatsLog; 34 import com.android.server.wifi.util.WorkSourceUtil; 35 36 import java.io.PrintWriter; 37 import java.util.ArrayList; 38 import java.util.List; 39 import java.util.NoSuchElementException; 40 41 /** 42 * WifiLockManager maintains the list of wake locks held by different applications. 43 */ 44 public class WifiLockManager { 45 private static final String TAG = "WifiLockManager"; 46 47 private static final int LOW_LATENCY_SUPPORT_UNDEFINED = -1; 48 private static final int LOW_LATENCY_NOT_SUPPORTED = 0; 49 private static final int LOW_LATENCY_SUPPORTED = 1; 50 51 private static final int IGNORE_SCREEN_STATE_MASK = 0x01; 52 private static final int IGNORE_WIFI_STATE_MASK = 0x02; 53 54 private int mLatencyModeSupport = LOW_LATENCY_SUPPORT_UNDEFINED; 55 56 private boolean mVerboseLoggingEnabled = false; 57 58 private final Clock mClock; 59 private final Context mContext; 60 private final BatteryStatsManager mBatteryStats; 61 private final FrameworkFacade mFrameworkFacade; 62 private final ClientModeImpl mClientModeImpl; 63 private final ActivityManager mActivityManager; 64 private final Handler mHandler; 65 private final WifiMetrics mWifiMetrics; 66 private final WifiNative mWifiNative; 67 68 private final List<WifiLock> mWifiLocks = new ArrayList<>(); 69 // map UIDs to their corresponding records (for low-latency locks) 70 private final SparseArray<UidRec> mLowLatencyUidWatchList = new SparseArray<>(); 71 private int mCurrentOpMode; 72 private boolean mScreenOn = false; 73 private boolean mWifiConnected = false; 74 75 // For shell command support 76 private boolean mForceHiPerfMode = false; 77 private boolean mForceLowLatencyMode = false; 78 79 // some wifi lock statistics 80 private int mFullHighPerfLocksAcquired; 81 private int mFullHighPerfLocksReleased; 82 private int mFullLowLatencyLocksAcquired; 83 private int mFullLowLatencyLocksReleased; 84 private long mCurrentSessionStartTimeMs; 85 WifiLockManager(Context context, BatteryStatsManager batteryStats, ClientModeImpl clientModeImpl, FrameworkFacade frameworkFacade, Handler handler, WifiNative wifiNative, Clock clock, WifiMetrics wifiMetrics)86 WifiLockManager(Context context, BatteryStatsManager batteryStats, 87 ClientModeImpl clientModeImpl, FrameworkFacade frameworkFacade, Handler handler, 88 WifiNative wifiNative, Clock clock, WifiMetrics wifiMetrics) { 89 mContext = context; 90 mBatteryStats = batteryStats; 91 mClientModeImpl = clientModeImpl; 92 mFrameworkFacade = frameworkFacade; 93 mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE); 94 mCurrentOpMode = WifiManager.WIFI_MODE_NO_LOCKS_HELD; 95 mWifiNative = wifiNative; 96 mHandler = handler; 97 mClock = clock; 98 mWifiMetrics = wifiMetrics; 99 100 // Register for UID fg/bg transitions 101 registerUidImportanceTransitions(); 102 } 103 104 // Check for conditions to activate high-perf lock canActivateHighPerfLock(int ignoreMask)105 private boolean canActivateHighPerfLock(int ignoreMask) { 106 boolean check = true; 107 108 // Only condition is when Wifi is connected 109 if ((ignoreMask & IGNORE_WIFI_STATE_MASK) == 0) { 110 check = check && mWifiConnected; 111 } 112 113 return check; 114 } 115 canActivateHighPerfLock()116 private boolean canActivateHighPerfLock() { 117 return canActivateHighPerfLock(0); 118 } 119 120 // Check for conditions to activate low-latency lock canActivateLowLatencyLock(int ignoreMask, UidRec uidRec)121 private boolean canActivateLowLatencyLock(int ignoreMask, UidRec uidRec) { 122 boolean check = true; 123 124 if ((ignoreMask & IGNORE_WIFI_STATE_MASK) == 0) { 125 check = check && mWifiConnected; 126 } 127 if ((ignoreMask & IGNORE_SCREEN_STATE_MASK) == 0) { 128 check = check && mScreenOn; 129 } 130 if (uidRec != null) { 131 check = check && uidRec.mIsFg; 132 } 133 134 return check; 135 } 136 canActivateLowLatencyLock(int ignoreMask)137 private boolean canActivateLowLatencyLock(int ignoreMask) { 138 return canActivateLowLatencyLock(ignoreMask, null); 139 } 140 canActivateLowLatencyLock()141 private boolean canActivateLowLatencyLock() { 142 return canActivateLowLatencyLock(0, null); 143 } 144 145 // Detect UIDs going foreground/background registerUidImportanceTransitions()146 private void registerUidImportanceTransitions() { 147 mActivityManager.addOnUidImportanceListener(new ActivityManager.OnUidImportanceListener() { 148 @Override 149 public void onUidImportance(final int uid, final int importance) { 150 mHandler.post(() -> { 151 UidRec uidRec = mLowLatencyUidWatchList.get(uid); 152 if (uidRec == null) { 153 // Not a uid in the watch list 154 return; 155 } 156 157 boolean newModeIsFg = (importance 158 == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND); 159 if (uidRec.mIsFg == newModeIsFg) { 160 return; // already at correct state 161 } 162 163 uidRec.mIsFg = newModeIsFg; 164 updateOpMode(); 165 166 // If conditions for lock activation are met, 167 // then UID either share the blame, or removed from sharing 168 // whether to start or stop the blame based on UID fg/bg state 169 if (canActivateLowLatencyLock()) { 170 setBlameLowLatencyUid(uid, uidRec.mIsFg); 171 } 172 }); 173 } 174 }, ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE); 175 } 176 177 /** 178 * Method allowing a calling app to acquire a Wifi WakeLock in the supplied mode. 179 * 180 * This method checks that the lock mode is a valid WifiLock mode. 181 * @param lockMode int representation of the Wifi WakeLock type. 182 * @param tag String passed to WifiManager.WifiLock 183 * @param binder IBinder for the calling app 184 * @param ws WorkSource of the calling app 185 * 186 * @return true if the lock was successfully acquired, false if the lockMode was invalid. 187 */ acquireWifiLock(int lockMode, String tag, IBinder binder, WorkSource ws)188 public boolean acquireWifiLock(int lockMode, String tag, IBinder binder, WorkSource ws) { 189 // Make a copy of the WorkSource before adding it to the WakeLock 190 // This is to make sure worksource value can not be changed by caller 191 // after function returns. 192 WorkSource newWorkSource = new WorkSource(ws); 193 return addLock(new WifiLock(lockMode, tag, binder, newWorkSource)); 194 } 195 196 /** 197 * Method used by applications to release a WiFi Wake lock. 198 * 199 * @param binder IBinder for the calling app. 200 * @return true if the lock was released, false if the caller did not hold any locks 201 */ releaseWifiLock(IBinder binder)202 public boolean releaseWifiLock(IBinder binder) { 203 return releaseLock(binder); 204 } 205 206 /** 207 * Method used to get the strongest lock type currently held by the WifiLockManager. 208 * 209 * If no locks are held, WifiManager.WIFI_MODE_NO_LOCKS_HELD is returned. 210 * 211 * @return int representing the currently held (highest power consumption) lock. 212 */ getStrongestLockMode()213 public synchronized int getStrongestLockMode() { 214 // If Wifi Client is not connected, then all locks are not effective 215 if (!mWifiConnected) { 216 return WifiManager.WIFI_MODE_NO_LOCKS_HELD; 217 } 218 219 // Check if mode is forced to hi-perf 220 if (mForceHiPerfMode) { 221 return WifiManager.WIFI_MODE_FULL_HIGH_PERF; 222 } 223 224 // Check if mode is forced to low-latency 225 if (mForceLowLatencyMode) { 226 return WifiManager.WIFI_MODE_FULL_LOW_LATENCY; 227 } 228 229 if (mScreenOn && countFgLowLatencyUids() > 0) { 230 return WifiManager.WIFI_MODE_FULL_LOW_LATENCY; 231 } 232 233 if (mFullHighPerfLocksAcquired > mFullHighPerfLocksReleased) { 234 return WifiManager.WIFI_MODE_FULL_HIGH_PERF; 235 } 236 237 return WifiManager.WIFI_MODE_NO_LOCKS_HELD; 238 } 239 240 /** 241 * Method to create a WorkSource containing all active WifiLock WorkSources. 242 */ createMergedWorkSource()243 public synchronized WorkSource createMergedWorkSource() { 244 WorkSource mergedWS = new WorkSource(); 245 for (WifiLock lock : mWifiLocks) { 246 mergedWS.add(lock.getWorkSource()); 247 } 248 return mergedWS; 249 } 250 251 /** 252 * Method used to update WifiLocks with a new WorkSouce. 253 * 254 * @param binder IBinder for the calling application. 255 * @param ws WorkSource to add to the existing WifiLock(s). 256 */ updateWifiLockWorkSource(IBinder binder, WorkSource ws)257 public synchronized void updateWifiLockWorkSource(IBinder binder, WorkSource ws) { 258 259 // Now check if there is an active lock 260 WifiLock wl = findLockByBinder(binder); 261 if (wl == null) { 262 throw new IllegalArgumentException("Wifi lock not active"); 263 } 264 265 // Make a copy of the WorkSource before adding it to the WakeLock 266 // This is to make sure worksource value can not be changed by caller 267 // after function returns. 268 WorkSource newWorkSource = new WorkSource(ws); 269 270 if (mVerboseLoggingEnabled) { 271 Log.d(TAG, "updateWifiLockWakeSource: " + wl + ", newWorkSource=" + newWorkSource); 272 } 273 274 // Note: 275 // Log the acquire before the release to avoid "holes" in the collected data due to 276 // an acquire event immediately after a release in the case where newWorkSource and 277 // wl.mWorkSource share one or more attribution UIDs. Both batteryStats and statsd 278 // can correctly match "nested" acquire / release pairs. 279 switch(wl.mMode) { 280 case WifiManager.WIFI_MODE_FULL_HIGH_PERF: 281 // Shift blame to new worksource if needed 282 if (canActivateHighPerfLock()) { 283 setBlameHiPerfWs(newWorkSource, true); 284 setBlameHiPerfWs(wl.mWorkSource, false); 285 } 286 break; 287 case WifiManager.WIFI_MODE_FULL_LOW_LATENCY: 288 addWsToLlWatchList(newWorkSource); 289 removeWsFromLlWatchList(wl.mWorkSource); 290 updateOpMode(); 291 break; 292 default: 293 // Do nothing 294 break; 295 } 296 297 wl.mWorkSource = newWorkSource; 298 } 299 300 /** 301 * Method Used for shell command support 302 * 303 * @param isEnabled True to force hi-perf mode, false to leave it up to acquired wifiLocks. 304 * @return True for success, false for failure (failure turns forcing mode off) 305 */ forceHiPerfMode(boolean isEnabled)306 public boolean forceHiPerfMode(boolean isEnabled) { 307 mForceHiPerfMode = isEnabled; 308 mForceLowLatencyMode = false; 309 if (!updateOpMode()) { 310 Log.e(TAG, "Failed to force hi-perf mode, returning to normal mode"); 311 mForceHiPerfMode = false; 312 return false; 313 } 314 return true; 315 } 316 317 /** 318 * Method Used for shell command support 319 * 320 * @param isEnabled True to force low-latency mode, false to leave it up to acquired wifiLocks. 321 * @return True for success, false for failure (failure turns forcing mode off) 322 */ forceLowLatencyMode(boolean isEnabled)323 public boolean forceLowLatencyMode(boolean isEnabled) { 324 mForceLowLatencyMode = isEnabled; 325 mForceHiPerfMode = false; 326 if (!updateOpMode()) { 327 Log.e(TAG, "Failed to force low-latency mode, returning to normal mode"); 328 mForceLowLatencyMode = false; 329 return false; 330 } 331 return true; 332 } 333 334 /** 335 * Handler for screen state (on/off) changes 336 */ handleScreenStateChanged(boolean screenOn)337 public void handleScreenStateChanged(boolean screenOn) { 338 if (mVerboseLoggingEnabled) { 339 Log.d(TAG, "handleScreenStateChanged: screenOn = " + screenOn); 340 } 341 342 mScreenOn = screenOn; 343 344 if (canActivateLowLatencyLock(IGNORE_SCREEN_STATE_MASK)) { 345 // Update the running mode 346 updateOpMode(); 347 // Adjust blaming for UIDs in foreground 348 setBlameLowLatencyWatchList(screenOn); 349 } 350 } 351 352 /** 353 * Handler for Wifi Client mode state changes 354 */ updateWifiClientConnected(boolean isConnected)355 public void updateWifiClientConnected(boolean isConnected) { 356 if (mWifiConnected == isConnected) { 357 // No need to take action 358 return; 359 } 360 mWifiConnected = isConnected; 361 362 // Adjust blaming for UIDs in foreground carrying low latency locks 363 if (canActivateLowLatencyLock(IGNORE_WIFI_STATE_MASK)) { 364 setBlameLowLatencyWatchList(mWifiConnected); 365 } 366 367 // Adjust blaming for UIDs carrying high perf locks 368 // Note that blaming is adjusted only if needed, 369 // since calling this API is reference counted 370 if (canActivateHighPerfLock(IGNORE_WIFI_STATE_MASK)) { 371 setBlameHiPerfLocks(mWifiConnected); 372 } 373 374 updateOpMode(); 375 } 376 setBlameHiPerfLocks(boolean shouldBlame)377 private void setBlameHiPerfLocks(boolean shouldBlame) { 378 for (WifiLock lock : mWifiLocks) { 379 if (lock.mMode == WifiManager.WIFI_MODE_FULL_HIGH_PERF) { 380 setBlameHiPerfWs(lock.getWorkSource(), shouldBlame); 381 } 382 } 383 } 384 385 /** 386 * Validate that the lock mode is valid - i.e. one of the supported enumerations. 387 * 388 * @param lockMode The lock mode to verify. 389 * @return true for valid lock modes, false otherwise. 390 */ isValidLockMode(int lockMode)391 public static boolean isValidLockMode(int lockMode) { 392 if (lockMode != WifiManager.WIFI_MODE_FULL 393 && lockMode != WifiManager.WIFI_MODE_SCAN_ONLY 394 && lockMode != WifiManager.WIFI_MODE_FULL_HIGH_PERF 395 && lockMode != WifiManager.WIFI_MODE_FULL_LOW_LATENCY) { 396 return false; 397 } 398 return true; 399 } 400 addUidToLlWatchList(int uid)401 private void addUidToLlWatchList(int uid) { 402 UidRec uidRec = mLowLatencyUidWatchList.get(uid); 403 if (uidRec != null) { 404 uidRec.mLockCount++; 405 } else { 406 uidRec = new UidRec(uid); 407 uidRec.mLockCount = 1; 408 mLowLatencyUidWatchList.put(uid, uidRec); 409 410 // Now check if the uid is running in foreground 411 if (mFrameworkFacade.isAppForeground(mContext, uid)) { 412 uidRec.mIsFg = true; 413 } 414 415 if (canActivateLowLatencyLock(0, uidRec)) { 416 // Share the blame for this uid 417 setBlameLowLatencyUid(uid, true); 418 } 419 } 420 } 421 removeUidFromLlWatchList(int uid)422 private void removeUidFromLlWatchList(int uid) { 423 UidRec uidRec = mLowLatencyUidWatchList.get(uid); 424 if (uidRec == null) { 425 Log.e(TAG, "Failed to find uid in low-latency watch list"); 426 return; 427 } 428 429 if (uidRec.mLockCount > 0) { 430 uidRec.mLockCount--; 431 } else { 432 Log.e(TAG, "Error, uid record conatains no locks"); 433 } 434 if (uidRec.mLockCount == 0) { 435 mLowLatencyUidWatchList.remove(uid); 436 437 // Remove blame for this UID if it was alerady set 438 // Note that blame needs to be stopped only if it was started before 439 // to avoid calling the API unnecessarily, since it is reference counted 440 if (canActivateLowLatencyLock(0, uidRec)) { 441 setBlameLowLatencyUid(uid, false); 442 } 443 } 444 } 445 addWsToLlWatchList(WorkSource ws)446 private void addWsToLlWatchList(WorkSource ws) { 447 int wsSize = ws.size(); 448 for (int i = 0; i < wsSize; i++) { 449 final int uid = ws.getUid(i); 450 addUidToLlWatchList(uid); 451 } 452 453 final List<WorkChain> workChains = ws.getWorkChains(); 454 if (workChains != null) { 455 for (int i = 0; i < workChains.size(); ++i) { 456 final WorkChain workChain = workChains.get(i); 457 final int uid = workChain.getAttributionUid(); 458 addUidToLlWatchList(uid); 459 } 460 } 461 } 462 removeWsFromLlWatchList(WorkSource ws)463 private void removeWsFromLlWatchList(WorkSource ws) { 464 int wsSize = ws.size(); 465 for (int i = 0; i < wsSize; i++) { 466 final int uid = ws.getUid(i); 467 removeUidFromLlWatchList(uid); 468 } 469 470 final List<WorkChain> workChains = ws.getWorkChains(); 471 if (workChains != null) { 472 for (int i = 0; i < workChains.size(); ++i) { 473 final WorkChain workChain = workChains.get(i); 474 final int uid = workChain.getAttributionUid(); 475 removeUidFromLlWatchList(uid); 476 } 477 } 478 } 479 addLock(WifiLock lock)480 private synchronized boolean addLock(WifiLock lock) { 481 if (mVerboseLoggingEnabled) { 482 Log.d(TAG, "addLock: " + lock); 483 } 484 485 if (findLockByBinder(lock.getBinder()) != null) { 486 if (mVerboseLoggingEnabled) { 487 Log.d(TAG, "attempted to add a lock when already holding one"); 488 } 489 return false; 490 } 491 492 mWifiLocks.add(lock); 493 494 switch(lock.mMode) { 495 case WifiManager.WIFI_MODE_FULL_HIGH_PERF: 496 ++mFullHighPerfLocksAcquired; 497 // Start blaming this worksource if conditions are met 498 if (canActivateHighPerfLock()) { 499 setBlameHiPerfWs(lock.mWorkSource, true); 500 } 501 break; 502 case WifiManager.WIFI_MODE_FULL_LOW_LATENCY: 503 addWsToLlWatchList(lock.getWorkSource()); 504 ++mFullLowLatencyLocksAcquired; 505 break; 506 default: 507 // Do nothing 508 break; 509 } 510 511 // Recalculate the operating mode 512 updateOpMode(); 513 514 return true; 515 } 516 removeLock(IBinder binder)517 private synchronized WifiLock removeLock(IBinder binder) { 518 WifiLock lock = findLockByBinder(binder); 519 if (lock != null) { 520 mWifiLocks.remove(lock); 521 lock.unlinkDeathRecipient(); 522 } 523 return lock; 524 } 525 releaseLock(IBinder binder)526 private synchronized boolean releaseLock(IBinder binder) { 527 WifiLock wifiLock = removeLock(binder); 528 if (wifiLock == null) { 529 // attempting to release a lock that does not exist. 530 return false; 531 } 532 533 if (mVerboseLoggingEnabled) { 534 Log.d(TAG, "releaseLock: " + wifiLock); 535 } 536 537 switch(wifiLock.mMode) { 538 case WifiManager.WIFI_MODE_FULL_HIGH_PERF: 539 ++mFullHighPerfLocksReleased; 540 mWifiMetrics.addWifiLockAcqSession(WifiManager.WIFI_MODE_FULL_HIGH_PERF, 541 mClock.getElapsedSinceBootMillis() - wifiLock.getAcqTimestamp()); 542 // Stop blaming only if blaming was set before (conditions are met). 543 // This is to avoid calling the api unncessarily, since this API is 544 // reference counted in batteryStats and statsd 545 if (canActivateHighPerfLock()) { 546 setBlameHiPerfWs(wifiLock.mWorkSource, false); 547 } 548 break; 549 case WifiManager.WIFI_MODE_FULL_LOW_LATENCY: 550 removeWsFromLlWatchList(wifiLock.getWorkSource()); 551 ++mFullLowLatencyLocksReleased; 552 mWifiMetrics.addWifiLockAcqSession(WifiManager.WIFI_MODE_FULL_LOW_LATENCY, 553 mClock.getElapsedSinceBootMillis() - wifiLock.getAcqTimestamp()); 554 break; 555 default: 556 // Do nothing 557 break; 558 } 559 560 // Recalculate the operating mode 561 updateOpMode(); 562 563 return true; 564 } 565 updateOpMode()566 private synchronized boolean updateOpMode() { 567 final int newLockMode = getStrongestLockMode(); 568 569 if (newLockMode == mCurrentOpMode) { 570 // No action is needed 571 return true; 572 } 573 574 if (mVerboseLoggingEnabled) { 575 Log.d(TAG, "Current opMode: " + mCurrentOpMode + " New LockMode: " + newLockMode); 576 } 577 578 // Otherwise, we need to change current mode, first reset it to normal 579 switch (mCurrentOpMode) { 580 case WifiManager.WIFI_MODE_FULL_HIGH_PERF: 581 if (!mClientModeImpl.setPowerSave(true)) { 582 Log.e(TAG, "Failed to reset the OpMode from hi-perf to Normal"); 583 return false; 584 } 585 mWifiMetrics.addWifiLockActiveSession(WifiManager.WIFI_MODE_FULL_HIGH_PERF, 586 mClock.getElapsedSinceBootMillis() - mCurrentSessionStartTimeMs); 587 break; 588 589 case WifiManager.WIFI_MODE_FULL_LOW_LATENCY: 590 if (!setLowLatencyMode(false)) { 591 Log.e(TAG, "Failed to reset the OpMode from low-latency to Normal"); 592 return false; 593 } 594 mWifiMetrics.addWifiLockActiveSession(WifiManager.WIFI_MODE_FULL_LOW_LATENCY, 595 mClock.getElapsedSinceBootMillis() - mCurrentSessionStartTimeMs); 596 break; 597 598 case WifiManager.WIFI_MODE_NO_LOCKS_HELD: 599 default: 600 // No action 601 break; 602 } 603 604 // Set the current mode, before we attempt to set the new mode 605 mCurrentOpMode = WifiManager.WIFI_MODE_NO_LOCKS_HELD; 606 607 // Now switch to the new opMode 608 switch (newLockMode) { 609 case WifiManager.WIFI_MODE_FULL_HIGH_PERF: 610 if (!mClientModeImpl.setPowerSave(false)) { 611 Log.e(TAG, "Failed to set the OpMode to hi-perf"); 612 return false; 613 } 614 mCurrentSessionStartTimeMs = mClock.getElapsedSinceBootMillis(); 615 break; 616 617 case WifiManager.WIFI_MODE_FULL_LOW_LATENCY: 618 if (!setLowLatencyMode(true)) { 619 Log.e(TAG, "Failed to set the OpMode to low-latency"); 620 return false; 621 } 622 mCurrentSessionStartTimeMs = mClock.getElapsedSinceBootMillis(); 623 break; 624 625 case WifiManager.WIFI_MODE_NO_LOCKS_HELD: 626 // No action 627 break; 628 629 default: 630 // Invalid mode, don't change currentOpMode , and exit with error 631 Log.e(TAG, "Invalid new opMode: " + newLockMode); 632 return false; 633 } 634 635 // Now set the mode to the new value 636 mCurrentOpMode = newLockMode; 637 return true; 638 } 639 getLowLatencyModeSupport()640 private int getLowLatencyModeSupport() { 641 if (mLatencyModeSupport == LOW_LATENCY_SUPPORT_UNDEFINED) { 642 String ifaceName = mWifiNative.getClientInterfaceName(); 643 if (ifaceName == null) { 644 return LOW_LATENCY_SUPPORT_UNDEFINED; 645 } 646 647 long supportedFeatures = mWifiNative.getSupportedFeatureSet(ifaceName); 648 if (supportedFeatures != 0) { 649 if ((supportedFeatures & WifiManager.WIFI_FEATURE_LOW_LATENCY) != 0) { 650 mLatencyModeSupport = LOW_LATENCY_SUPPORTED; 651 } else { 652 mLatencyModeSupport = LOW_LATENCY_NOT_SUPPORTED; 653 } 654 } 655 } 656 657 return mLatencyModeSupport; 658 } 659 setLowLatencyMode(boolean enabled)660 private boolean setLowLatencyMode(boolean enabled) { 661 int lowLatencySupport = getLowLatencyModeSupport(); 662 663 if (lowLatencySupport == LOW_LATENCY_SUPPORT_UNDEFINED) { 664 // Support undefined, no action is taken 665 return false; 666 } 667 668 if (lowLatencySupport == LOW_LATENCY_SUPPORTED) { 669 if (!mClientModeImpl.setLowLatencyMode(enabled)) { 670 Log.e(TAG, "Failed to set low latency mode"); 671 return false; 672 } 673 674 if (!mClientModeImpl.setPowerSave(!enabled)) { 675 Log.e(TAG, "Failed to set power save mode"); 676 // Revert the low latency mode 677 mClientModeImpl.setLowLatencyMode(!enabled); 678 return false; 679 } 680 } else if (lowLatencySupport == LOW_LATENCY_NOT_SUPPORTED) { 681 // Only set power save mode 682 if (!mClientModeImpl.setPowerSave(!enabled)) { 683 Log.e(TAG, "Failed to set power save mode"); 684 return false; 685 } 686 } 687 688 return true; 689 } 690 findLockByBinder(IBinder binder)691 private synchronized WifiLock findLockByBinder(IBinder binder) { 692 for (WifiLock lock : mWifiLocks) { 693 if (lock.getBinder() == binder) { 694 return lock; 695 } 696 } 697 return null; 698 } 699 countFgLowLatencyUids()700 private int countFgLowLatencyUids() { 701 int uidCount = 0; 702 int listSize = mLowLatencyUidWatchList.size(); 703 for (int idx = 0; idx < listSize; idx++) { 704 UidRec uidRec = mLowLatencyUidWatchList.valueAt(idx); 705 if (uidRec.mIsFg) { 706 uidCount++; 707 } 708 } 709 return uidCount; 710 } 711 setBlameHiPerfWs(WorkSource ws, boolean shouldBlame)712 private void setBlameHiPerfWs(WorkSource ws, boolean shouldBlame) { 713 long ident = Binder.clearCallingIdentity(); 714 Pair<int[], String[]> uidsAndTags = WorkSourceUtil.getUidsAndTagsForWs(ws); 715 try { 716 if (shouldBlame) { 717 mBatteryStats.reportFullWifiLockAcquiredFromSource(ws); 718 WifiStatsLog.write(WifiStatsLog.WIFI_LOCK_STATE_CHANGED, 719 uidsAndTags.first, uidsAndTags.second, 720 WifiStatsLog.WIFI_LOCK_STATE_CHANGED__STATE__ON, 721 WifiStatsLog.WIFI_LOCK_STATE_CHANGED__MODE__WIFI_MODE_FULL_HIGH_PERF); 722 } else { 723 mBatteryStats.reportFullWifiLockReleasedFromSource(ws); 724 WifiStatsLog.write(WifiStatsLog.WIFI_LOCK_STATE_CHANGED, 725 uidsAndTags.first, uidsAndTags.second, 726 WifiStatsLog.WIFI_LOCK_STATE_CHANGED__STATE__OFF, 727 WifiStatsLog.WIFI_LOCK_STATE_CHANGED__MODE__WIFI_MODE_FULL_HIGH_PERF); 728 } 729 } finally { 730 Binder.restoreCallingIdentity(ident); 731 } 732 } 733 setBlameLowLatencyUid(int uid, boolean shouldBlame)734 private void setBlameLowLatencyUid(int uid, boolean shouldBlame) { 735 long ident = Binder.clearCallingIdentity(); 736 try { 737 if (shouldBlame) { 738 mBatteryStats.reportFullWifiLockAcquiredFromSource(new WorkSource(uid)); 739 WifiStatsLog.write_non_chained(WifiStatsLog.WIFI_LOCK_STATE_CHANGED, uid, null, 740 WifiStatsLog.WIFI_LOCK_STATE_CHANGED__STATE__ON, 741 WifiStatsLog.WIFI_LOCK_STATE_CHANGED__MODE__WIFI_MODE_FULL_LOW_LATENCY); 742 } else { 743 mBatteryStats.reportFullWifiLockReleasedFromSource(new WorkSource(uid)); 744 WifiStatsLog.write_non_chained(WifiStatsLog.WIFI_LOCK_STATE_CHANGED, uid, null, 745 WifiStatsLog.WIFI_LOCK_STATE_CHANGED__STATE__OFF, 746 WifiStatsLog.WIFI_LOCK_STATE_CHANGED__MODE__WIFI_MODE_FULL_LOW_LATENCY); 747 } 748 } finally { 749 Binder.restoreCallingIdentity(ident); 750 } 751 } 752 setBlameLowLatencyWatchList(boolean shouldBlame)753 private void setBlameLowLatencyWatchList(boolean shouldBlame) { 754 for (int idx = 0; idx < mLowLatencyUidWatchList.size(); idx++) { 755 UidRec uidRec = mLowLatencyUidWatchList.valueAt(idx); 756 // Affect the blame for only UIDs running in foreground 757 // UIDs running in the background are already not blamed, 758 // and they should remain in that state. 759 if (uidRec.mIsFg) { 760 setBlameLowLatencyUid(uidRec.mUid, shouldBlame); 761 } 762 } 763 } 764 dump(PrintWriter pw)765 protected void dump(PrintWriter pw) { 766 pw.println("Locks acquired: " 767 + mFullHighPerfLocksAcquired + " full high perf, " 768 + mFullLowLatencyLocksAcquired + " full low latency"); 769 pw.println("Locks released: " 770 + mFullHighPerfLocksReleased + " full high perf, " 771 + mFullLowLatencyLocksReleased + " full low latency"); 772 773 pw.println(); 774 pw.println("Locks held:"); 775 for (WifiLock lock : mWifiLocks) { 776 pw.print(" "); 777 pw.println(lock); 778 } 779 } 780 enableVerboseLogging(int verbose)781 protected void enableVerboseLogging(int verbose) { 782 if (verbose > 0) { 783 mVerboseLoggingEnabled = true; 784 } else { 785 mVerboseLoggingEnabled = false; 786 } 787 } 788 789 private class WifiLock implements IBinder.DeathRecipient { 790 String mTag; 791 int mUid; 792 IBinder mBinder; 793 int mMode; 794 WorkSource mWorkSource; 795 long mAcqTimestamp; 796 WifiLock(int lockMode, String tag, IBinder binder, WorkSource ws)797 WifiLock(int lockMode, String tag, IBinder binder, WorkSource ws) { 798 mTag = tag; 799 mBinder = binder; 800 mUid = Binder.getCallingUid(); 801 mMode = lockMode; 802 mWorkSource = ws; 803 mAcqTimestamp = mClock.getElapsedSinceBootMillis(); 804 try { 805 mBinder.linkToDeath(this, 0); 806 } catch (RemoteException e) { 807 Log.e(TAG, "mBinder.linkToDeath failed: " + e.getMessage()); 808 binderDied(); 809 } 810 } 811 getWorkSource()812 protected WorkSource getWorkSource() { 813 return mWorkSource; 814 } 815 getUid()816 protected int getUid() { 817 return mUid; 818 } 819 getBinder()820 protected IBinder getBinder() { 821 return mBinder; 822 } 823 getAcqTimestamp()824 protected long getAcqTimestamp() { 825 return mAcqTimestamp; 826 } 827 binderDied()828 public void binderDied() { 829 releaseLock(mBinder); 830 } 831 unlinkDeathRecipient()832 public void unlinkDeathRecipient() { 833 try { 834 mBinder.unlinkToDeath(this, 0); 835 } catch (NoSuchElementException e) { 836 Log.e(TAG, "mBinder.unlinkToDeath failed: " + e.getMessage()); 837 } 838 } 839 toString()840 public String toString() { 841 return "WifiLock{" + this.mTag + " type=" + this.mMode + " uid=" + mUid 842 + " workSource=" + mWorkSource + "}"; 843 } 844 } 845 846 private class UidRec { 847 final int mUid; 848 // Count of locks owned or co-owned by this UID 849 int mLockCount; 850 // Is this UID running in foreground 851 boolean mIsFg; 852 UidRec(int uid)853 UidRec(int uid) { 854 mUid = uid; 855 } 856 } 857 } 858