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