CaptivePortalTracker.java revision bf34122a96ef3d02e9b7935e07eb4f9b04034828
1da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff/*
2da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff * Copyright (C) 2012 The Android Open Source Project
3da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff *
4da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff * Licensed under the Apache License, Version 2.0 (the "License");
5da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff * you may not use this file except in compliance with the License.
6da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff * You may obtain a copy of the License at
7da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff *
8da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff *      http://www.apache.org/licenses/LICENSE-2.0
9da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff *
10da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff * Unless required by applicable law or agreed to in writing, software
11da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff * distributed under the License is distributed on an "AS IS" BASIS,
12da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff * See the License for the specific language governing permissions and
14da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff * limitations under the License.
15da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff */
16da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff
17da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriffpackage android.net;
18da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff
199538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriffimport android.app.Activity;
20da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriffimport android.app.Notification;
21da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriffimport android.app.NotificationManager;
22da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriffimport android.app.PendingIntent;
23da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriffimport android.content.BroadcastReceiver;
24da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriffimport android.content.Context;
25da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriffimport android.content.Intent;
26da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriffimport android.content.IntentFilter;
27da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriffimport android.content.res.Resources;
28108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brennerimport android.database.ContentObserver;
29da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriffimport android.net.ConnectivityManager;
30da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriffimport android.net.IConnectivityManager;
31108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brennerimport android.os.Handler;
329538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriffimport android.os.UserHandle;
33da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriffimport android.os.Message;
34da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriffimport android.os.RemoteException;
35da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriffimport android.provider.Settings;
36b8aad91f059527e04abaf8a83ed1ce6b5f09c55dIrfan Sheriffimport android.telephony.TelephonyManager;
37da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff
389538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriffimport com.android.internal.util.State;
399538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriffimport com.android.internal.util.StateMachine;
409538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff
41da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriffimport java.io.IOException;
42da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriffimport java.net.HttpURLConnection;
43da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriffimport java.net.InetAddress;
44da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriffimport java.net.Inet4Address;
45bf34122a96ef3d02e9b7935e07eb4f9b04034828Wink Savilleimport java.net.SocketTimeoutException;
46da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriffimport java.net.URL;
47da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriffimport java.net.UnknownHostException;
48da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff
49da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriffimport com.android.internal.R;
50da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff
51da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff/**
529538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff * This class allows captive portal detection on a network.
53da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff * @hide
54da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff */
559538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriffpublic class CaptivePortalTracker extends StateMachine {
56bf34122a96ef3d02e9b7935e07eb4f9b04034828Wink Saville    private static final boolean DBG = true;
57da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff    private static final String TAG = "CaptivePortalTracker";
58da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff
59da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff    private static final String DEFAULT_SERVER = "clients3.google.com";
60da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff    private static final String NOTIFICATION_ID = "CaptivePortal.Notification";
61da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff
62da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff    private static final int SOCKET_TIMEOUT_MS = 10000;
63da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff
64da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff    private String mServer;
65da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff    private String mUrl;
66da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff    private boolean mNotificationShown = false;
67da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff    private boolean mIsCaptivePortalCheckEnabled = false;
68da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff    private IConnectivityManager mConnService;
69b8aad91f059527e04abaf8a83ed1ce6b5f09c55dIrfan Sheriff    private TelephonyManager mTelephonyManager;
70da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff    private Context mContext;
71da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff    private NetworkInfo mNetworkInfo;
72da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff
739538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff    private static final int CMD_DETECT_PORTAL          = 0;
749538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff    private static final int CMD_CONNECTIVITY_CHANGE    = 1;
759538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff    private static final int CMD_DELAYED_CAPTIVE_CHECK  = 2;
76da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff
779538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff    /* This delay happens every time before we do a captive check on a network */
789538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff    private static final int DELAYED_CHECK_INTERVAL_MS = 10000;
799538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff    private int mDelayedCheckToken = 0;
809538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff
819538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff    private State mDefaultState = new DefaultState();
829538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff    private State mNoActiveNetworkState = new NoActiveNetworkState();
839538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff    private State mActiveNetworkState = new ActiveNetworkState();
849538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff    private State mDelayedCaptiveCheckState = new DelayedCaptiveCheckState();
859538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff
86108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner    private static final String SETUP_WIZARD_PACKAGE = "com.google.android.setupwizard";
87108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner    private boolean mDeviceProvisioned = false;
88108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner    private ProvisioningObserver mProvisioningObserver;
89108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner
909538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff    private CaptivePortalTracker(Context context, IConnectivityManager cs) {
919538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff        super(TAG);
92da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff
93da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        mContext = context;
94da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        mConnService = cs;
95b8aad91f059527e04abaf8a83ed1ce6b5f09c55dIrfan Sheriff        mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
96108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner        mProvisioningObserver = new ProvisioningObserver();
97da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff
98da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        IntentFilter filter = new IntentFilter();
99da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
100108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner        filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE);
101da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        mContext.registerReceiver(mReceiver, filter);
102da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff
103625239a05401bbf18b04d9874cea3f82da7c29a1Jeff Sharkey        mServer = Settings.Global.getString(mContext.getContentResolver(),
104625239a05401bbf18b04d9874cea3f82da7c29a1Jeff Sharkey                Settings.Global.CAPTIVE_PORTAL_SERVER);
105da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        if (mServer == null) mServer = DEFAULT_SERVER;
106da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff
107625239a05401bbf18b04d9874cea3f82da7c29a1Jeff Sharkey        mIsCaptivePortalCheckEnabled = Settings.Global.getInt(mContext.getContentResolver(),
108625239a05401bbf18b04d9874cea3f82da7c29a1Jeff Sharkey                Settings.Global.CAPTIVE_PORTAL_DETECTION_ENABLED, 1) == 1;
1099538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff
1109538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff        addState(mDefaultState);
1119538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff            addState(mNoActiveNetworkState, mDefaultState);
1129538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff            addState(mActiveNetworkState, mDefaultState);
1139538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                addState(mDelayedCaptiveCheckState, mActiveNetworkState);
1149538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff        setInitialState(mNoActiveNetworkState);
115da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff    }
116da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff
117108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner    private class ProvisioningObserver extends ContentObserver {
118108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner        ProvisioningObserver() {
119108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner            super(new Handler());
120108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner            mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor(
121108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner                    Settings.Global.DEVICE_PROVISIONED), false, this);
122108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner            onChange(false); // load initial value
123108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner        }
124108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner
125108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner        @Override
126108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner        public void onChange(boolean selfChange) {
127108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner            mDeviceProvisioned = Settings.Global.getInt(mContext.getContentResolver(),
128108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner                    Settings.Global.DEVICE_PROVISIONED, 0) != 0;
129108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner        }
130108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner    }
131108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner
132da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
133da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        @Override
134da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        public void onReceive(Context context, Intent intent) {
135da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff            String action = intent.getAction();
136108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner            // Normally, we respond to CONNECTIVITY_ACTION, allowing time for the change in
137108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner            // connectivity to stabilize, but if the device is not yet provisioned, respond
138108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner            // immediately to speed up transit through the setup wizard.
139108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner            if ((mDeviceProvisioned && action.equals(ConnectivityManager.CONNECTIVITY_ACTION))
140108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner                    || (!mDeviceProvisioned
141108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner                            && action.equals(ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE))) {
142da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff                NetworkInfo info = intent.getParcelableExtra(
143da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff                        ConnectivityManager.EXTRA_NETWORK_INFO);
1449538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                sendMessage(obtainMessage(CMD_CONNECTIVITY_CHANGE, info));
145da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff            }
146da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        }
147da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff    };
148da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff
1499538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff    public static CaptivePortalTracker makeCaptivePortalTracker(Context context,
150da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff            IConnectivityManager cs) {
1519538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff        CaptivePortalTracker captivePortal = new CaptivePortalTracker(context, cs);
1529538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff        captivePortal.start();
153da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        return captivePortal;
154da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff    }
155da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff
1569538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff    public void detectCaptivePortal(NetworkInfo info) {
1579538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff        sendMessage(obtainMessage(CMD_DETECT_PORTAL, info));
1589538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff    }
1599538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff
1609538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff    private class DefaultState extends State {
1619538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff        @Override
1629538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff        public void enter() {
1639538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff            if (DBG) log(getName() + "\n");
164da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        }
165da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff
166da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        @Override
1679538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff        public boolean processMessage(Message message) {
1689538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff            if (DBG) log(getName() + message.toString() + "\n");
1699538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff            switch (message.what) {
1709538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                case CMD_DETECT_PORTAL:
1719538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                    NetworkInfo info = (NetworkInfo) message.obj;
1729538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                    // Checking on a secondary connection is not supported
1739538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                    // yet
1749538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                    notifyPortalCheckComplete(info);
175da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff                    break;
1769538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                case CMD_CONNECTIVITY_CHANGE:
1779538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                case CMD_DELAYED_CAPTIVE_CHECK:
1789538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                    break;
1799538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                default:
1809538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                    loge("Ignoring " + message);
1819538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                    break;
1829538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff            }
1839538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff            return HANDLED;
1849538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff        }
1859538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff    }
186da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff
1879538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff    private class NoActiveNetworkState extends State {
1889538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff        @Override
1899538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff        public void enter() {
1909538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff            if (DBG) log(getName() + "\n");
1919538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff            mNetworkInfo = null;
1929538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff            /* Clear any previous notification */
1939538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff            setNotificationVisible(false);
1949538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff        }
195da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff
1969538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff        @Override
1979538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff        public boolean processMessage(Message message) {
1989538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff            if (DBG) log(getName() + message.toString() + "\n");
1999538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff            InetAddress server;
2009538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff            NetworkInfo info;
2019538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff            switch (message.what) {
2029538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                case CMD_CONNECTIVITY_CHANGE:
2039538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                    info = (NetworkInfo) message.obj;
2049538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                    if (info.isConnected() && isActiveNetwork(info)) {
2059538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                        mNetworkInfo = info;
2069538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                        transitionTo(mDelayedCaptiveCheckState);
207da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff                    }
208da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff                    break;
209da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff                default:
2109538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                    return NOT_HANDLED;
211da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff            }
2129538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff            return HANDLED;
2139538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff        }
2149538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff    }
2159538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff
2169538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff    private class ActiveNetworkState extends State {
2179538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff        @Override
2189538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff        public void enter() {
2199538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff            if (DBG) log(getName() + "\n");
220da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        }
221da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff
2229538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff        @Override
2239538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff        public boolean processMessage(Message message) {
2249538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff            NetworkInfo info;
2259538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff            switch (message.what) {
2269538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff               case CMD_CONNECTIVITY_CHANGE:
2279538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                    info = (NetworkInfo) message.obj;
2289538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                    if (!info.isConnected()
2299538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                            && info.getType() == mNetworkInfo.getType()) {
2309538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                        if (DBG) log("Disconnected from active network " + info);
2319538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                        transitionTo(mNoActiveNetworkState);
2329538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                    } else if (info.getType() != mNetworkInfo.getType() &&
2339538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                            info.isConnected() &&
2349538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                            isActiveNetwork(info)) {
2359538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                        if (DBG) log("Active network switched " + info);
2369538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                        deferMessage(message);
2379538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                        transitionTo(mNoActiveNetworkState);
2389538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                    }
2399538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                    break;
2409538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                default:
2419538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                    return NOT_HANDLED;
2429538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff            }
2439538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff            return HANDLED;
244da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        }
245da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff    }
246da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff
2479538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff
2489538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff
2499538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff    private class DelayedCaptiveCheckState extends State {
2509538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff        @Override
2519538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff        public void enter() {
2529538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff            if (DBG) log(getName() + "\n");
253108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner            Message message = obtainMessage(CMD_DELAYED_CAPTIVE_CHECK, ++mDelayedCheckToken, 0);
254108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner            if (mDeviceProvisioned) {
255108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner                sendMessageDelayed(message, DELAYED_CHECK_INTERVAL_MS);
256108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner            } else {
257108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner                sendMessage(message);
258108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner            }
2599538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff        }
2609538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff
2619538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff        @Override
2629538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff        public boolean processMessage(Message message) {
2639538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff            if (DBG) log(getName() + message.toString() + "\n");
2649538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff            switch (message.what) {
2659538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                case CMD_DELAYED_CAPTIVE_CHECK:
2669538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                    if (message.arg1 == mDelayedCheckToken) {
2679538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                        InetAddress server = lookupHost(mServer);
268108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner                        boolean captive = server != null && isCaptivePortal(server);
269108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner                        if (captive) {
270108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner                            if (DBG) log("Captive network " + mNetworkInfo);
271108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner                        } else {
272108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner                            if (DBG) log("Not captive network " + mNetworkInfo);
273108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner                        }
274108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner                        if (mDeviceProvisioned) {
275108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner                            if (captive) {
276108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner                                // Setup Wizard will assist the user in connecting to a captive
277108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner                                // portal, so make the notification visible unless during setup
2789538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                                setNotificationVisible(true);
2799538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                            }
280108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner                        } else {
281108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner                            Intent intent = new Intent(
282108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner                                    ConnectivityManager.ACTION_CAPTIVE_PORTAL_TEST_COMPLETED);
283108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner                            intent.putExtra(ConnectivityManager.EXTRA_IS_CAPTIVE_PORTAL, captive);
284108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner                            intent.setPackage(SETUP_WIZARD_PACKAGE);
285108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner                            mContext.sendBroadcast(intent);
2869538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                        }
287108da0cfa4a2f59cc953a4ec61314e69b61d6777Russell Brenner
2889538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                        transitionTo(mActiveNetworkState);
2899538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                    }
2909538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                    break;
2919538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                default:
2929538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                    return NOT_HANDLED;
2939538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff            }
2949538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff            return HANDLED;
2959538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff        }
296da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff    }
297da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff
2989538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff    private void notifyPortalCheckComplete(NetworkInfo info) {
2999538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff        if (info == null) {
3009538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff            loge("notifyPortalCheckComplete on null");
3019538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff            return;
3029538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff        }
303da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        try {
3049538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff            mConnService.captivePortalCheckComplete(info);
305da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        } catch(RemoteException e) {
306da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff            e.printStackTrace();
307da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        }
308da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff    }
309da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff
3109538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff    private boolean isActiveNetwork(NetworkInfo info) {
311da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        try {
3129538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff            NetworkInfo active = mConnService.getActiveNetworkInfo();
3139538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff            if (active != null && active.getType() == info.getType()) {
3149538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                return true;
3159538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff            }
316da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        } catch (RemoteException e) {
317da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff            e.printStackTrace();
318da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        }
3199538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff        return false;
320da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff    }
321da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff
322da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff    /**
323da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff     * Do a URL fetch on a known server to see if we get the data we expect
324da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff     */
325da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff    private boolean isCaptivePortal(InetAddress server) {
326da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        HttpURLConnection urlConnection = null;
327da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        if (!mIsCaptivePortalCheckEnabled) return false;
328da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff
329da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        mUrl = "http://" + server.getHostAddress() + "/generate_204";
3309538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff        if (DBG) log("Checking " + mUrl);
331da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        try {
332da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff            URL url = new URL(mUrl);
333da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff            urlConnection = (HttpURLConnection) url.openConnection();
334da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff            urlConnection.setInstanceFollowRedirects(false);
335da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff            urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS);
336da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff            urlConnection.setReadTimeout(SOCKET_TIMEOUT_MS);
337da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff            urlConnection.setUseCaches(false);
338da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff            urlConnection.getInputStream();
339da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff            // we got a valid response, but not from the real google
340da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff            return urlConnection.getResponseCode() != 204;
341bf34122a96ef3d02e9b7935e07eb4f9b04034828Wink Saville        } catch (SocketTimeoutException e) {
342bf34122a96ef3d02e9b7935e07eb4f9b04034828Wink Saville            if (DBG) log("Probably a portal: exception " + e);
343bf34122a96ef3d02e9b7935e07eb4f9b04034828Wink Saville            return true;
344da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        } catch (IOException e) {
345da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff            if (DBG) log("Probably not a portal: exception " + e);
346da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff            return false;
347da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        } finally {
348da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff            if (urlConnection != null) {
349da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff                urlConnection.disconnect();
350da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff            }
351da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        }
352da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff    }
353da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff
354da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff    private InetAddress lookupHost(String hostname) {
355da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        InetAddress inetAddress[];
356da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        try {
357da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff            inetAddress = InetAddress.getAllByName(hostname);
358da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        } catch (UnknownHostException e) {
359da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff            return null;
360da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        }
361da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff
362da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        for (InetAddress a : inetAddress) {
363da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff            if (a instanceof Inet4Address) return a;
364da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        }
365da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        return null;
366da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff    }
367da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff
368da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff    private void setNotificationVisible(boolean visible) {
369da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        // if it should be hidden and it is already hidden, then noop
370da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        if (!visible && !mNotificationShown) {
371da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff            return;
372da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        }
373da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff
374da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        Resources r = Resources.getSystem();
375da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        NotificationManager notificationManager = (NotificationManager) mContext
376da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff            .getSystemService(Context.NOTIFICATION_SERVICE);
377da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff
378da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        if (visible) {
3799538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff            CharSequence title;
380b8aad91f059527e04abaf8a83ed1ce6b5f09c55dIrfan Sheriff            CharSequence details;
381ebb8f413e63dee6d96904a49a3508b97671e5fe8Irfan Sheriff            int icon;
382b8aad91f059527e04abaf8a83ed1ce6b5f09c55dIrfan Sheriff            switch (mNetworkInfo.getType()) {
383b8aad91f059527e04abaf8a83ed1ce6b5f09c55dIrfan Sheriff                case ConnectivityManager.TYPE_WIFI:
384b8aad91f059527e04abaf8a83ed1ce6b5f09c55dIrfan Sheriff                    title = r.getString(R.string.wifi_available_sign_in, 0);
385b8aad91f059527e04abaf8a83ed1ce6b5f09c55dIrfan Sheriff                    details = r.getString(R.string.network_available_sign_in_detailed,
386b8aad91f059527e04abaf8a83ed1ce6b5f09c55dIrfan Sheriff                            mNetworkInfo.getExtraInfo());
387ebb8f413e63dee6d96904a49a3508b97671e5fe8Irfan Sheriff                    icon = R.drawable.stat_notify_wifi_in_range;
388b8aad91f059527e04abaf8a83ed1ce6b5f09c55dIrfan Sheriff                    break;
389b8aad91f059527e04abaf8a83ed1ce6b5f09c55dIrfan Sheriff                case ConnectivityManager.TYPE_MOBILE:
390b8aad91f059527e04abaf8a83ed1ce6b5f09c55dIrfan Sheriff                    title = r.getString(R.string.network_available_sign_in, 0);
391b8aad91f059527e04abaf8a83ed1ce6b5f09c55dIrfan Sheriff                    // TODO: Change this to pull from NetworkInfo once a printable
392b8aad91f059527e04abaf8a83ed1ce6b5f09c55dIrfan Sheriff                    // name has been added to it
393b8aad91f059527e04abaf8a83ed1ce6b5f09c55dIrfan Sheriff                    details = mTelephonyManager.getNetworkOperatorName();
394ebb8f413e63dee6d96904a49a3508b97671e5fe8Irfan Sheriff                    icon = R.drawable.stat_notify_rssi_in_range;
395b8aad91f059527e04abaf8a83ed1ce6b5f09c55dIrfan Sheriff                    break;
396b8aad91f059527e04abaf8a83ed1ce6b5f09c55dIrfan Sheriff                default:
397b8aad91f059527e04abaf8a83ed1ce6b5f09c55dIrfan Sheriff                    title = r.getString(R.string.network_available_sign_in, 0);
398b8aad91f059527e04abaf8a83ed1ce6b5f09c55dIrfan Sheriff                    details = r.getString(R.string.network_available_sign_in_detailed,
399b8aad91f059527e04abaf8a83ed1ce6b5f09c55dIrfan Sheriff                            mNetworkInfo.getExtraInfo());
400ebb8f413e63dee6d96904a49a3508b97671e5fe8Irfan Sheriff                    icon = R.drawable.stat_notify_rssi_in_range;
401b8aad91f059527e04abaf8a83ed1ce6b5f09c55dIrfan Sheriff                    break;
4029538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff            }
403da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff
404da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff            Notification notification = new Notification();
405da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff            notification.when = 0;
406ebb8f413e63dee6d96904a49a3508b97671e5fe8Irfan Sheriff            notification.icon = icon;
407da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff            notification.flags = Notification.FLAG_AUTO_CANCEL;
4089538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff            Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(mUrl));
4099538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff            intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT |
4109538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff                    Intent.FLAG_ACTIVITY_NEW_TASK);
4119538bdd3c77968c7673719c580ae653ede4654d6Irfan Sheriff            notification.contentIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
412da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff            notification.tickerText = title;
413da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff            notification.setLatestEventInfo(mContext, title, details, notification.contentIntent);
414da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff
415da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff            notificationManager.notify(NOTIFICATION_ID, 1, notification);
416da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        } else {
417da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff            notificationManager.cancel(NOTIFICATION_ID, 1);
418da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        }
419da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff        mNotificationShown = visible;
420da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff    }
421da6da0907b28d4704aabbdb1bbeb4300954670d1Irfan Sheriff}
422