diff options
Diffstat (limited to 'services/java/com/android')
-rw-r--r-- | services/java/com/android/server/BackupManagerService.java | 1 | ||||
-rw-r--r-- | services/java/com/android/server/SystemServer.java | 1 | ||||
-rw-r--r-- | services/java/com/android/server/net/LockdownVpnTracker.java | 18 | ||||
-rw-r--r-- | services/java/com/android/server/wifi/WifiNotificationController.java | 292 | ||||
-rw-r--r-- | services/java/com/android/server/wifi/WifiService.java (renamed from services/java/com/android/server/WifiService.java) | 545 | ||||
-rw-r--r-- | services/java/com/android/server/wifi/WifiSettingsStore.java | 178 | ||||
-rw-r--r-- | services/java/com/android/server/wifi/WifiTrafficPoller.java | 163 |
7 files changed, 701 insertions, 497 deletions
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java index dfde692..401a25f 100644 --- a/services/java/com/android/server/BackupManagerService.java +++ b/services/java/com/android/server/BackupManagerService.java @@ -3897,6 +3897,7 @@ class BackupManagerService extends IBackupManager.Stub { && !info.domain.equals(FullBackup.DATABASE_TREE_TOKEN) && !info.domain.equals(FullBackup.ROOT_TREE_TOKEN) && !info.domain.equals(FullBackup.SHAREDPREFS_TREE_TOKEN) + && !info.domain.equals(FullBackup.MANAGED_EXTERNAL_TREE_TOKEN) && !info.domain.equals(FullBackup.OBB_TREE_TOKEN) && !info.domain.equals(FullBackup.CACHE_TREE_TOKEN)) { throw new IOException("Unrecognized domain " + info.domain); diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 949c2ed..8ef247e 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -62,6 +62,7 @@ import com.android.server.power.PowerManagerService; import com.android.server.power.ShutdownThread; import com.android.server.search.SearchManagerService; import com.android.server.usb.UsbService; +import com.android.server.wifi.WifiService; import com.android.server.wm.WindowManagerService; import dalvik.system.VMRuntime; diff --git a/services/java/com/android/server/net/LockdownVpnTracker.java b/services/java/com/android/server/net/LockdownVpnTracker.java index f32dd09..5b6e485 100644 --- a/services/java/com/android/server/net/LockdownVpnTracker.java +++ b/services/java/com/android/server/net/LockdownVpnTracker.java @@ -56,7 +56,9 @@ public class LockdownVpnTracker { private static final int MAX_ERROR_COUNT = 4; private static final String ACTION_LOCKDOWN_RESET = "com.android.server.action.LOCKDOWN_RESET"; + private static final String ACTION_VPN_SETTINGS = "android.net.vpn.SETTINGS"; + private static final String EXTRA_PICK_LOCKDOWN = "android.net.vpn.PICK_LOCKDOWN"; private final Context mContext; private final INetworkManagementService mNetService; @@ -66,7 +68,8 @@ public class LockdownVpnTracker { private final Object mStateLock = new Object(); - private PendingIntent mResetIntent; + private final PendingIntent mConfigIntent; + private final PendingIntent mResetIntent; private String mAcceptedEgressIface; private String mAcceptedIface; @@ -86,6 +89,10 @@ public class LockdownVpnTracker { mVpn = Preconditions.checkNotNull(vpn); mProfile = Preconditions.checkNotNull(profile); + final Intent configIntent = new Intent(ACTION_VPN_SETTINGS); + configIntent.putExtra(EXTRA_PICK_LOCKDOWN, true); + mConfigIntent = PendingIntent.getActivity(mContext, 0, configIntent, 0); + final Intent resetIntent = new Intent(ACTION_LOCKDOWN_RESET); resetIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); mResetIntent = PendingIntent.getBroadcast(mContext, 0, resetIntent, 0); @@ -193,6 +200,7 @@ public class LockdownVpnTracker { // TODO: support non-standard port numbers mNetService.setFirewallEgressDestRule(mProfile.server, 500, true); mNetService.setFirewallEgressDestRule(mProfile.server, 4500, true); + mNetService.setFirewallEgressDestRule(mProfile.server, 1701, true); } catch (RemoteException e) { throw new RuntimeException("Problem setting firewall rules", e); } @@ -218,6 +226,7 @@ public class LockdownVpnTracker { try { mNetService.setFirewallEgressDestRule(mProfile.server, 500, false); mNetService.setFirewallEgressDestRule(mProfile.server, 4500, false); + mNetService.setFirewallEgressDestRule(mProfile.server, 1701, false); } catch (RemoteException e) { throw new RuntimeException("Problem setting firewall rules", e); } @@ -281,10 +290,13 @@ public class LockdownVpnTracker { builder.setWhen(0); builder.setSmallIcon(iconRes); builder.setContentTitle(mContext.getString(titleRes)); - builder.setContentText(mContext.getString(R.string.vpn_lockdown_reset)); - builder.setContentIntent(mResetIntent); + builder.setContentText(mContext.getString(R.string.vpn_lockdown_config)); + builder.setContentIntent(mConfigIntent); builder.setPriority(Notification.PRIORITY_LOW); builder.setOngoing(true); + builder.addAction( + R.drawable.ic_menu_refresh, mContext.getString(R.string.reset), mResetIntent); + NotificationManager.from(mContext).notify(TAG, 0, builder.build()); } diff --git a/services/java/com/android/server/wifi/WifiNotificationController.java b/services/java/com/android/server/wifi/WifiNotificationController.java new file mode 100644 index 0000000..17ef7c8 --- /dev/null +++ b/services/java/com/android/server/wifi/WifiNotificationController.java @@ -0,0 +1,292 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wifi; + +import android.app.Notification; +import android.app.NotificationManager; +import android.app.TaskStackBuilder; +import android.content.BroadcastReceiver; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.database.ContentObserver; +import android.net.NetworkInfo; +import android.net.wifi.ScanResult; +import android.net.wifi.WifiManager; +import android.net.wifi.WifiStateMachine; +import android.os.Handler; +import android.os.Message; +import android.os.UserHandle; +import android.provider.Settings; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.List; + +/* Takes care of handling the "open wi-fi network available" notification @hide */ +final class WifiNotificationController { + /** + * The icon to show in the 'available networks' notification. This will also + * be the ID of the Notification given to the NotificationManager. + */ + private static final int ICON_NETWORKS_AVAILABLE = + com.android.internal.R.drawable.stat_notify_wifi_in_range; + /** + * When a notification is shown, we wait this amount before possibly showing it again. + */ + private final long NOTIFICATION_REPEAT_DELAY_MS; + /** + * Whether the user has set the setting to show the 'available networks' notification. + */ + private boolean mNotificationEnabled; + /** + * Observes the user setting to keep {@link #mNotificationEnabled} in sync. + */ + private NotificationEnabledSettingObserver mNotificationEnabledSettingObserver; + /** + * The {@link System#currentTimeMillis()} must be at least this value for us + * to show the notification again. + */ + private long mNotificationRepeatTime; + /** + * The Notification object given to the NotificationManager. + */ + private Notification mNotification; + /** + * Whether the notification is being shown, as set by us. That is, if the + * user cancels the notification, we will not receive the callback so this + * will still be true. We only guarantee if this is false, then the + * notification is not showing. + */ + private boolean mNotificationShown; + /** + * The number of continuous scans that must occur before consider the + * supplicant in a scanning state. This allows supplicant to associate with + * remembered networks that are in the scan results. + */ + private static final int NUM_SCANS_BEFORE_ACTUALLY_SCANNING = 3; + /** + * The number of scans since the last network state change. When this + * exceeds {@link #NUM_SCANS_BEFORE_ACTUALLY_SCANNING}, we consider the + * supplicant to actually be scanning. When the network state changes to + * something other than scanning, we reset this to 0. + */ + private int mNumScansSinceNetworkStateChange; + + private final Context mContext; + private final WifiStateMachine mWifiStateMachine; + private NetworkInfo mNetworkInfo; + + WifiNotificationController(Context context, WifiStateMachine wsm) { + mContext = context; + mWifiStateMachine = wsm; + + IntentFilter filter = new IntentFilter(); + filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); + filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); + filter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION); + + mContext.registerReceiver( + new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (intent.getAction().equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) { + resetNotification(); + } else if (intent.getAction().equals( + WifiManager.NETWORK_STATE_CHANGED_ACTION)) { + mNetworkInfo = (NetworkInfo) intent.getParcelableExtra( + WifiManager.EXTRA_NETWORK_INFO); + // reset & clear notification on a network connect & disconnect + switch(mNetworkInfo.getDetailedState()) { + case CONNECTED: + case DISCONNECTED: + case CAPTIVE_PORTAL_CHECK: + resetNotification(); + break; + } + } else if (intent.getAction().equals( + WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) { + checkAndSetNotification(mNetworkInfo, + mWifiStateMachine.syncGetScanResultsList()); + } + } + }, filter); + + // Setting is in seconds + NOTIFICATION_REPEAT_DELAY_MS = Settings.Global.getInt(context.getContentResolver(), + Settings.Global.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, 900) * 1000l; + mNotificationEnabledSettingObserver = new NotificationEnabledSettingObserver(new Handler()); + mNotificationEnabledSettingObserver.register(); + } + + private synchronized void checkAndSetNotification(NetworkInfo networkInfo, + List<ScanResult> scanResults) { + // TODO: unregister broadcast so we do not have to check here + // If we shouldn't place a notification on available networks, then + // don't bother doing any of the following + if (!mNotificationEnabled) return; + if (networkInfo == null) return; + + NetworkInfo.State state = networkInfo.getState(); + if ((state == NetworkInfo.State.DISCONNECTED) + || (state == NetworkInfo.State.UNKNOWN)) { + if (scanResults != null) { + int numOpenNetworks = 0; + for (int i = scanResults.size() - 1; i >= 0; i--) { + ScanResult scanResult = scanResults.get(i); + + //A capability of [ESS] represents an open access point + //that is available for an STA to connect + if (scanResult.capabilities != null && + scanResult.capabilities.equals("[ESS]")) { + numOpenNetworks++; + } + } + + if (numOpenNetworks > 0) { + if (++mNumScansSinceNetworkStateChange >= NUM_SCANS_BEFORE_ACTUALLY_SCANNING) { + /* + * We've scanned continuously at least + * NUM_SCANS_BEFORE_NOTIFICATION times. The user + * probably does not have a remembered network in range, + * since otherwise supplicant would have tried to + * associate and thus resetting this counter. + */ + setNotificationVisible(true, numOpenNetworks, false, 0); + } + return; + } + } + } + + // No open networks in range, remove the notification + setNotificationVisible(false, 0, false, 0); + } + + /** + * Clears variables related to tracking whether a notification has been + * shown recently and clears the current notification. + */ + private synchronized void resetNotification() { + mNotificationRepeatTime = 0; + mNumScansSinceNetworkStateChange = 0; + setNotificationVisible(false, 0, false, 0); + } + + /** + * Display or don't display a notification that there are open Wi-Fi networks. + * @param visible {@code true} if notification should be visible, {@code false} otherwise + * @param numNetworks the number networks seen + * @param force {@code true} to force notification to be shown/not-shown, + * even if it is already shown/not-shown. + * @param delay time in milliseconds after which the notification should be made + * visible or invisible. + */ + private void setNotificationVisible(boolean visible, int numNetworks, boolean force, + int delay) { + + // Since we use auto cancel on the notification, when the + // mNetworksAvailableNotificationShown is true, the notification may + // have actually been canceled. However, when it is false we know + // for sure that it is not being shown (it will not be shown any other + // place than here) + + // If it should be hidden and it is already hidden, then noop + if (!visible && !mNotificationShown && !force) { + return; + } + + NotificationManager notificationManager = (NotificationManager) mContext + .getSystemService(Context.NOTIFICATION_SERVICE); + + Message message; + if (visible) { + + // Not enough time has passed to show the notification again + if (System.currentTimeMillis() < mNotificationRepeatTime) { + return; + } + + if (mNotification == null) { + // Cache the Notification object. + mNotification = new Notification(); + mNotification.when = 0; + mNotification.icon = ICON_NETWORKS_AVAILABLE; + mNotification.flags = Notification.FLAG_AUTO_CANCEL; + mNotification.contentIntent = TaskStackBuilder.create(mContext) + .addNextIntentWithParentStack( + new Intent(WifiManager.ACTION_PICK_WIFI_NETWORK)) + .getPendingIntent(0, 0, null, UserHandle.CURRENT); + } + + CharSequence title = mContext.getResources().getQuantityText( + com.android.internal.R.plurals.wifi_available, numNetworks); + CharSequence details = mContext.getResources().getQuantityText( + com.android.internal.R.plurals.wifi_available_detailed, numNetworks); + mNotification.tickerText = title; + mNotification.setLatestEventInfo(mContext, title, details, mNotification.contentIntent); + + mNotificationRepeatTime = System.currentTimeMillis() + NOTIFICATION_REPEAT_DELAY_MS; + + notificationManager.notifyAsUser(null, ICON_NETWORKS_AVAILABLE, mNotification, + UserHandle.ALL); + } else { + notificationManager.cancelAsUser(null, ICON_NETWORKS_AVAILABLE, UserHandle.ALL); + } + + mNotificationShown = visible; + } + + void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + pw.println("mNotificationEnabled " + mNotificationEnabled); + pw.println("mNotificationRepeatTime " + mNotificationRepeatTime); + pw.println("mNotificationShown " + mNotificationShown); + pw.println("mNumScansSinceNetworkStateChange " + mNumScansSinceNetworkStateChange); + } + + private class NotificationEnabledSettingObserver extends ContentObserver { + public NotificationEnabledSettingObserver(Handler handler) { + super(handler); + } + + public void register() { + ContentResolver cr = mContext.getContentResolver(); + cr.registerContentObserver(Settings.Global.getUriFor( + Settings.Global.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON), true, this); + synchronized (WifiNotificationController.this) { + mNotificationEnabled = getValue(); + } + } + + @Override + public void onChange(boolean selfChange) { + super.onChange(selfChange); + + synchronized (WifiNotificationController.this) { + mNotificationEnabled = getValue(); + resetNotification(); + } + } + + private boolean getValue() { + return Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 1) == 1; + } + } + +} diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/wifi/WifiService.java index ad6eb4d..3c14e3d 100644 --- a/services/java/com/android/server/WifiService.java +++ b/services/java/com/android/server/wifi/WifiService.java @@ -14,45 +14,33 @@ * limitations under the License. */ -package com.android.server; +package com.android.server.wifi; import android.app.ActivityManager; import android.app.AlarmManager; import android.app.AppOpsManager; -import android.app.Notification; -import android.app.NotificationManager; import android.app.PendingIntent; -import android.app.TaskStackBuilder; import android.bluetooth.BluetoothAdapter; import android.content.BroadcastReceiver; -import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; -import android.database.ContentObserver; import android.net.wifi.IWifiManager; import android.net.wifi.ScanResult; -import android.net.wifi.SupplicantState; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.net.wifi.WifiStateMachine; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiWatchdogStateMachine; -import android.net.wifi.WifiConfiguration.KeyMgmt; -import android.net.wifi.WpsInfo; -import android.net.wifi.WpsResult; import android.net.ConnectivityManager; import android.net.DhcpInfo; import android.net.DhcpResults; import android.net.LinkAddress; -import android.net.LinkProperties; import android.net.NetworkInfo; -import android.net.NetworkInfo.State; import android.net.NetworkInfo.DetailedState; import android.net.NetworkUtils; import android.net.RouteInfo; -import android.net.TrafficStats; import android.os.Binder; import android.os.Handler; import android.os.Messenger; @@ -61,12 +49,10 @@ import android.os.IBinder; import android.os.INetworkManagementService; import android.os.Message; import android.os.RemoteException; -import android.os.ServiceManager; import android.os.SystemProperties; import android.os.UserHandle; import android.os.WorkSource; import android.provider.Settings; -import android.text.TextUtils; import android.util.Log; import android.util.Slog; @@ -75,11 +61,7 @@ import java.io.PrintWriter; import java.net.InetAddress; import java.net.Inet4Address; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Set; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicBoolean; import com.android.internal.app.IBatteryStats; import com.android.internal.telephony.TelephonyIntents; @@ -97,13 +79,13 @@ import com.android.internal.R; // as a SM to track soft AP/client/adhoc bring up based // on device idle state, airplane mode and boot. -public class WifiService extends IWifiManager.Stub { +public final class WifiService extends IWifiManager.Stub { private static final String TAG = "WifiService"; private static final boolean DBG = false; private final WifiStateMachine mWifiStateMachine; - private Context mContext; + private final Context mContext; private AlarmManager mAlarmManager; private PendingIntent mIdleIntent; @@ -130,19 +112,14 @@ public class WifiService extends IWifiManager.Stub { private final IBatteryStats mBatteryStats; private final AppOpsManager mAppOps; - private boolean mEnableTrafficStatsPoll = false; - private int mTrafficStatsPollToken = 0; - private long mTxPkts; - private long mRxPkts; - /* Tracks last reported data activity */ - private int mDataActivity; private String mInterfaceName; - /** - * Interval in milliseconds between polling for traffic - * statistics - */ - private static final int POLL_TRAFFIC_STATS_INTERVAL_MSECS = 1000; + /* Tracks the open wi-fi network notification */ + private WifiNotificationController mNotificationController; + /* Polls traffic stats and notifies clients */ + private WifiTrafficPoller mTrafficPoller; + /* Tracks the persisted states for wi-fi & airplane mode */ + private WifiSettingsStore mSettingsStore; /** * See {@link Settings.Global#WIFI_IDLE_MS}. This is the default value if a @@ -156,78 +133,14 @@ public class WifiService extends IWifiManager.Stub { private static final String ACTION_DEVICE_IDLE = "com.android.server.WifiManager.action.DEVICE_IDLE"; - private static final int WIFI_DISABLED = 0; - private static final int WIFI_ENABLED = 1; - /* Wifi enabled while in airplane mode */ - private static final int WIFI_ENABLED_AIRPLANE_OVERRIDE = 2; - /* Wifi disabled due to airplane mode on */ - private static final int WIFI_DISABLED_AIRPLANE_ON = 3; - - /* Persisted state that tracks the wifi & airplane interaction from settings */ - private AtomicInteger mPersistWifiState = new AtomicInteger(WIFI_DISABLED); - /* Tracks current airplane mode state */ - private AtomicBoolean mAirplaneModeOn = new AtomicBoolean(false); - /* Tracks whether wifi is enabled from WifiStateMachine's perspective */ - private boolean mWifiEnabled; - /* The work source (UID) that triggered the current WIFI scan, synchronized * on this */ private WorkSource mScanWorkSource; private boolean mIsReceiverRegistered = false; - NetworkInfo mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0, "WIFI", ""); - // Variables relating to the 'available networks' notification - /** - * The icon to show in the 'available networks' notification. This will also - * be the ID of the Notification given to the NotificationManager. - */ - private static final int ICON_NETWORKS_AVAILABLE = - com.android.internal.R.drawable.stat_notify_wifi_in_range; - /** - * When a notification is shown, we wait this amount before possibly showing it again. - */ - private final long NOTIFICATION_REPEAT_DELAY_MS; - /** - * Whether the user has set the setting to show the 'available networks' notification. - */ - private boolean mNotificationEnabled; - /** - * Observes the user setting to keep {@link #mNotificationEnabled} in sync. - */ - private NotificationEnabledSettingObserver mNotificationEnabledSettingObserver; - /** - * The {@link System#currentTimeMillis()} must be at least this value for us - * to show the notification again. - */ - private long mNotificationRepeatTime; - /** - * The Notification object given to the NotificationManager. - */ - private Notification mNotification; - /** - * Whether the notification is being shown, as set by us. That is, if the - * user cancels the notification, we will not receive the callback so this - * will still be true. We only guarantee if this is false, then the - * notification is not showing. - */ - private boolean mNotificationShown; - /** - * The number of continuous scans that must occur before consider the - * supplicant in a scanning state. This allows supplicant to associate with - * remembered networks that are in the scan results. - */ - private static final int NUM_SCANS_BEFORE_ACTUALLY_SCANNING = 3; - /** - * The number of scans since the last network state change. When this - * exceeds {@link #NUM_SCANS_BEFORE_ACTUALLY_SCANNING}, we consider the - * supplicant to actually be scanning. When the network state changes to - * something other than scanning, we reset this to 0. - */ - private int mNumScansSinceNetworkStateChange; - /** * Asynchronous channel to WifiStateMachine */ @@ -241,9 +154,9 @@ public class WifiService extends IWifiManager.Stub { /** * Handles client connections */ - private class AsyncServiceHandler extends Handler { + private class ClientHandler extends Handler { - AsyncServiceHandler(android.os.Looper looper) { + ClientHandler(android.os.Looper looper) { super(looper); } @@ -273,48 +186,13 @@ public class WifiService extends IWifiManager.Stub { ac.connect(mContext, this, msg.replyTo); break; } - case WifiManager.ENABLE_TRAFFIC_STATS_POLL: { - mEnableTrafficStatsPoll = (msg.arg1 == 1); - mTrafficStatsPollToken++; - if (mEnableTrafficStatsPoll) { - notifyOnDataActivity(); - sendMessageDelayed(Message.obtain(this, WifiManager.TRAFFIC_STATS_POLL, - mTrafficStatsPollToken, 0), POLL_TRAFFIC_STATS_INTERVAL_MSECS); - } - break; - } - case WifiManager.TRAFFIC_STATS_POLL: { - if (msg.arg1 == mTrafficStatsPollToken) { - notifyOnDataActivity(); - sendMessageDelayed(Message.obtain(this, WifiManager.TRAFFIC_STATS_POLL, - mTrafficStatsPollToken, 0), POLL_TRAFFIC_STATS_INTERVAL_MSECS); - } - break; - } - case WifiManager.CONNECT_NETWORK: { - mWifiStateMachine.sendMessage(Message.obtain(msg)); - break; - } - case WifiManager.SAVE_NETWORK: { - mWifiStateMachine.sendMessage(Message.obtain(msg)); - break; - } - case WifiManager.FORGET_NETWORK: { - mWifiStateMachine.sendMessage(Message.obtain(msg)); - break; - } - case WifiManager.START_WPS: { - mWifiStateMachine.sendMessage(Message.obtain(msg)); - break; - } - case WifiManager.CANCEL_WPS: { - mWifiStateMachine.sendMessage(Message.obtain(msg)); - break; - } - case WifiManager.DISABLE_NETWORK: { - mWifiStateMachine.sendMessage(Message.obtain(msg)); - break; - } + /* Client commands are forwarded to state machine */ + case WifiManager.CONNECT_NETWORK: + case WifiManager.SAVE_NETWORK: + case WifiManager.FORGET_NETWORK: + case WifiManager.START_WPS: + case WifiManager.CANCEL_WPS: + case WifiManager.DISABLE_NETWORK: case WifiManager.RSSI_PKTCNT_FETCH: { mWifiStateMachine.sendMessage(Message.obtain(msg)); break; @@ -326,7 +204,7 @@ public class WifiService extends IWifiManager.Stub { } } } - private AsyncServiceHandler mAsyncServiceHandler; + private ClientHandler mClientHandler; /** * Handles interaction with WifiStateMachine @@ -375,7 +253,7 @@ public class WifiService extends IWifiManager.Stub { private final WorkSource mTmpWorkSource = new WorkSource(); private WifiWatchdogStateMachine mWifiWatchdogStateMachine; - WifiService(Context context) { + public WifiService(Context context) { mContext = context; mInterfaceName = SystemProperties.get("wifi.interface", "wlan0"); @@ -389,65 +267,36 @@ public class WifiService extends IWifiManager.Stub { Intent idleIntent = new Intent(ACTION_DEVICE_IDLE, null); mIdleIntent = PendingIntent.getBroadcast(mContext, IDLE_REQUEST, idleIntent, 0); + mNotificationController = new WifiNotificationController(mContext, mWifiStateMachine); + mTrafficPoller = new WifiTrafficPoller(mContext, mClients, mInterfaceName); + mSettingsStore = new WifiSettingsStore(mContext); + mContext.registerReceiver( new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - mAirplaneModeOn.set(isAirplaneModeOn()); - handleAirplaneModeToggled(mAirplaneModeOn.get()); - updateWifiState(); + if (mSettingsStore.handleAirplaneModeToggled()) { + updateWifiState(); + } } }, new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED)); - IntentFilter filter = new IntentFilter(); - filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); - filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); - filter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION); - mContext.registerReceiver( new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - if (intent.getAction().equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) { - int wifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, - WifiManager.WIFI_STATE_DISABLED); - - mWifiEnabled = (wifiState == WifiManager.WIFI_STATE_ENABLED); - - // reset & clear notification on any wifi state change - resetNotification(); - } else if (intent.getAction().equals( - WifiManager.NETWORK_STATE_CHANGED_ACTION)) { - mNetworkInfo = (NetworkInfo) intent.getParcelableExtra( - WifiManager.EXTRA_NETWORK_INFO); - // reset & clear notification on a network connect & disconnect - switch(mNetworkInfo.getDetailedState()) { - case CONNECTED: - case DISCONNECTED: - case CAPTIVE_PORTAL_CHECK: - evaluateTrafficStatsPolling(); - resetNotification(); - break; - } - } else if (intent.getAction().equals( + if (intent.getAction().equals( WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) { noteScanEnd(); - checkAndSetNotification(); } } - }, filter); + }, new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)); HandlerThread wifiThread = new HandlerThread("WifiService"); wifiThread.start(); - mAsyncServiceHandler = new AsyncServiceHandler(wifiThread.getLooper()); + mClientHandler = new ClientHandler(wifiThread.getLooper()); mWifiStateMachineHandler = new WifiStateMachineHandler(wifiThread.getLooper()); - - // Setting is in seconds - NOTIFICATION_REPEAT_DELAY_MS = Settings.Global.getInt(context.getContentResolver(), - Settings.Global.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, 900) * 1000l; - mNotificationEnabledSettingObserver = new NotificationEnabledSettingObserver(new Handler()); - mNotificationEnabledSettingObserver.register(); } /** Tell battery stats about a new WIFI scan */ @@ -495,10 +344,8 @@ public class WifiService extends IWifiManager.Stub { * This function is used only at boot time */ public void checkAndStartWifi() { - mAirplaneModeOn.set(isAirplaneModeOn()); - mPersistWifiState.set(getPersistedWifiState()); - /* Start if Wi-Fi should be enabled or the saved state indicates Wi-Fi was on */ - boolean wifiEnabled = shouldWifiBeEnabled() || testAndClearWifiSavedState(); + /* Check if wi-fi needs to be enabled */ + boolean wifiEnabled = mSettingsStore.shouldWifiBeEnabled(); Slog.i(TAG, "WifiService starting up with Wi-Fi " + (wifiEnabled ? "enabled" : "disabled")); @@ -511,75 +358,6 @@ public class WifiService extends IWifiManager.Stub { } - private boolean testAndClearWifiSavedState() { - final ContentResolver cr = mContext.getContentResolver(); - int wifiSavedState = 0; - try { - wifiSavedState = Settings.Global.getInt(cr, Settings.Global.WIFI_SAVED_STATE); - if(wifiSavedState == 1) - Settings.Global.putInt(cr, Settings.Global.WIFI_SAVED_STATE, 0); - } catch (Settings.SettingNotFoundException e) { - ; - } - return (wifiSavedState == 1); - } - - private int getPersistedWifiState() { - final ContentResolver cr = mContext.getContentResolver(); - try { - return Settings.Global.getInt(cr, Settings.Global.WIFI_ON); - } catch (Settings.SettingNotFoundException e) { - Settings.Global.putInt(cr, Settings.Global.WIFI_ON, WIFI_DISABLED); - return WIFI_DISABLED; - } - } - - private boolean shouldWifiBeEnabled() { - if (mAirplaneModeOn.get()) { - return mPersistWifiState.get() == WIFI_ENABLED_AIRPLANE_OVERRIDE; - } else { - return mPersistWifiState.get() != WIFI_DISABLED; - } - } - - private void handleWifiToggled(boolean wifiEnabled) { - boolean airplaneEnabled = mAirplaneModeOn.get() && isAirplaneToggleable(); - if (wifiEnabled) { - if (airplaneEnabled) { - persistWifiState(WIFI_ENABLED_AIRPLANE_OVERRIDE); - } else { - persistWifiState(WIFI_ENABLED); - } - } else { - // When wifi state is disabled, we do not care - // if airplane mode is on or not. The scenario of - // wifi being disabled due to airplane mode being turned on - // is handled handleAirplaneModeToggled() - persistWifiState(WIFI_DISABLED); - } - } - - private void handleAirplaneModeToggled(boolean airplaneEnabled) { - if (airplaneEnabled) { - // Wifi disabled due to airplane on - if (mWifiEnabled) { - persistWifiState(WIFI_DISABLED_AIRPLANE_ON); - } - } else { - /* On airplane mode disable, restore wifi state if necessary */ - if (testAndClearWifiSavedState() || - mPersistWifiState.get() == WIFI_ENABLED_AIRPLANE_OVERRIDE) { - persistWifiState(WIFI_ENABLED); - } - } - } - - private void persistWifiState(int state) { - final ContentResolver cr = mContext.getContentResolver(); - mPersistWifiState.set(state); - Settings.Global.putInt(cr, Settings.Global.WIFI_ON, state); - } - /** * see {@link android.net.wifi.WifiManager#pingSupplicant()} * @return {@code true} if the operation succeeds, {@code false} otherwise @@ -640,24 +418,28 @@ public class WifiService extends IWifiManager.Stub { Slog.e(TAG, "Invoking mWifiStateMachine.setWifiEnabled\n"); } - if (enable) { - reportStartWorkSource(); - } - mWifiStateMachine.setWifiEnabled(enable); - /* - * Caller might not have WRITE_SECURE_SETTINGS, - * only CHANGE_WIFI_STATE is enforced - */ + * Caller might not have WRITE_SECURE_SETTINGS, + * only CHANGE_WIFI_STATE is enforced + */ long ident = Binder.clearCallingIdentity(); try { - handleWifiToggled(enable); + if (! mSettingsStore.handleWifiToggled(enable)) { + // Nothing to do if wifi cannot be toggled + return true; + } } finally { Binder.restoreCallingIdentity(ident); } if (enable) { + reportStartWorkSource(); + } + + mWifiStateMachine.setWifiEnabled(enable); + + if (enable) { if (!mIsReceiverRegistered) { registerForBroadcasts(); mIsReceiverRegistered = true; @@ -1047,7 +829,7 @@ public class WifiService extends IWifiManager.Stub { public Messenger getWifiServiceMessenger() { enforceAccessPermission(); enforceChangePermission(); - return new Messenger(mAsyncServiceHandler); + return new Messenger(mClientHandler); } /** Get a reference to WifiStateMachine handler for AsyncChannel communication */ @@ -1082,14 +864,12 @@ public class WifiService extends IWifiManager.Stub { } mAlarmManager.cancel(mIdleIntent); mScreenOff = false; - evaluateTrafficStatsPolling(); setDeviceIdleAndUpdateWifi(false); } else if (action.equals(Intent.ACTION_SCREEN_OFF)) { if (DBG) { Slog.d(TAG, "ACTION_SCREEN_OFF"); } mScreenOff = true; - evaluateTrafficStatsPolling(); /* * Set a timer to put Wi-Fi to sleep, but only if the screen is off * AND the "stay on while plugged in" setting doesn't match the @@ -1221,11 +1001,11 @@ public class WifiService extends IWifiManager.Stub { } /* Disable tethering when airplane mode is enabled */ - if (mAirplaneModeOn.get()) { + if (mSettingsStore.isAirplaneModeOn()) { mWifiStateMachine.setWifiApEnabled(null, false); } - if (shouldWifiBeEnabled()) { + if (mSettingsStore.shouldWifiBeEnabled()) { if (wifiShouldBeStarted) { reportStartWorkSource(); mWifiStateMachine.setWifiEnabled(true); @@ -1253,30 +1033,6 @@ public class WifiService extends IWifiManager.Stub { mContext.registerReceiver(mReceiver, intentFilter); } - private boolean isAirplaneSensitive() { - String airplaneModeRadios = Settings.Global.getString(mContext.getContentResolver(), - Settings.Global.AIRPLANE_MODE_RADIOS); - return airplaneModeRadios == null - || airplaneModeRadios.contains(Settings.Global.RADIO_WIFI); - } - - private boolean isAirplaneToggleable() { - String toggleableRadios = Settings.Global.getString(mContext.getContentResolver(), - Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS); - return toggleableRadios != null - && toggleableRadios.contains(Settings.Global.RADIO_WIFI); - } - - /** - * Returns true if Wi-Fi is sensitive to airplane mode, and airplane mode is - * currently on. - * @return {@code true} if airplane mode is on. - */ - private boolean isAirplaneModeOn() { - return isAirplaneSensitive() && Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.AIRPLANE_MODE_ON, 0) == 1; - } - @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) @@ -1296,18 +1052,9 @@ public class WifiService extends IWifiManager.Stub { pw.println("mEmergencyCallbackMode " + mEmergencyCallbackMode); pw.println("mMulticastEnabled " + mMulticastEnabled); pw.println("mMulticastDisabled " + mMulticastDisabled); - pw.println("mEnableTrafficStatsPoll " + mEnableTrafficStatsPoll); - pw.println("mTrafficStatsPollToken " + mTrafficStatsPollToken); - pw.println("mTxPkts " + mTxPkts); - pw.println("mRxPkts " + mRxPkts); - pw.println("mDataActivity " + mDataActivity); - pw.println("mPersistWifiState " + mPersistWifiState.get()); - pw.println("mAirplaneModeOn " + mAirplaneModeOn.get()); - pw.println("mWifiEnabled " + mWifiEnabled); - pw.println("mNotificationEnabled " + mNotificationEnabled); - pw.println("mNotificationRepeatTime " + mNotificationRepeatTime); - pw.println("mNotificationShown " + mNotificationShown); - pw.println("mNumScansSinceNetworkStateChange " + mNumScansSinceNetworkStateChange); + mSettingsStore.dump(fd, pw, args); + mNotificationController.dump(fd, pw, args); + mTrafficPoller.dump(fd, pw, args); pw.println("Latest scan results:"); List<ScanResult> scanResults = mWifiStateMachine.syncGetScanResultsList(); @@ -1700,194 +1447,4 @@ public class WifiService extends IWifiManager.Stub { return (mMulticasters.size() > 0); } } - - /** - * Evaluate if traffic stats polling is needed based on - * connection and screen on status - */ - private void evaluateTrafficStatsPolling() { - Message msg; - if (mNetworkInfo.getDetailedState() == DetailedState.CONNECTED && !mScreenOff) { - msg = Message.obtain(mAsyncServiceHandler, - WifiManager.ENABLE_TRAFFIC_STATS_POLL, 1, 0); - } else { - msg = Message.obtain(mAsyncServiceHandler, - WifiManager.ENABLE_TRAFFIC_STATS_POLL, 0, 0); - } - msg.sendToTarget(); - } - - private void notifyOnDataActivity() { - long sent, received; - long preTxPkts = mTxPkts, preRxPkts = mRxPkts; - int dataActivity = WifiManager.DATA_ACTIVITY_NONE; - - mTxPkts = TrafficStats.getTxPackets(mInterfaceName); - mRxPkts = TrafficStats.getRxPackets(mInterfaceName); - - if (preTxPkts > 0 || preRxPkts > 0) { - sent = mTxPkts - preTxPkts; - received = mRxPkts - preRxPkts; - if (sent > 0) { - dataActivity |= WifiManager.DATA_ACTIVITY_OUT; - } - if (received > 0) { - dataActivity |= WifiManager.DATA_ACTIVITY_IN; - } - - if (dataActivity != mDataActivity && !mScreenOff) { - mDataActivity = dataActivity; - for (AsyncChannel client : mClients) { - client.sendMessage(WifiManager.DATA_ACTIVITY_NOTIFICATION, mDataActivity); - } - } - } - } - - - private void checkAndSetNotification() { - // If we shouldn't place a notification on available networks, then - // don't bother doing any of the following - if (!mNotificationEnabled) return; - - State state = mNetworkInfo.getState(); - if ((state == NetworkInfo.State.DISCONNECTED) - || (state == NetworkInfo.State.UNKNOWN)) { - // Look for an open network - List<ScanResult> scanResults = mWifiStateMachine.syncGetScanResultsList(); - if (scanResults != null) { - int numOpenNetworks = 0; - for (int i = scanResults.size() - 1; i >= 0; i--) { - ScanResult scanResult = scanResults.get(i); - - //A capability of [ESS] represents an open access point - //that is available for an STA to connect - if (scanResult.capabilities != null && - scanResult.capabilities.equals("[ESS]")) { - numOpenNetworks++; - } - } - - if (numOpenNetworks > 0) { - if (++mNumScansSinceNetworkStateChange >= NUM_SCANS_BEFORE_ACTUALLY_SCANNING) { - /* - * We've scanned continuously at least - * NUM_SCANS_BEFORE_NOTIFICATION times. The user - * probably does not have a remembered network in range, - * since otherwise supplicant would have tried to - * associate and thus resetting this counter. - */ - setNotificationVisible(true, numOpenNetworks, false, 0); - } - return; - } - } - } - - // No open networks in range, remove the notification - setNotificationVisible(false, 0, false, 0); - } - - /** - * Clears variables related to tracking whether a notification has been - * shown recently and clears the current notification. - */ - private void resetNotification() { - mNotificationRepeatTime = 0; - mNumScansSinceNetworkStateChange = 0; - setNotificationVisible(false, 0, false, 0); - } - - /** - * Display or don't display a notification that there are open Wi-Fi networks. - * @param visible {@code true} if notification should be visible, {@code false} otherwise - * @param numNetworks the number networks seen - * @param force {@code true} to force notification to be shown/not-shown, - * even if it is already shown/not-shown. - * @param delay time in milliseconds after which the notification should be made - * visible or invisible. - */ - private void setNotificationVisible(boolean visible, int numNetworks, boolean force, - int delay) { - - // Since we use auto cancel on the notification, when the - // mNetworksAvailableNotificationShown is true, the notification may - // have actually been canceled. However, when it is false we know - // for sure that it is not being shown (it will not be shown any other - // place than here) - - // If it should be hidden and it is already hidden, then noop - if (!visible && !mNotificationShown && !force) { - return; - } - - NotificationManager notificationManager = (NotificationManager) mContext - .getSystemService(Context.NOTIFICATION_SERVICE); - - Message message; - if (visible) { - - // Not enough time has passed to show the notification again - if (System.currentTimeMillis() < mNotificationRepeatTime) { - return; - } - - if (mNotification == null) { - // Cache the Notification object. - mNotification = new Notification(); - mNotification.when = 0; - mNotification.icon = ICON_NETWORKS_AVAILABLE; - mNotification.flags = Notification.FLAG_AUTO_CANCEL; - mNotification.contentIntent = TaskStackBuilder.create(mContext) - .addNextIntentWithParentStack( - new Intent(WifiManager.ACTION_PICK_WIFI_NETWORK)) - .getPendingIntent(0, 0, null, UserHandle.CURRENT); - } - - CharSequence title = mContext.getResources().getQuantityText( - com.android.internal.R.plurals.wifi_available, numNetworks); - CharSequence details = mContext.getResources().getQuantityText( - com.android.internal.R.plurals.wifi_available_detailed, numNetworks); - mNotification.tickerText = title; - mNotification.setLatestEventInfo(mContext, title, details, mNotification.contentIntent); - - mNotificationRepeatTime = System.currentTimeMillis() + NOTIFICATION_REPEAT_DELAY_MS; - - notificationManager.notifyAsUser(null, ICON_NETWORKS_AVAILABLE, mNotification, - UserHandle.ALL); - } else { - notificationManager.cancelAsUser(null, ICON_NETWORKS_AVAILABLE, UserHandle.ALL); - } - - mNotificationShown = visible; - } - - private class NotificationEnabledSettingObserver extends ContentObserver { - - public NotificationEnabledSettingObserver(Handler handler) { - super(handler); - } - - public void register() { - ContentResolver cr = mContext.getContentResolver(); - cr.registerContentObserver(Settings.Global.getUriFor( - Settings.Global.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON), true, this); - mNotificationEnabled = getValue(); - } - - @Override - public void onChange(boolean selfChange) { - super.onChange(selfChange); - - mNotificationEnabled = getValue(); - resetNotification(); - } - - private boolean getValue() { - return Settings.Global.getInt(mContext.getContentResolver(), - Settings.Global.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 1) == 1; - } - } - - } diff --git a/services/java/com/android/server/wifi/WifiSettingsStore.java b/services/java/com/android/server/wifi/WifiSettingsStore.java new file mode 100644 index 0000000..d7c8752 --- /dev/null +++ b/services/java/com/android/server/wifi/WifiSettingsStore.java @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wifi; + +import android.content.ContentResolver; +import android.content.Context; +import android.provider.Settings; + +import java.io.FileDescriptor; +import java.io.PrintWriter; + +/* Tracks persisted settings for Wi-Fi and airplane mode interaction */ +final class WifiSettingsStore { + /* Values tracked in Settings.Global.WIFI_ON */ + private static final int WIFI_DISABLED = 0; + private static final int WIFI_ENABLED = 1; + /* Wifi enabled while in airplane mode */ + private static final int WIFI_ENABLED_AIRPLANE_OVERRIDE = 2; + /* Wifi disabled due to airplane mode on */ + private static final int WIFI_DISABLED_AIRPLANE_ON = 3; + + /* Persisted state that tracks the wifi & airplane interaction from settings */ + private int mPersistWifiState = WIFI_DISABLED; + /* Tracks current airplane mode state */ + private boolean mAirplaneModeOn = false; + /* Tracks whether wifi is enabled from WifiStateMachine's perspective */ + private final Context mContext; + + /* Tracks if we have checked the saved wi-fi state after boot */ + private boolean mCheckSavedStateAtBoot = false; + + WifiSettingsStore(Context context) { + mContext = context; + mAirplaneModeOn = getPersistedAirplaneModeOn(); + mPersistWifiState = getPersistedWifiState(); + } + + synchronized boolean shouldWifiBeEnabled() { + if (!mCheckSavedStateAtBoot) { + mCheckSavedStateAtBoot = true; + if (testAndClearWifiSavedState()) return true; + } + + if (mAirplaneModeOn) { + return mPersistWifiState == WIFI_ENABLED_AIRPLANE_OVERRIDE; + } else { + return mPersistWifiState != WIFI_DISABLED; + } + } + + /** + * Returns true if airplane mode is currently on. + * @return {@code true} if airplane mode is on. + */ + synchronized boolean isAirplaneModeOn() { + return mAirplaneModeOn; + } + + synchronized boolean handleWifiToggled(boolean wifiEnabled) { + // Can Wi-Fi be toggled in airplane mode ? + if (mAirplaneModeOn && !isAirplaneToggleable()) { + return false; + } + + if (wifiEnabled) { + if (mAirplaneModeOn) { + persistWifiState(WIFI_ENABLED_AIRPLANE_OVERRIDE); + } else { + persistWifiState(WIFI_ENABLED); + } + } else { + // When wifi state is disabled, we do not care + // if airplane mode is on or not. The scenario of + // wifi being disabled due to airplane mode being turned on + // is handled handleAirplaneModeToggled() + persistWifiState(WIFI_DISABLED); + } + return true; + } + + synchronized boolean handleAirplaneModeToggled() { + // Is Wi-Fi sensitive to airplane mode changes ? + if (!isAirplaneSensitive()) { + return false; + } + + mAirplaneModeOn = getPersistedAirplaneModeOn(); + if (mAirplaneModeOn) { + // Wifi disabled due to airplane on + if (mPersistWifiState == WIFI_ENABLED) { + persistWifiState(WIFI_DISABLED_AIRPLANE_ON); + } + } else { + /* On airplane mode disable, restore wifi state if necessary */ + if (testAndClearWifiSavedState() || + mPersistWifiState == WIFI_ENABLED_AIRPLANE_OVERRIDE) { + persistWifiState(WIFI_ENABLED); + } + } + return true; + } + + void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + pw.println("mPersistWifiState " + mPersistWifiState); + pw.println("mAirplaneModeOn " + mAirplaneModeOn); + } + + private void persistWifiState(int state) { + final ContentResolver cr = mContext.getContentResolver(); + mPersistWifiState = state; + Settings.Global.putInt(cr, Settings.Global.WIFI_ON, state); + } + + /* Does Wi-Fi need to be disabled when airplane mode is on ? */ + private boolean isAirplaneSensitive() { + String airplaneModeRadios = Settings.Global.getString(mContext.getContentResolver(), + Settings.Global.AIRPLANE_MODE_RADIOS); + return airplaneModeRadios == null + || airplaneModeRadios.contains(Settings.Global.RADIO_WIFI); + } + + /* Is Wi-Fi allowed to be re-enabled while airplane mode is on ? */ + private boolean isAirplaneToggleable() { + String toggleableRadios = Settings.Global.getString(mContext.getContentResolver(), + Settings.Global.AIRPLANE_MODE_TOGGLEABLE_RADIOS); + return toggleableRadios != null + && toggleableRadios.contains(Settings.Global.RADIO_WIFI); + } + + /* After a reboot, we restore wi-fi to be on if it was turned off temporarily for tethering. + * The settings app tracks the saved state, but the framework has to check it at boot to + * make sure the wi-fi is turned on in case it was turned off for the purpose of tethering. + * + * Note that this is not part of the regular WIFI_ON setting because this only needs to + * be controlled through the settings app and not the Wi-Fi public API. + */ + private boolean testAndClearWifiSavedState() { + final ContentResolver cr = mContext.getContentResolver(); + int wifiSavedState = 0; + try { + wifiSavedState = Settings.Global.getInt(cr, Settings.Global.WIFI_SAVED_STATE); + if(wifiSavedState == 1) + Settings.Global.putInt(cr, Settings.Global.WIFI_SAVED_STATE, 0); + } catch (Settings.SettingNotFoundException e) { + ; + } + return (wifiSavedState == 1); + } + + private int getPersistedWifiState() { + final ContentResolver cr = mContext.getContentResolver(); + try { + return Settings.Global.getInt(cr, Settings.Global.WIFI_ON); + } catch (Settings.SettingNotFoundException e) { + Settings.Global.putInt(cr, Settings.Global.WIFI_ON, WIFI_DISABLED); + return WIFI_DISABLED; + } + } + + private boolean getPersistedAirplaneModeOn() { + return Settings.Global.getInt(mContext.getContentResolver(), + Settings.Global.AIRPLANE_MODE_ON, 0) == 1; + } +} diff --git a/services/java/com/android/server/wifi/WifiTrafficPoller.java b/services/java/com/android/server/wifi/WifiTrafficPoller.java new file mode 100644 index 0000000..3fcb6c1 --- /dev/null +++ b/services/java/com/android/server/wifi/WifiTrafficPoller.java @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wifi; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.net.NetworkInfo; +import static android.net.NetworkInfo.DetailedState.CONNECTED; +import android.net.TrafficStats; +import android.net.wifi.WifiManager; +import android.os.Handler; +import android.os.Message; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; + +import com.android.internal.util.AsyncChannel; + +/* Polls for traffic stats and notifies the clients */ +final class WifiTrafficPoller { + /** + * Interval in milliseconds between polling for traffic + * statistics + */ + private static final int POLL_TRAFFIC_STATS_INTERVAL_MSECS = 1000; + + private boolean mEnableTrafficStatsPoll = false; + private int mTrafficStatsPollToken = 0; + private long mTxPkts; + private long mRxPkts; + /* Tracks last reported data activity */ + private int mDataActivity; + + private final List<AsyncChannel> mClients; + // err on the side of updating at boot since screen on broadcast may be missed + // the first time + private AtomicBoolean mScreenOn = new AtomicBoolean(true); + private final TrafficHandler mTrafficHandler; + private NetworkInfo mNetworkInfo; + private final String mInterface; + + WifiTrafficPoller(Context context, List<AsyncChannel> clients, String iface) { + mClients = clients; + mInterface = iface; + mTrafficHandler = new TrafficHandler(); + + IntentFilter filter = new IntentFilter(); + filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); + filter.addAction(Intent.ACTION_SCREEN_OFF); + filter.addAction(Intent.ACTION_SCREEN_ON); + + context.registerReceiver( + new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (intent.getAction().equals( + WifiManager.NETWORK_STATE_CHANGED_ACTION)) { + mNetworkInfo = (NetworkInfo) intent.getParcelableExtra( + WifiManager.EXTRA_NETWORK_INFO); + } else if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) { + mScreenOn.set(false); + } else if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) { + mScreenOn.set(true); + } + evaluateTrafficStatsPolling(); + } + }, filter); + } + + + private class TrafficHandler extends Handler { + public void handleMessage(Message msg) { + switch (msg.what) { + case WifiManager.ENABLE_TRAFFIC_STATS_POLL: { + mEnableTrafficStatsPoll = (msg.arg1 == 1); + mTrafficStatsPollToken++; + if (mEnableTrafficStatsPoll) { + notifyOnDataActivity(); + sendMessageDelayed(Message.obtain(this, WifiManager.TRAFFIC_STATS_POLL, + mTrafficStatsPollToken, 0), POLL_TRAFFIC_STATS_INTERVAL_MSECS); + } + break; + } + case WifiManager.TRAFFIC_STATS_POLL: { + if (msg.arg1 == mTrafficStatsPollToken) { + notifyOnDataActivity(); + sendMessageDelayed(Message.obtain(this, WifiManager.TRAFFIC_STATS_POLL, + mTrafficStatsPollToken, 0), POLL_TRAFFIC_STATS_INTERVAL_MSECS); + } + break; + } + } + + } + } + + private void evaluateTrafficStatsPolling() { + Message msg; + if (mNetworkInfo == null) return; + if (mNetworkInfo.getDetailedState() == CONNECTED && mScreenOn.get()) { + msg = Message.obtain(mTrafficHandler, + WifiManager.ENABLE_TRAFFIC_STATS_POLL, 1, 0); + } else { + msg = Message.obtain(mTrafficHandler, + WifiManager.ENABLE_TRAFFIC_STATS_POLL, 0, 0); + } + msg.sendToTarget(); + } + + private void notifyOnDataActivity() { + long sent, received; + long preTxPkts = mTxPkts, preRxPkts = mRxPkts; + int dataActivity = WifiManager.DATA_ACTIVITY_NONE; + + mTxPkts = TrafficStats.getTxPackets(mInterface); + mRxPkts = TrafficStats.getRxPackets(mInterface); + + if (preTxPkts > 0 || preRxPkts > 0) { + sent = mTxPkts - preTxPkts; + received = mRxPkts - preRxPkts; + if (sent > 0) { + dataActivity |= WifiManager.DATA_ACTIVITY_OUT; + } + if (received > 0) { + dataActivity |= WifiManager.DATA_ACTIVITY_IN; + } + + if (dataActivity != mDataActivity && mScreenOn.get()) { + mDataActivity = dataActivity; + for (AsyncChannel client : mClients) { + client.sendMessage(WifiManager.DATA_ACTIVITY_NOTIFICATION, mDataActivity); + } + } + } + } + + void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + pw.println("mEnableTrafficStatsPoll " + mEnableTrafficStatsPoll); + pw.println("mTrafficStatsPollToken " + mTrafficStatsPollToken); + pw.println("mTxPkts " + mTxPkts); + pw.println("mRxPkts " + mRxPkts); + pw.println("mDataActivity " + mDataActivity); + } + +} |