• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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