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