1155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande/* 2155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande * Copyright (C) 2013 The Android Open Source Project 3155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande * 4155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande * Licensed under the Apache License, Version 2.0 (the "License"); 5155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande * you may not use this file except in compliance with the License. 6155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande * You may obtain a copy of the License at 7155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande * 8155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande * http://www.apache.org/licenses/LICENSE-2.0 9155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande * 10155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande * Unless required by applicable law or agreed to in writing, software 11155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande * distributed under the License is distributed on an "AS IS" BASIS, 12155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande * See the License for the specific language governing permissions and 14155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande * limitations under the License. 15155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande */ 16155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande 17155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpandepackage com.android.server.wifi; 18155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande 19155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpandeimport android.app.Notification; 20155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpandeimport android.app.NotificationManager; 21155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpandeimport android.app.TaskStackBuilder; 22155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpandeimport android.content.BroadcastReceiver; 23155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpandeimport android.content.ContentResolver; 24155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpandeimport android.content.Context; 25155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpandeimport android.content.Intent; 26155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpandeimport android.content.IntentFilter; 27155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpandeimport android.database.ContentObserver; 28155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpandeimport android.net.NetworkInfo; 29155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpandeimport android.net.wifi.ScanResult; 30155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpandeimport android.net.wifi.WifiManager; 31155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpandeimport android.os.Handler; 3240abf54c81c5624641543d86e1d7ab21ebe30175Paul Stewartimport android.os.Looper; 33155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpandeimport android.os.Message; 34155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpandeimport android.os.UserHandle; 35155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpandeimport android.provider.Settings; 36155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande 37155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpandeimport java.io.FileDescriptor; 38155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpandeimport java.io.PrintWriter; 39155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpandeimport java.util.List; 40155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande 41155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande/* Takes care of handling the "open wi-fi network available" notification @hide */ 42155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpandefinal class WifiNotificationController { 43155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande /** 44155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande * The icon to show in the 'available networks' notification. This will also 45155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande * be the ID of the Notification given to the NotificationManager. 46155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande */ 47155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande private static final int ICON_NETWORKS_AVAILABLE = 48155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande com.android.internal.R.drawable.stat_notify_wifi_in_range; 49155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande /** 50155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande * When a notification is shown, we wait this amount before possibly showing it again. 51155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande */ 52155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande private final long NOTIFICATION_REPEAT_DELAY_MS; 53155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande /** 54155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande * Whether the user has set the setting to show the 'available networks' notification. 55155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande */ 56155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande private boolean mNotificationEnabled; 57155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande /** 58155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande * Observes the user setting to keep {@link #mNotificationEnabled} in sync. 59155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande */ 60155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande private NotificationEnabledSettingObserver mNotificationEnabledSettingObserver; 61155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande /** 62155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande * The {@link System#currentTimeMillis()} must be at least this value for us 63155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande * to show the notification again. 64155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande */ 65155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande private long mNotificationRepeatTime; 66155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande /** 67155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande * The Notification object given to the NotificationManager. 68155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande */ 692247009c2527c80e7fb00f056aea67d1fe09671bChris Wren private Notification.Builder mNotificationBuilder; 70155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande /** 71155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande * Whether the notification is being shown, as set by us. That is, if the 72155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande * user cancels the notification, we will not receive the callback so this 73155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande * will still be true. We only guarantee if this is false, then the 74155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande * notification is not showing. 75155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande */ 76155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande private boolean mNotificationShown; 77155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande /** 78155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande * The number of continuous scans that must occur before consider the 79155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande * supplicant in a scanning state. This allows supplicant to associate with 80155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande * remembered networks that are in the scan results. 81155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande */ 82155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande private static final int NUM_SCANS_BEFORE_ACTUALLY_SCANNING = 3; 83155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande /** 84155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande * The number of scans since the last network state change. When this 85155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande * exceeds {@link #NUM_SCANS_BEFORE_ACTUALLY_SCANNING}, we consider the 86155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande * supplicant to actually be scanning. When the network state changes to 87155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande * something other than scanning, we reset this to 0. 88155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande */ 89155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande private int mNumScansSinceNetworkStateChange; 90155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande 91155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande private final Context mContext; 92155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande private final WifiStateMachine mWifiStateMachine; 93155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande private NetworkInfo mNetworkInfo; 94250e70cb113e75ac9b24b09bbf176804d37755d8Paul Stewart private NetworkInfo.DetailedState mDetailedState; 95155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande private volatile int mWifiState; 96250e70cb113e75ac9b24b09bbf176804d37755d8Paul Stewart private FrameworkFacade mFrameworkFacade; 97155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande 9840abf54c81c5624641543d86e1d7ab21ebe30175Paul Stewart WifiNotificationController(Context context, Looper looper, WifiStateMachine wsm, 9940abf54c81c5624641543d86e1d7ab21ebe30175Paul Stewart FrameworkFacade framework, Notification.Builder builder) { 100155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande mContext = context; 101155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande mWifiStateMachine = wsm; 102250e70cb113e75ac9b24b09bbf176804d37755d8Paul Stewart mFrameworkFacade = framework; 103250e70cb113e75ac9b24b09bbf176804d37755d8Paul Stewart mNotificationBuilder = builder; 104155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande mWifiState = WifiManager.WIFI_STATE_UNKNOWN; 105250e70cb113e75ac9b24b09bbf176804d37755d8Paul Stewart mDetailedState = NetworkInfo.DetailedState.IDLE; 106155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande 107155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande IntentFilter filter = new IntentFilter(); 108155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); 109155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION); 110155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande filter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION); 111155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande 112155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande mContext.registerReceiver( 113155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande new BroadcastReceiver() { 114155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande @Override 115155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande public void onReceive(Context context, Intent intent) { 116155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande if (intent.getAction().equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) { 117155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande mWifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, 118155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande WifiManager.WIFI_STATE_UNKNOWN); 119155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande resetNotification(); 120155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande } else if (intent.getAction().equals( 121155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande WifiManager.NETWORK_STATE_CHANGED_ACTION)) { 122155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande mNetworkInfo = (NetworkInfo) intent.getParcelableExtra( 123155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande WifiManager.EXTRA_NETWORK_INFO); 124250e70cb113e75ac9b24b09bbf176804d37755d8Paul Stewart NetworkInfo.DetailedState detailedState = 125250e70cb113e75ac9b24b09bbf176804d37755d8Paul Stewart mNetworkInfo.getDetailedState(); 126250e70cb113e75ac9b24b09bbf176804d37755d8Paul Stewart if (detailedState != NetworkInfo.DetailedState.SCANNING 127250e70cb113e75ac9b24b09bbf176804d37755d8Paul Stewart && detailedState != mDetailedState) { 128250e70cb113e75ac9b24b09bbf176804d37755d8Paul Stewart mDetailedState = detailedState; 129250e70cb113e75ac9b24b09bbf176804d37755d8Paul Stewart // reset & clear notification on a network connect & disconnect 130250e70cb113e75ac9b24b09bbf176804d37755d8Paul Stewart switch(mDetailedState) { 131250e70cb113e75ac9b24b09bbf176804d37755d8Paul Stewart case CONNECTED: 132250e70cb113e75ac9b24b09bbf176804d37755d8Paul Stewart case DISCONNECTED: 133250e70cb113e75ac9b24b09bbf176804d37755d8Paul Stewart case CAPTIVE_PORTAL_CHECK: 134250e70cb113e75ac9b24b09bbf176804d37755d8Paul Stewart resetNotification(); 135250e70cb113e75ac9b24b09bbf176804d37755d8Paul Stewart break; 136250e70cb113e75ac9b24b09bbf176804d37755d8Paul Stewart } 137155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande } 138155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande } else if (intent.getAction().equals( 139155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) { 140155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande checkAndSetNotification(mNetworkInfo, 141155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande mWifiStateMachine.syncGetScanResultsList()); 142155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande } 143155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande } 144155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande }, filter); 145155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande 146155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande // Setting is in seconds 147250e70cb113e75ac9b24b09bbf176804d37755d8Paul Stewart NOTIFICATION_REPEAT_DELAY_MS = mFrameworkFacade.getIntegerSetting(context, 148155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande Settings.Global.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, 900) * 1000l; 14940abf54c81c5624641543d86e1d7ab21ebe30175Paul Stewart mNotificationEnabledSettingObserver = new NotificationEnabledSettingObserver( 15040abf54c81c5624641543d86e1d7ab21ebe30175Paul Stewart new Handler(looper)); 151155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande mNotificationEnabledSettingObserver.register(); 152155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande } 153155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande 154155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande private synchronized void checkAndSetNotification(NetworkInfo networkInfo, 155155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande List<ScanResult> scanResults) { 156c5e82ad2600416d7eafdde5d1d02400ece28e48aVinit Deshpande 157155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande // TODO: unregister broadcast so we do not have to check here 158155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande // If we shouldn't place a notification on available networks, then 159155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande // don't bother doing any of the following 160155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande if (!mNotificationEnabled) return; 161155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande if (mWifiState != WifiManager.WIFI_STATE_ENABLED) return; 162155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande 163c5e82ad2600416d7eafdde5d1d02400ece28e48aVinit Deshpande NetworkInfo.State state = NetworkInfo.State.DISCONNECTED; 164c5e82ad2600416d7eafdde5d1d02400ece28e48aVinit Deshpande if (networkInfo != null) 165c5e82ad2600416d7eafdde5d1d02400ece28e48aVinit Deshpande state = networkInfo.getState(); 166c5e82ad2600416d7eafdde5d1d02400ece28e48aVinit Deshpande 167155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande if ((state == NetworkInfo.State.DISCONNECTED) 168155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande || (state == NetworkInfo.State.UNKNOWN)) { 169155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande if (scanResults != null) { 170155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande int numOpenNetworks = 0; 171155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande for (int i = scanResults.size() - 1; i >= 0; i--) { 172155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande ScanResult scanResult = scanResults.get(i); 173155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande 174155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande //A capability of [ESS] represents an open access point 175155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande //that is available for an STA to connect 176155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande if (scanResult.capabilities != null && 177155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande scanResult.capabilities.equals("[ESS]")) { 178155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande numOpenNetworks++; 179155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande } 180155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande } 181155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande 182155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande if (numOpenNetworks > 0) { 183155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande if (++mNumScansSinceNetworkStateChange >= NUM_SCANS_BEFORE_ACTUALLY_SCANNING) { 184155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande /* 185155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande * We've scanned continuously at least 186155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande * NUM_SCANS_BEFORE_NOTIFICATION times. The user 187155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande * probably does not have a remembered network in range, 188155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande * since otherwise supplicant would have tried to 189155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande * associate and thus resetting this counter. 190155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande */ 191155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande setNotificationVisible(true, numOpenNetworks, false, 0); 192155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande } 193155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande return; 194155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande } 195155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande } 196155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande } 197155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande 198155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande // No open networks in range, remove the notification 199155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande setNotificationVisible(false, 0, false, 0); 200155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande } 201155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande 202155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande /** 203155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande * Clears variables related to tracking whether a notification has been 204155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande * shown recently and clears the current notification. 205155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande */ 206155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande private synchronized void resetNotification() { 207155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande mNotificationRepeatTime = 0; 208155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande mNumScansSinceNetworkStateChange = 0; 209155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande setNotificationVisible(false, 0, false, 0); 210155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande } 211155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande 212155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande /** 213155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande * Display or don't display a notification that there are open Wi-Fi networks. 214155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande * @param visible {@code true} if notification should be visible, {@code false} otherwise 215155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande * @param numNetworks the number networks seen 216155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande * @param force {@code true} to force notification to be shown/not-shown, 217155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande * even if it is already shown/not-shown. 218155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande * @param delay time in milliseconds after which the notification should be made 219155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande * visible or invisible. 220155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande */ 221155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande private void setNotificationVisible(boolean visible, int numNetworks, boolean force, 222155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande int delay) { 223155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande 224155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande // Since we use auto cancel on the notification, when the 225155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande // mNetworksAvailableNotificationShown is true, the notification may 226155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande // have actually been canceled. However, when it is false we know 227155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande // for sure that it is not being shown (it will not be shown any other 228155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande // place than here) 229155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande 230155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande // If it should be hidden and it is already hidden, then noop 231155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande if (!visible && !mNotificationShown && !force) { 232155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande return; 233155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande } 234155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande 235155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande NotificationManager notificationManager = (NotificationManager) mContext 236155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande .getSystemService(Context.NOTIFICATION_SERVICE); 237155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande 238155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande Message message; 239155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande if (visible) { 240155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande 241155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande // Not enough time has passed to show the notification again 242155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande if (System.currentTimeMillis() < mNotificationRepeatTime) { 243155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande return; 244155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande } 245155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande 2462247009c2527c80e7fb00f056aea67d1fe09671bChris Wren if (mNotificationBuilder == null) { 2472247009c2527c80e7fb00f056aea67d1fe09671bChris Wren // Cache the Notification builder object. 2482247009c2527c80e7fb00f056aea67d1fe09671bChris Wren mNotificationBuilder = new Notification.Builder(mContext) 2492247009c2527c80e7fb00f056aea67d1fe09671bChris Wren .setWhen(0) 2502247009c2527c80e7fb00f056aea67d1fe09671bChris Wren .setSmallIcon(ICON_NETWORKS_AVAILABLE) 2512247009c2527c80e7fb00f056aea67d1fe09671bChris Wren .setAutoCancel(true) 2522247009c2527c80e7fb00f056aea67d1fe09671bChris Wren .setContentIntent(TaskStackBuilder.create(mContext) 2532247009c2527c80e7fb00f056aea67d1fe09671bChris Wren .addNextIntentWithParentStack( 2542247009c2527c80e7fb00f056aea67d1fe09671bChris Wren new Intent(WifiManager.ACTION_PICK_WIFI_NETWORK)) 2552247009c2527c80e7fb00f056aea67d1fe09671bChris Wren .getPendingIntent(0, 0, null, UserHandle.CURRENT)) 2562247009c2527c80e7fb00f056aea67d1fe09671bChris Wren .setColor(mContext.getResources().getColor( 2572247009c2527c80e7fb00f056aea67d1fe09671bChris Wren com.android.internal.R.color.system_notification_accent_color)); 258155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande } 259155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande 260155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande CharSequence title = mContext.getResources().getQuantityText( 261155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande com.android.internal.R.plurals.wifi_available, numNetworks); 262155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande CharSequence details = mContext.getResources().getQuantityText( 263155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande com.android.internal.R.plurals.wifi_available_detailed, numNetworks); 2642247009c2527c80e7fb00f056aea67d1fe09671bChris Wren mNotificationBuilder.setTicker(title); 2652247009c2527c80e7fb00f056aea67d1fe09671bChris Wren mNotificationBuilder.setContentTitle(title); 2662247009c2527c80e7fb00f056aea67d1fe09671bChris Wren mNotificationBuilder.setContentText(details); 267155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande 268155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande mNotificationRepeatTime = System.currentTimeMillis() + NOTIFICATION_REPEAT_DELAY_MS; 269155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande 2702247009c2527c80e7fb00f056aea67d1fe09671bChris Wren notificationManager.notifyAsUser(null, ICON_NETWORKS_AVAILABLE, 2712247009c2527c80e7fb00f056aea67d1fe09671bChris Wren mNotificationBuilder.build(), UserHandle.ALL); 272155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande } else { 273155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande notificationManager.cancelAsUser(null, ICON_NETWORKS_AVAILABLE, UserHandle.ALL); 274155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande } 275155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande 276155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande mNotificationShown = visible; 277155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande } 278155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande 279155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 280155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande pw.println("mNotificationEnabled " + mNotificationEnabled); 281155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande pw.println("mNotificationRepeatTime " + mNotificationRepeatTime); 282155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande pw.println("mNotificationShown " + mNotificationShown); 283155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande pw.println("mNumScansSinceNetworkStateChange " + mNumScansSinceNetworkStateChange); 284155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande } 285155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande 286155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande private class NotificationEnabledSettingObserver extends ContentObserver { 287155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande public NotificationEnabledSettingObserver(Handler handler) { 288155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande super(handler); 289155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande } 290155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande 291155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande public void register() { 292155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande ContentResolver cr = mContext.getContentResolver(); 293155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande cr.registerContentObserver(Settings.Global.getUriFor( 294155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande Settings.Global.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON), true, this); 295155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande synchronized (WifiNotificationController.this) { 296155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande mNotificationEnabled = getValue(); 297155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande } 298155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande } 299155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande 300155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande @Override 301155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande public void onChange(boolean selfChange) { 302155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande super.onChange(selfChange); 303155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande 304155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande synchronized (WifiNotificationController.this) { 305155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande mNotificationEnabled = getValue(); 306155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande resetNotification(); 307155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande } 308155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande } 309155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande 310155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande private boolean getValue() { 311250e70cb113e75ac9b24b09bbf176804d37755d8Paul Stewart return mFrameworkFacade.getIntegerSetting(mContext, 312155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande Settings.Global.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 1) == 1; 313155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande } 314155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande } 315155b9d09ef9b8ead3ca617afdd91e74070d3f0cbVinit Deshpande} 316