• 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 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