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