Home | History | Annotate | Download | only in wifi
      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.content.Context;
     20 import android.net.wifi.WifiManager;
     21 import android.os.Binder;
     22 import android.os.IBinder;
     23 import android.os.RemoteException;
     24 import android.os.WorkSource;
     25 import android.util.Slog;
     26 
     27 import com.android.internal.app.IBatteryStats;
     28 
     29 import java.io.PrintWriter;
     30 import java.util.ArrayList;
     31 import java.util.List;
     32 
     33 /**
     34  * WifiLockManager maintains the list of wake locks held by different applications.
     35  */
     36 public class WifiLockManager {
     37     private static final String TAG = "WifiLockManager";
     38     private boolean mVerboseLoggingEnabled = false;
     39 
     40     private final Context mContext;
     41     private final IBatteryStats mBatteryStats;
     42 
     43     private final List<WifiLock> mWifiLocks = new ArrayList<>();
     44     // some wifi lock statistics
     45     private int mFullHighPerfLocksAcquired;
     46     private int mFullHighPerfLocksReleased;
     47     private int mFullLocksAcquired;
     48     private int mFullLocksReleased;
     49     private int mScanLocksAcquired;
     50     private int mScanLocksReleased;
     51 
     52     WifiLockManager(Context context, IBatteryStats batteryStats) {
     53         mContext = context;
     54         mBatteryStats = batteryStats;
     55     }
     56 
     57     /**
     58      * Method allowing a calling app to acquire a Wifi WakeLock in the supplied mode.
     59      *
     60      * This method verifies that the caller has permission to make the call and that the lock mode
     61      * is a valid WifiLock mode.
     62      * @param lockMode int representation of the Wifi WakeLock type.
     63      * @param tag String passed to WifiManager.WifiLock
     64      * @param binder IBinder for the calling app
     65      * @param ws WorkSource of the calling app
     66      *
     67      * @return true if the lock was successfully acquired, false if the lockMode was invalid.
     68      */
     69     public boolean acquireWifiLock(int lockMode, String tag, IBinder binder, WorkSource ws) {
     70         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
     71         if (!isValidLockMode(lockMode)) {
     72             throw new IllegalArgumentException("lockMode =" + lockMode);
     73         }
     74         if (ws == null || ws.size() == 0) {
     75             ws = new WorkSource(Binder.getCallingUid());
     76         } else {
     77             mContext.enforceCallingOrSelfPermission(
     78                     android.Manifest.permission.UPDATE_DEVICE_STATS, null);
     79         }
     80         return addLock(new WifiLock(lockMode, tag, binder, ws));
     81     }
     82 
     83     /**
     84      * Method used by applications to release a WiFi Wake lock.  This method checks permissions for
     85      * the caller and if allowed, releases the underlying WifiLock(s).
     86      *
     87      * @param binder IBinder for the calling app.
     88      * @return true if the lock was released, false if the caller did not hold any locks
     89      */
     90     public boolean releaseWifiLock(IBinder binder) {
     91         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);
     92         return releaseLock(binder);
     93     }
     94 
     95     /**
     96      * Method used to get the strongest lock type currently held by the WifiLockManager.
     97      *
     98      * If no locks are held, WifiManager.WIFI_MODE_NO_LOCKS_HELD is returned.
     99      *
    100      * @return int representing the currently held (highest power consumption) lock.
    101      */
    102     public synchronized int getStrongestLockMode() {
    103         if (mWifiLocks.isEmpty()) {
    104             return WifiManager.WIFI_MODE_NO_LOCKS_HELD;
    105         }
    106 
    107         if (mFullHighPerfLocksAcquired > mFullHighPerfLocksReleased) {
    108             return WifiManager.WIFI_MODE_FULL_HIGH_PERF;
    109         }
    110 
    111         if (mFullLocksAcquired > mFullLocksReleased) {
    112             return WifiManager.WIFI_MODE_FULL;
    113         }
    114 
    115         return WifiManager.WIFI_MODE_SCAN_ONLY;
    116     }
    117 
    118     /**
    119      * Method to create a WorkSource containing all active WifiLock WorkSources.
    120      */
    121     public synchronized WorkSource createMergedWorkSource() {
    122         WorkSource mergedWS = new WorkSource();
    123         for (WifiLock lock : mWifiLocks) {
    124             mergedWS.add(lock.getWorkSource());
    125         }
    126         return mergedWS;
    127     }
    128 
    129     /**
    130      * Method used to update WifiLocks with a new WorkSouce.
    131      *
    132      * @param binder IBinder for the calling application.
    133      * @param ws WorkSource to add to the existing WifiLock(s).
    134      */
    135     public synchronized void updateWifiLockWorkSource(IBinder binder, WorkSource ws) {
    136         // Does the caller have permission to make this call?
    137         mContext.enforceCallingOrSelfPermission(
    138                 android.Manifest.permission.UPDATE_DEVICE_STATS, null);
    139 
    140         // Now check if there is an active lock
    141         WifiLock wl = findLockByBinder(binder);
    142         if (wl == null) {
    143             throw new IllegalArgumentException("Wifi lock not active");
    144         }
    145 
    146         WorkSource newWorkSource;
    147         if (ws == null || ws.size() == 0) {
    148             newWorkSource = new WorkSource(Binder.getCallingUid());
    149         } else {
    150             // Make a copy of the WorkSource before adding it to the WakeLock
    151             newWorkSource = new WorkSource(ws);
    152         }
    153 
    154         long ident = Binder.clearCallingIdentity();
    155         try {
    156             mBatteryStats.noteFullWifiLockReleasedFromSource(wl.mWorkSource);
    157             wl.mWorkSource = newWorkSource;
    158             mBatteryStats.noteFullWifiLockAcquiredFromSource(wl.mWorkSource);
    159         } catch (RemoteException e) {
    160         } finally {
    161             Binder.restoreCallingIdentity(ident);
    162         }
    163     }
    164 
    165     private static boolean isValidLockMode(int lockMode) {
    166         if (lockMode != WifiManager.WIFI_MODE_FULL
    167                 && lockMode != WifiManager.WIFI_MODE_SCAN_ONLY
    168                 && lockMode != WifiManager.WIFI_MODE_FULL_HIGH_PERF) {
    169             return false;
    170         }
    171         return true;
    172     }
    173 
    174     private synchronized boolean addLock(WifiLock lock) {
    175         if (mVerboseLoggingEnabled) {
    176             Slog.d(TAG, "addLock: " + lock);
    177         }
    178 
    179         if (findLockByBinder(lock.getBinder()) != null) {
    180             if (mVerboseLoggingEnabled) {
    181                 Slog.d(TAG, "attempted to add a lock when already holding one");
    182             }
    183             return false;
    184         }
    185 
    186         mWifiLocks.add(lock);
    187 
    188         boolean lockAdded = false;
    189         long ident = Binder.clearCallingIdentity();
    190         try {
    191             mBatteryStats.noteFullWifiLockAcquiredFromSource(lock.mWorkSource);
    192             switch(lock.mMode) {
    193                 case WifiManager.WIFI_MODE_FULL:
    194                     ++mFullLocksAcquired;
    195                     break;
    196                 case WifiManager.WIFI_MODE_FULL_HIGH_PERF:
    197                     ++mFullHighPerfLocksAcquired;
    198                     break;
    199                 case WifiManager.WIFI_MODE_SCAN_ONLY:
    200                     ++mScanLocksAcquired;
    201                     break;
    202             }
    203             lockAdded = true;
    204         } catch (RemoteException e) {
    205         } finally {
    206             Binder.restoreCallingIdentity(ident);
    207         }
    208         return lockAdded;
    209     }
    210 
    211     private synchronized WifiLock removeLock(IBinder binder) {
    212         WifiLock lock = findLockByBinder(binder);
    213         if (lock != null) {
    214             mWifiLocks.remove(lock);
    215             lock.unlinkDeathRecipient();
    216         }
    217         return lock;
    218     }
    219 
    220     private synchronized boolean releaseLock(IBinder binder) {
    221         WifiLock wifiLock = removeLock(binder);
    222         if (wifiLock == null) {
    223             // attempting to release a lock that is not active.
    224             return false;
    225         }
    226 
    227         if (mVerboseLoggingEnabled) {
    228             Slog.d(TAG, "releaseLock: " + wifiLock);
    229         }
    230 
    231         long ident = Binder.clearCallingIdentity();
    232         try {
    233             mBatteryStats.noteFullWifiLockReleasedFromSource(wifiLock.mWorkSource);
    234             switch(wifiLock.mMode) {
    235                 case WifiManager.WIFI_MODE_FULL:
    236                     ++mFullLocksReleased;
    237                     break;
    238                 case WifiManager.WIFI_MODE_FULL_HIGH_PERF:
    239                     ++mFullHighPerfLocksReleased;
    240                     break;
    241                 case WifiManager.WIFI_MODE_SCAN_ONLY:
    242                     ++mScanLocksReleased;
    243                     break;
    244             }
    245         } catch (RemoteException e) {
    246         } finally {
    247             Binder.restoreCallingIdentity(ident);
    248         }
    249         return true;
    250     }
    251 
    252 
    253     private synchronized WifiLock findLockByBinder(IBinder binder) {
    254         for (WifiLock lock : mWifiLocks) {
    255             if (lock.getBinder() == binder) {
    256                 return lock;
    257             }
    258         }
    259         return null;
    260     }
    261 
    262     protected void dump(PrintWriter pw) {
    263         pw.println("Locks acquired: " + mFullLocksAcquired + " full, "
    264                 + mFullHighPerfLocksAcquired + " full high perf, "
    265                 + mScanLocksAcquired + " scan");
    266         pw.println("Locks released: " + mFullLocksReleased + " full, "
    267                 + mFullHighPerfLocksReleased + " full high perf, "
    268                 + mScanLocksReleased + " scan");
    269         pw.println();
    270         pw.println("Locks held:");
    271         for (WifiLock lock : mWifiLocks) {
    272             pw.print("    ");
    273             pw.println(lock);
    274         }
    275     }
    276 
    277     protected void enableVerboseLogging(int verbose) {
    278         if (verbose > 0) {
    279             mVerboseLoggingEnabled = true;
    280         } else {
    281             mVerboseLoggingEnabled = false;
    282         }
    283     }
    284 
    285     private class WifiLock implements IBinder.DeathRecipient {
    286         String mTag;
    287         int mUid;
    288         IBinder mBinder;
    289         int mMode;
    290         WorkSource mWorkSource;
    291 
    292         WifiLock(int lockMode, String tag, IBinder binder, WorkSource ws) {
    293             mTag = tag;
    294             mBinder = binder;
    295             mUid = Binder.getCallingUid();
    296             mMode = lockMode;
    297             mWorkSource = ws;
    298             try {
    299                 mBinder.linkToDeath(this, 0);
    300             } catch (RemoteException e) {
    301                 binderDied();
    302             }
    303         }
    304 
    305         protected WorkSource getWorkSource() {
    306             return mWorkSource;
    307         }
    308 
    309         protected int getUid() {
    310             return mUid;
    311         }
    312 
    313         protected IBinder getBinder() {
    314             return mBinder;
    315         }
    316 
    317         public void binderDied() {
    318             releaseLock(mBinder);
    319         }
    320 
    321         public void unlinkDeathRecipient() {
    322             mBinder.unlinkToDeath(this, 0);
    323         }
    324 
    325         public String toString() {
    326             return "WifiLock{" + this.mTag + " type=" + this.mMode + " uid=" + mUid + "}";
    327         }
    328     }
    329 }
    330