1b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff/*
2b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff * Copyright (C) 2013 The Android Open Source Project
3b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff *
4b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff * Licensed under the Apache License, Version 2.0 (the "License");
5b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff * you may not use this file except in compliance with the License.
6b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff * You may obtain a copy of the License at
7b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff *
8b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff *      http://www.apache.org/licenses/LICENSE-2.0
9b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff *
10b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff * Unless required by applicable law or agreed to in writing, software
11b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff * distributed under the License is distributed on an "AS IS" BASIS,
12b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff * See the License for the specific language governing permissions and
14b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff * limitations under the License.
15b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff */
16b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff
17b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriffpackage com.android.server.wifi;
18b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff
19b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriffimport android.app.Notification;
20b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriffimport android.app.NotificationManager;
21b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriffimport android.app.TaskStackBuilder;
22b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriffimport android.content.BroadcastReceiver;
23b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriffimport android.content.ContentResolver;
24b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriffimport android.content.Context;
25b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriffimport android.content.Intent;
26b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriffimport android.content.IntentFilter;
27b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriffimport android.database.ContentObserver;
28b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriffimport android.net.NetworkInfo;
29b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriffimport android.net.wifi.ScanResult;
30b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriffimport android.net.wifi.WifiManager;
31b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriffimport android.net.wifi.WifiStateMachine;
32b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriffimport android.os.Handler;
33b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriffimport android.os.Message;
34b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriffimport android.os.UserHandle;
35b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriffimport android.provider.Settings;
36b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff
37b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriffimport java.io.FileDescriptor;
38b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriffimport java.io.PrintWriter;
39b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriffimport java.util.List;
40b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff
41b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff/* Takes care of handling the "open wi-fi network available" notification @hide */
42b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sherifffinal class WifiNotificationController {
43b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff    /**
44b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff     * The icon to show in the 'available networks' notification. This will also
45b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff     * be the ID of the Notification given to the NotificationManager.
46b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff     */
47b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff    private static final int ICON_NETWORKS_AVAILABLE =
48b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff            com.android.internal.R.drawable.stat_notify_wifi_in_range;
49b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff    /**
50b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff     * When a notification is shown, we wait this amount before possibly showing it again.
51b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff     */
52b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff    private final long NOTIFICATION_REPEAT_DELAY_MS;
53b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff    /**
54b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff     * Whether the user has set the setting to show the 'available networks' notification.
55b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff     */
56b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff    private boolean mNotificationEnabled;
57b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff    /**
58b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff     * Observes the user setting to keep {@link #mNotificationEnabled} in sync.
59b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff     */
60b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff    private NotificationEnabledSettingObserver mNotificationEnabledSettingObserver;
61b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff    /**
62b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff     * The {@link System#currentTimeMillis()} must be at least this value for us
63b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff     * to show the notification again.
64b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff     */
65b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff    private long mNotificationRepeatTime;
66b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff    /**
67b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff     * The Notification object given to the NotificationManager.
68b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff     */
69b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff    private Notification mNotification;
70b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff    /**
71b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff     * Whether the notification is being shown, as set by us. That is, if the
72b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff     * user cancels the notification, we will not receive the callback so this
73b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff     * will still be true. We only guarantee if this is false, then the
74b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff     * notification is not showing.
75b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff     */
76b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff    private boolean mNotificationShown;
77b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff    /**
78b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff     * The number of continuous scans that must occur before consider the
79b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff     * supplicant in a scanning state. This allows supplicant to associate with
80b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff     * remembered networks that are in the scan results.
81b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff     */
82b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff    private static final int NUM_SCANS_BEFORE_ACTUALLY_SCANNING = 3;
83b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff    /**
84b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff     * The number of scans since the last network state change. When this
85b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff     * exceeds {@link #NUM_SCANS_BEFORE_ACTUALLY_SCANNING}, we consider the
86b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff     * supplicant to actually be scanning. When the network state changes to
87b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff     * something other than scanning, we reset this to 0.
88b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff     */
89b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff    private int mNumScansSinceNetworkStateChange;
90b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff
91b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff    private final Context mContext;
92b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff    private final WifiStateMachine mWifiStateMachine;
93b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff    private NetworkInfo mNetworkInfo;
948c776925540d6deec2bf7a3eab6bf4b11f6d2747Robert Greenwalt    private volatile int mWifiState;
95b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff
96b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff    WifiNotificationController(Context context, WifiStateMachine wsm) {
97b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff        mContext = context;
98b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff        mWifiStateMachine = wsm;
998c776925540d6deec2bf7a3eab6bf4b11f6d2747Robert Greenwalt        mWifiState = WifiManager.WIFI_STATE_UNKNOWN;
100b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff
101b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff        IntentFilter filter = new IntentFilter();
102b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff        filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
103b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff        filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
104b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff        filter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
105b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff
106b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff        mContext.registerReceiver(
107b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                new BroadcastReceiver() {
108b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                    @Override
109b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                    public void onReceive(Context context, Intent intent) {
110b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                        if (intent.getAction().equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
1118c776925540d6deec2bf7a3eab6bf4b11f6d2747Robert Greenwalt                            mWifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
1128c776925540d6deec2bf7a3eab6bf4b11f6d2747Robert Greenwalt                                    WifiManager.WIFI_STATE_UNKNOWN);
113b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                            resetNotification();
114b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                        } else if (intent.getAction().equals(
115b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                                WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
116b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                            mNetworkInfo = (NetworkInfo) intent.getParcelableExtra(
117b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                                    WifiManager.EXTRA_NETWORK_INFO);
118b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                            // reset & clear notification on a network connect & disconnect
119b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                            switch(mNetworkInfo.getDetailedState()) {
120b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                                case CONNECTED:
121b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                                case DISCONNECTED:
122b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                                case CAPTIVE_PORTAL_CHECK:
123b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                                    resetNotification();
124b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                                    break;
125b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                            }
126b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                        } else if (intent.getAction().equals(
127b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                                WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
128b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                            checkAndSetNotification(mNetworkInfo,
129b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                                    mWifiStateMachine.syncGetScanResultsList());
130b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                        }
131b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                    }
132b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                }, filter);
133b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff
134b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff        // Setting is in seconds
135b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff        NOTIFICATION_REPEAT_DELAY_MS = Settings.Global.getInt(context.getContentResolver(),
136b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                Settings.Global.WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, 900) * 1000l;
137b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff        mNotificationEnabledSettingObserver = new NotificationEnabledSettingObserver(new Handler());
138b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff        mNotificationEnabledSettingObserver.register();
139b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff    }
140b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff
141b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff    private synchronized void checkAndSetNotification(NetworkInfo networkInfo,
142b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff            List<ScanResult> scanResults) {
143b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff        // TODO: unregister broadcast so we do not have to check here
144b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff        // If we shouldn't place a notification on available networks, then
145b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff        // don't bother doing any of the following
146b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff        if (!mNotificationEnabled) return;
147b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff        if (networkInfo == null) return;
1488c776925540d6deec2bf7a3eab6bf4b11f6d2747Robert Greenwalt        if (mWifiState != WifiManager.WIFI_STATE_ENABLED) return;
149b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff
150b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff        NetworkInfo.State state = networkInfo.getState();
151b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff        if ((state == NetworkInfo.State.DISCONNECTED)
152b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                || (state == NetworkInfo.State.UNKNOWN)) {
153b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff            if (scanResults != null) {
154b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                int numOpenNetworks = 0;
155b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                for (int i = scanResults.size() - 1; i >= 0; i--) {
156b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                    ScanResult scanResult = scanResults.get(i);
157b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff
158b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                    //A capability of [ESS] represents an open access point
159b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                    //that is available for an STA to connect
160b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                    if (scanResult.capabilities != null &&
161b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                            scanResult.capabilities.equals("[ESS]")) {
162b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                        numOpenNetworks++;
163b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                    }
164b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                }
165b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff
166b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                if (numOpenNetworks > 0) {
167b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                    if (++mNumScansSinceNetworkStateChange >= NUM_SCANS_BEFORE_ACTUALLY_SCANNING) {
168b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                        /*
169b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                         * We've scanned continuously at least
170b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                         * NUM_SCANS_BEFORE_NOTIFICATION times. The user
171b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                         * probably does not have a remembered network in range,
172b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                         * since otherwise supplicant would have tried to
173b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                         * associate and thus resetting this counter.
174b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                         */
175b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                        setNotificationVisible(true, numOpenNetworks, false, 0);
176b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                    }
177b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                    return;
178b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                }
179b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff            }
180b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff        }
181b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff
182b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff        // No open networks in range, remove the notification
183b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff        setNotificationVisible(false, 0, false, 0);
184b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff    }
185b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff
186b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff    /**
187b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff     * Clears variables related to tracking whether a notification has been
188b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff     * shown recently and clears the current notification.
189b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff     */
190b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff    private synchronized void resetNotification() {
191b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff        mNotificationRepeatTime = 0;
192b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff        mNumScansSinceNetworkStateChange = 0;
193b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff        setNotificationVisible(false, 0, false, 0);
194b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff    }
195b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff
196b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff    /**
197b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff     * Display or don't display a notification that there are open Wi-Fi networks.
198b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff     * @param visible {@code true} if notification should be visible, {@code false} otherwise
199b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff     * @param numNetworks the number networks seen
200b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff     * @param force {@code true} to force notification to be shown/not-shown,
201b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff     * even if it is already shown/not-shown.
202b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff     * @param delay time in milliseconds after which the notification should be made
203b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff     * visible or invisible.
204b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff     */
205b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff    private void setNotificationVisible(boolean visible, int numNetworks, boolean force,
206b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff            int delay) {
207b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff
208b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff        // Since we use auto cancel on the notification, when the
209b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff        // mNetworksAvailableNotificationShown is true, the notification may
210b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff        // have actually been canceled.  However, when it is false we know
211b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff        // for sure that it is not being shown (it will not be shown any other
212b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff        // place than here)
213b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff
214b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff        // If it should be hidden and it is already hidden, then noop
215b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff        if (!visible && !mNotificationShown && !force) {
216b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff            return;
217b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff        }
218b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff
219b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff        NotificationManager notificationManager = (NotificationManager) mContext
220b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                .getSystemService(Context.NOTIFICATION_SERVICE);
221b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff
222b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff        Message message;
223b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff        if (visible) {
224b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff
225b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff            // Not enough time has passed to show the notification again
226b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff            if (System.currentTimeMillis() < mNotificationRepeatTime) {
227b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                return;
228b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff            }
229b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff
230b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff            if (mNotification == null) {
231b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                // Cache the Notification object.
232b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                mNotification = new Notification();
233b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                mNotification.when = 0;
234b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                mNotification.icon = ICON_NETWORKS_AVAILABLE;
235b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                mNotification.flags = Notification.FLAG_AUTO_CANCEL;
236b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                mNotification.contentIntent = TaskStackBuilder.create(mContext)
237b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                        .addNextIntentWithParentStack(
238b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                                new Intent(WifiManager.ACTION_PICK_WIFI_NETWORK))
239b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                        .getPendingIntent(0, 0, null, UserHandle.CURRENT);
240b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff            }
241b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff
242b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff            CharSequence title = mContext.getResources().getQuantityText(
243b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                    com.android.internal.R.plurals.wifi_available, numNetworks);
244b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff            CharSequence details = mContext.getResources().getQuantityText(
245b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                    com.android.internal.R.plurals.wifi_available_detailed, numNetworks);
246b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff            mNotification.tickerText = title;
247b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff            mNotification.setLatestEventInfo(mContext, title, details, mNotification.contentIntent);
248b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff
249b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff            mNotificationRepeatTime = System.currentTimeMillis() + NOTIFICATION_REPEAT_DELAY_MS;
250b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff
251b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff            notificationManager.notifyAsUser(null, ICON_NETWORKS_AVAILABLE, mNotification,
252b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                    UserHandle.ALL);
253b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff        } else {
254b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff            notificationManager.cancelAsUser(null, ICON_NETWORKS_AVAILABLE, UserHandle.ALL);
255b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff        }
256b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff
257b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff        mNotificationShown = visible;
258b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff    }
259b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff
260b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff    void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
261b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff        pw.println("mNotificationEnabled " + mNotificationEnabled);
262b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff        pw.println("mNotificationRepeatTime " + mNotificationRepeatTime);
263b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff        pw.println("mNotificationShown " + mNotificationShown);
264b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff        pw.println("mNumScansSinceNetworkStateChange " + mNumScansSinceNetworkStateChange);
265b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff    }
266b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff
267b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff    private class NotificationEnabledSettingObserver extends ContentObserver {
268b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff        public NotificationEnabledSettingObserver(Handler handler) {
269b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff            super(handler);
270b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff        }
271b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff
272b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff        public void register() {
273b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff            ContentResolver cr = mContext.getContentResolver();
274b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff            cr.registerContentObserver(Settings.Global.getUriFor(
275b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                    Settings.Global.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON), true, this);
276b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff            synchronized (WifiNotificationController.this) {
277b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                mNotificationEnabled = getValue();
278b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff            }
279b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff        }
280b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff
281b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff        @Override
282b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff        public void onChange(boolean selfChange) {
283b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff            super.onChange(selfChange);
284b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff
285b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff            synchronized (WifiNotificationController.this) {
286b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                mNotificationEnabled = getValue();
287b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                resetNotification();
288b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff            }
289b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff        }
290b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff
291b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff        private boolean getValue() {
292b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff            return Settings.Global.getInt(mContext.getContentResolver(),
293b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff                    Settings.Global.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, 1) == 1;
294b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff        }
295b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff    }
296b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff
297b8c0e009a74ac2eaee8946fbe0bb3b3fe2749c9aIrfan Sheriff}
298