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