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