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