mobile/android/base/GeckoNetworkManager.java

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/mobile/android/base/GeckoNetworkManager.java	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,268 @@
     1.4 +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
     1.5 + * This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +package org.mozilla.gecko;
    1.10 +
    1.11 +import org.mozilla.gecko.mozglue.JNITarget;
    1.12 +
    1.13 +import android.content.BroadcastReceiver;
    1.14 +import android.content.Context;
    1.15 +import android.content.Intent;
    1.16 +import android.content.IntentFilter;
    1.17 +import android.net.ConnectivityManager;
    1.18 +import android.net.DhcpInfo;
    1.19 +import android.net.NetworkInfo;
    1.20 +import android.net.wifi.WifiManager;
    1.21 +import android.telephony.TelephonyManager;
    1.22 +import android.util.Log;
    1.23 +
    1.24 +/*
    1.25 + * A part of the work of GeckoNetworkManager is to give an general connection
    1.26 + * type based on the current connection. According to spec of NetworkInformation
    1.27 + * API version 3, connection types include: bluetooth, cellular, ethernet, none,
    1.28 + * wifi and other. The objective of providing such general connection is due to
    1.29 + * some security concerns. In short, we don't want to expose the information of
    1.30 + * exact network type, especially the cellular network type.
    1.31 + *
    1.32 + * Current connection is firstly obtained from Android's ConnectivityManager,
    1.33 + * which is represented by the constant, and then will be mapped into the
    1.34 + * connection type defined in Network Information API version 3.
    1.35 + */
    1.36 +
    1.37 +public class GeckoNetworkManager extends BroadcastReceiver {
    1.38 +    private static final String LOGTAG = "GeckoNetworkManager";
    1.39 +
    1.40 +    static private final GeckoNetworkManager sInstance = new GeckoNetworkManager();
    1.41 +
    1.42 +    // Connection Type defined in Network Information API v3.
    1.43 +    private enum ConnectionType {
    1.44 +        CELLULAR(0),
    1.45 +        BLUETOOTH(1),
    1.46 +        ETHERNET(2),
    1.47 +        WIFI(3),
    1.48 +        OTHER(4),
    1.49 +        NONE(5);
    1.50 +
    1.51 +        public final int value;
    1.52 +
    1.53 +        private ConnectionType(int value) {
    1.54 +            this.value = value;
    1.55 +        }
    1.56 +    }
    1.57 +
    1.58 +    private enum InfoType {
    1.59 +        MCC,
    1.60 +        MNC
    1.61 +    }
    1.62 +
    1.63 +    private ConnectionType mConnectionType = ConnectionType.NONE;
    1.64 +    private final IntentFilter mNetworkFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
    1.65 +
    1.66 +    // Whether the manager should be listening to Network Information changes.
    1.67 +    private boolean mShouldBeListening = false;
    1.68 +
    1.69 +    // Whether the manager should notify Gecko that a change in Network
    1.70 +    // Information happened.
    1.71 +    private boolean mShouldNotify = false;
    1.72 +
    1.73 +    // The application context used for registering receivers, so
    1.74 +    // we can unregister them again later.
    1.75 +    private volatile Context mApplicationContext;
    1.76 +
    1.77 +    public static GeckoNetworkManager getInstance() {
    1.78 +        return sInstance;
    1.79 +    }
    1.80 +
    1.81 +    @Override
    1.82 +    public void onReceive(Context aContext, Intent aIntent) {
    1.83 +        updateConnectionType();
    1.84 +    }
    1.85 +
    1.86 +    public void start(final Context context) {
    1.87 +        // Note that this initialization clause only runs once.
    1.88 +        mApplicationContext = context.getApplicationContext();
    1.89 +        if (mConnectionType == ConnectionType.NONE) {
    1.90 +            mConnectionType = getConnectionType();
    1.91 +        }
    1.92 +
    1.93 +        mShouldBeListening = true;
    1.94 +        updateConnectionType();
    1.95 +
    1.96 +        if (mShouldNotify) {
    1.97 +            startListening();
    1.98 +        }
    1.99 +    }
   1.100 +
   1.101 +    private void startListening() {
   1.102 +        final Context appContext = mApplicationContext;
   1.103 +        if (appContext == null) {
   1.104 +            Log.w(LOGTAG, "Not registering receiver: no context!");
   1.105 +            return;
   1.106 +        }
   1.107 +
   1.108 +        Log.v(LOGTAG, "Registering receiver.");
   1.109 +        appContext.registerReceiver(this, mNetworkFilter);
   1.110 +    }
   1.111 +
   1.112 +    public void stop() {
   1.113 +        mShouldBeListening = false;
   1.114 +
   1.115 +        if (mShouldNotify) {
   1.116 +            stopListening();
   1.117 +        }
   1.118 +    }
   1.119 +
   1.120 +    private void stopListening() {
   1.121 +        if (null == mApplicationContext) {
   1.122 +            return;
   1.123 +        }
   1.124 +
   1.125 +        mApplicationContext.unregisterReceiver(this);
   1.126 +    }
   1.127 +
   1.128 +    private int wifiDhcpGatewayAddress() {
   1.129 +        if (mConnectionType != ConnectionType.WIFI) {
   1.130 +            return 0;
   1.131 +        }
   1.132 +
   1.133 +        if (null == mApplicationContext) {
   1.134 +            return 0;
   1.135 +        }
   1.136 +
   1.137 +        try {
   1.138 +            WifiManager mgr = (WifiManager) mApplicationContext.getSystemService(Context.WIFI_SERVICE);
   1.139 +            DhcpInfo d = mgr.getDhcpInfo();
   1.140 +            if (d == null) {
   1.141 +                return 0;
   1.142 +            }
   1.143 +
   1.144 +            return d.gateway;
   1.145 +
   1.146 +        } catch (Exception ex) {
   1.147 +            // getDhcpInfo() is not documented to require any permissions, but on some devices
   1.148 +            // requires android.permission.ACCESS_WIFI_STATE. Just catch the generic exception
   1.149 +            // here and returning 0. Not logging because this could be noisy.
   1.150 +            return 0;
   1.151 +        }
   1.152 +    }
   1.153 +
   1.154 +    private void updateConnectionType() {
   1.155 +        ConnectionType previousConnectionType = mConnectionType;
   1.156 +        mConnectionType = getConnectionType();
   1.157 +
   1.158 +        if (mConnectionType == previousConnectionType || !mShouldNotify) {
   1.159 +            return;
   1.160 +        }
   1.161 +
   1.162 +        GeckoAppShell.sendEventToGecko(GeckoEvent.createNetworkEvent(
   1.163 +                                       mConnectionType.value,
   1.164 +                                       mConnectionType == ConnectionType.WIFI,
   1.165 +                                       wifiDhcpGatewayAddress()));
   1.166 +    }
   1.167 +
   1.168 +    public double[] getCurrentInformation() {
   1.169 +        return new double[] { mConnectionType.value,
   1.170 +                              (mConnectionType == ConnectionType.WIFI) ? 1.0 : 0.0,
   1.171 +                              wifiDhcpGatewayAddress()};
   1.172 +    }
   1.173 +
   1.174 +    public void enableNotifications() {
   1.175 +        // We set mShouldNotify *after* calling updateConnectionType() to make sure we
   1.176 +        // don't notify an eventual change in mConnectionType.
   1.177 +        mConnectionType = ConnectionType.NONE; // force a notification
   1.178 +        updateConnectionType();
   1.179 +        mShouldNotify = true;
   1.180 +
   1.181 +        if (mShouldBeListening) {
   1.182 +            startListening();
   1.183 +        }
   1.184 +    }
   1.185 +
   1.186 +    public void disableNotifications() {
   1.187 +        mShouldNotify = false;
   1.188 +
   1.189 +        if (mShouldBeListening) {
   1.190 +            stopListening();
   1.191 +        }
   1.192 +    }
   1.193 +
   1.194 +    private ConnectionType getConnectionType() {
   1.195 +        final Context appContext = mApplicationContext;
   1.196 +
   1.197 +        if (null == appContext) {
   1.198 +            return ConnectionType.NONE;
   1.199 +        }
   1.200 +
   1.201 +        ConnectivityManager cm = (ConnectivityManager) appContext.getSystemService(Context.CONNECTIVITY_SERVICE);
   1.202 +        if (cm == null) {
   1.203 +            Log.e(LOGTAG, "Connectivity service does not exist");
   1.204 +            return ConnectionType.NONE;
   1.205 +        }
   1.206 +
   1.207 +        NetworkInfo ni = null;
   1.208 +        try {
   1.209 +            ni = cm.getActiveNetworkInfo();
   1.210 +        } catch (SecurityException se) {} // if we don't have the permission, fall through to null check
   1.211 +
   1.212 +        if (ni == null) {
   1.213 +            return ConnectionType.NONE;
   1.214 +        }
   1.215 +
   1.216 +        switch (ni.getType()) {
   1.217 +        case ConnectivityManager.TYPE_BLUETOOTH:
   1.218 +            return ConnectionType.BLUETOOTH;
   1.219 +        case ConnectivityManager.TYPE_ETHERNET:
   1.220 +            return ConnectionType.ETHERNET;
   1.221 +        case ConnectivityManager.TYPE_MOBILE:
   1.222 +        case ConnectivityManager.TYPE_WIMAX:
   1.223 +            return ConnectionType.CELLULAR;
   1.224 +        case ConnectivityManager.TYPE_WIFI:
   1.225 +            return ConnectionType.WIFI;
   1.226 +        default:
   1.227 +            Log.w(LOGTAG, "Ignoring the current network type.");
   1.228 +            return ConnectionType.OTHER;
   1.229 +        }
   1.230 +    }
   1.231 +
   1.232 +    private static int getNetworkOperator(InfoType type, Context context) {
   1.233 +        if (null == context) {
   1.234 +            return -1;
   1.235 +        }
   1.236 +
   1.237 +        TelephonyManager tel = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
   1.238 +        if (tel == null) {
   1.239 +            Log.e(LOGTAG, "Telephony service does not exist");
   1.240 +            return -1;
   1.241 +        }
   1.242 +
   1.243 +        String networkOperator = tel.getNetworkOperator();
   1.244 +        if (networkOperator == null || networkOperator.length() <= 3) {
   1.245 +            return -1;
   1.246 +        }
   1.247 +        if (type == InfoType.MNC) {
   1.248 +            return Integer.parseInt(networkOperator.substring(3));
   1.249 +        } else if (type == InfoType.MCC) {
   1.250 +            return Integer.parseInt(networkOperator.substring(0, 3));
   1.251 +        }
   1.252 +
   1.253 +        return -1;
   1.254 +    }
   1.255 +
   1.256 +    /**
   1.257 +     * These are called from JavaScript ctypes. Avoid letting ProGuard delete them.
   1.258 +     *
   1.259 +     * Note that these methods must only be called after GeckoAppShell has been
   1.260 +     * initialized: they depend on access to the context.
   1.261 +     */
   1.262 +    @JNITarget
   1.263 +    public static int getMCC() {
   1.264 +        return getNetworkOperator(InfoType.MCC, GeckoAppShell.getContext().getApplicationContext());
   1.265 +    }
   1.266 +
   1.267 +    @JNITarget
   1.268 +    public static int getMNC() {
   1.269 +        return getNetworkOperator(InfoType.MNC, GeckoAppShell.getContext().getApplicationContext());
   1.270 +    }
   1.271 +}

mercurial