diff options
author | Wei Wang <weiwa@google.com> | 2017-04-28 01:25:12 +0000 |
---|---|---|
committer | android-build-merger <android-build-merger@google.com> | 2017-04-28 01:25:12 +0000 |
commit | 5be1372a78bd45091d1fd656ef71578d530cda82 (patch) | |
tree | 31cc396447c43791cdf457886b622cecee5f7100 /service | |
parent | abeba8806b30d4109c22d5c3ee86bebeb7a09e7a (diff) | |
parent | 1ddaf32d5cbac5c9237a86eb214397df7c33bc00 (diff) |
Merge "Throttle wifi scan requests from background apps (2/2)." into oc-dev am: a6ff93d77c
am: 1ddaf32d5c
Change-Id: Id77304dc8f987653cc4820b3ca9ee336befb179a
Diffstat (limited to 'service')
-rw-r--r-- | service/java/com/android/server/wifi/WifiServiceImpl.java | 153 |
1 files changed, 133 insertions, 20 deletions
diff --git a/service/java/com/android/server/wifi/WifiServiceImpl.java b/service/java/com/android/server/wifi/WifiServiceImpl.java index b1569c17d..ed1b49314 100644 --- a/service/java/com/android/server/wifi/WifiServiceImpl.java +++ b/service/java/com/android/server/wifi/WifiServiceImpl.java @@ -30,6 +30,8 @@ import static com.android.server.wifi.WifiController.CMD_USER_PRESENT; import static com.android.server.wifi.WifiController.CMD_WIFI_TOGGLED; import android.Manifest; +import android.app.ActivityManager; +import android.app.ActivityManager.RunningAppProcessInfo; import android.app.AppOpsManager; import android.bluetooth.BluetoothAdapter; import android.content.BroadcastReceiver; @@ -74,12 +76,13 @@ import android.os.PowerManager; import android.os.Process; import android.os.RemoteException; import android.os.ResultReceiver; -import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; import android.os.WorkSource; import android.provider.Settings; import android.text.TextUtils; +import android.util.ArrayMap; +import android.util.ArraySet; import android.util.Log; import android.util.Slog; @@ -131,15 +134,26 @@ public class WifiServiceImpl extends IWifiManager.Stub { private static final String DUMP_ARG_SET_IPREACH_DISCONNECT_ENABLED = "enabled"; private static final String DUMP_ARG_SET_IPREACH_DISCONNECT_DISABLED = "disabled"; + // Default scan background throttling interval if not overriden in settings + private static final long DEFAULT_SCAN_BACKGROUND_THROTTLE_INTERVAL_MS = 30 * 60 * 1000; + + // Apps with importance higher than this value is considered as background app. + private static final int BACKGROUND_IMPORTANCE_CUTOFF = + RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE; + final WifiStateMachine mWifiStateMachine; private final Context mContext; private final FrameworkFacade mFacade; + private final Clock mClock; + private final ArraySet<String> mBackgroundThrottlePackageWhitelist = new ArraySet<>(); private final PowerManager mPowerManager; private final AppOpsManager mAppOps; private final UserManager mUserManager; + private final ActivityManager mActivityManager; private final WifiCountryCode mCountryCode; + private long mBackgroundThrottleInterval; // Debug counter tracking scan requests sent by WifiManager private int scanRequestCounter = 0; @@ -156,9 +170,12 @@ public class WifiServiceImpl extends IWifiManager.Stub { /* Backup/Restore Module */ private final WifiBackupRestore mWifiBackupRestore; - private WifiScanner mWifiScanner; + // Map of package name of background scan apps and last scan timestamp. + private final ArrayMap<String, Long> mLastScanTimestamps; + private WifiScanner mWifiScanner; private WifiLog mLog; + /** * Asynchronous channel to WifiStateMachine */ @@ -328,6 +345,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { public WifiServiceImpl(Context context, WifiInjector wifiInjector, AsyncChannel asyncChannel) { mContext = context; mWifiInjector = wifiInjector; + mClock = wifiInjector.getClock(); mFacade = mWifiInjector.getFrameworkFacade(); mWifiMetrics = mWifiInjector.getWifiMetrics(); @@ -339,6 +357,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { mSettingsStore = mWifiInjector.getWifiSettingsStore(); mPowerManager = mContext.getSystemService(PowerManager.class); mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE); + mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE); mCertManager = mWifiInjector.getWifiCertManager(); mWifiLockManager = mWifiInjector.getWifiLockManager(); mWifiMulticastLockManager = mWifiInjector.getWifiMulticastLockManager(); @@ -354,6 +373,9 @@ public class WifiServiceImpl extends IWifiManager.Stub { mWifiPermissionsUtil = mWifiInjector.getWifiPermissionsUtil(); mLog = mWifiInjector.makeLog(TAG); mFrameworkFacade = wifiInjector.getFrameworkFacade(); + mLastScanTimestamps = new ArrayMap<>(); + updateBackgroundThrottleInterval(); + updateBackgroundThrottlingWhitelist(); enableVerboseLoggingInternal(getVerboseLoggingLevel()); } @@ -387,6 +409,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { (wifiEnabled ? "enabled" : "disabled")); registerForScanModeChange(); + registerForBackgroundThrottleChanges(); mContext.registerReceiver( new BroadcastReceiver() { @Override @@ -461,11 +484,25 @@ public class WifiServiceImpl extends IWifiManager.Stub { * * @param settings If null, use default parameter, i.e. full scan. * @param workSource If null, all blame is given to the calling uid. + * @param packageName Package name of the app that requests wifi scan. */ @Override - public void startScan(ScanSettings settings, WorkSource workSource) { + public void startScan(ScanSettings settings, WorkSource workSource, String packageName) { enforceChangePermission(); + mLog.trace("startScan uid=%").c(Binder.getCallingUid()).flush(); + // Check and throttle background apps for wifi scan. + if (isRequestFromBackground(packageName)) { + long lastScanMs = mLastScanTimestamps.getOrDefault(packageName, 0L); + long elapsedRealtime = mClock.getElapsedSinceBootMillis(); + + if (lastScanMs != 0 && (elapsedRealtime - lastScanMs) < mBackgroundThrottleInterval) { + sendFailedScanBroadcast(); + return; + } + // Proceed with the scan request and record the time. + mLastScanTimestamps.put(packageName, elapsedRealtime); + } synchronized (this) { if (mWifiScanner == null) { mWifiScanner = mWifiInjector.getWifiScanner(); @@ -474,21 +511,11 @@ public class WifiServiceImpl extends IWifiManager.Stub { // Need to send an immediate scan result broadcast in case the // caller is waiting for a result .. - // clear calling identity to send broadcast - long callingIdentity = Binder.clearCallingIdentity(); - try { - // TODO: investigate if the logic to cancel scans when idle can move to - // WifiScanningServiceImpl. This will 1 - clean up WifiServiceImpl and 2 - - // avoid plumbing an awkward path to report a cancelled/failed scan. This will - // be sent directly until b/31398592 is fixed. - Intent intent = new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION); - intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); - intent.putExtra(WifiManager.EXTRA_RESULTS_UPDATED, false); - mContext.sendBroadcastAsUser(intent, UserHandle.ALL); - } finally { - // restore calling identity - Binder.restoreCallingIdentity(callingIdentity); - } + // TODO: investigate if the logic to cancel scans when idle can move to + // WifiScanningServiceImpl. This will 1 - clean up WifiServiceImpl and 2 - + // avoid plumbing an awkward path to report a cancelled/failed scan. This will + // be sent directly until b/31398592 is fixed. + sendFailedScanBroadcast(); mScanPending = true; return; } @@ -513,6 +540,45 @@ public class WifiServiceImpl extends IWifiManager.Stub { settings, workSource); } + // Send a failed scan broadcast to indicate the current scan request failed. + private void sendFailedScanBroadcast() { + // clear calling identity to send broadcast + long callingIdentity = Binder.clearCallingIdentity(); + try { + Intent intent = new Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); + intent.putExtra(WifiManager.EXTRA_RESULTS_UPDATED, false); + mContext.sendBroadcastAsUser(intent, UserHandle.ALL); + } finally { + // restore calling identity + Binder.restoreCallingIdentity(callingIdentity); + } + + } + + // Check if the request comes from background. + private boolean isRequestFromBackground(String packageName) { + // Requests from system or wifi are not background. + if (Binder.getCallingUid() == Process.SYSTEM_UID + || Binder.getCallingUid() == Process.WIFI_UID) { + return false; + } + mAppOps.checkPackage(Binder.getCallingUid(), packageName); + if (mBackgroundThrottlePackageWhitelist.contains(packageName)) { + return false; + } + + // getPackageImportance requires PACKAGE_USAGE_STATS permission, so clearing the incoming + // identify so the permission check can be done on system process where wifi runs in. + long callingIdentity = Binder.clearCallingIdentity(); + try { + return mActivityManager.getPackageImportance(packageName) + > BACKGROUND_IMPORTANCE_CUTOFF; + } finally { + Binder.restoreCallingIdentity(callingIdentity); + } + } + @Override public String getCurrentNetworkWpsNfcConfigurationToken() { enforceConnectivityInternalPermission(); @@ -541,7 +607,8 @@ public class WifiServiceImpl extends IWifiManager.Stub { } if (doScan) { // Someone requested a scan while we were idle; do a full scan now. - startScan(null, null); + // The package name doesn't matter as the request comes from System UID. + startScan(null, null, ""); } } @@ -953,7 +1020,7 @@ public class WifiServiceImpl extends IWifiManager.Stub { } // Convert the LinkLayerStats into EnergyActivity - energyInfo = new WifiActivityEnergyInfo(SystemClock.elapsedRealtime(), + energyInfo = new WifiActivityEnergyInfo(mClock.getElapsedSinceBootMillis(), WifiActivityEnergyInfo.STACK_STATE_STATE_IDLE, stats.tx_time, txTimePerLevel, stats.rx_time, rxIdleTime, energyUsed); } @@ -1588,6 +1655,52 @@ public class WifiServiceImpl extends IWifiManager.Stub { mFrameworkFacade.registerContentObserver(mContext, Settings.Global.getUriFor(Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE), false, contentObserver); + + } + + // Monitors settings changes related to background wifi scan throttling. + private void registerForBackgroundThrottleChanges() { + mFrameworkFacade.registerContentObserver( + mContext, + Settings.Global.getUriFor( + Settings.Global.WIFI_SCAN_BACKGROUND_THROTTLE_INTERVAL_MS), + false, + new ContentObserver(null) { + @Override + public void onChange(boolean selfChange) { + updateBackgroundThrottleInterval(); + } + } + ); + mFrameworkFacade.registerContentObserver( + mContext, + Settings.Global.getUriFor( + Settings.Global.WIFI_SCAN_BACKGROUND_THROTTLE_PACKAGE_WHITELIST), + false, + new ContentObserver(null) { + @Override + public void onChange(boolean selfChange) { + updateBackgroundThrottlingWhitelist(); + } + } + ); + } + + private void updateBackgroundThrottleInterval() { + mBackgroundThrottleInterval = mFrameworkFacade.getLongSetting( + mContext, + Settings.Global.WIFI_SCAN_BACKGROUND_THROTTLE_INTERVAL_MS, + DEFAULT_SCAN_BACKGROUND_THROTTLE_INTERVAL_MS); + } + + private void updateBackgroundThrottlingWhitelist() { + String setting = mFrameworkFacade.getStringSetting( + mContext, + Settings.Global.WIFI_SCAN_BACKGROUND_THROTTLE_PACKAGE_WHITELIST); + mBackgroundThrottlePackageWhitelist.clear(); + if (setting != null) { + mBackgroundThrottlePackageWhitelist.addAll(Arrays.asList(setting.split(","))); + } } private void registerForBroadcasts() { |