1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.messaging.util;
18
19import android.content.BroadcastReceiver;
20import android.content.Context;
21import android.content.Intent;
22import android.content.IntentFilter;
23import android.net.ConnectivityManager;
24import android.telephony.PhoneStateListener;
25import android.telephony.ServiceState;
26import android.telephony.SignalStrength;
27import android.telephony.TelephonyManager;
28
29public class ConnectivityUtil {
30    // Assume not connected until informed differently
31    private volatile int mCurrentServiceState = ServiceState.STATE_POWER_OFF;
32
33    private final TelephonyManager mTelephonyManager;
34    private final Context mContext;
35    private final ConnectivityBroadcastReceiver mReceiver;
36    private final ConnectivityManager mConnMgr;
37
38    private ConnectivityListener mListener;
39    private final IntentFilter mIntentFilter;
40
41    public interface ConnectivityListener {
42        public void onConnectivityStateChanged(final Context context, final Intent intent);
43        public void onPhoneStateChanged(final Context context, int serviceState);
44    }
45
46    public ConnectivityUtil(final Context context) {
47        mContext = context;
48        mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
49        mConnMgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
50        mReceiver = new ConnectivityBroadcastReceiver();
51        mIntentFilter = new IntentFilter();
52        mIntentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
53    }
54
55    public int getCurrentServiceState() {
56        return mCurrentServiceState;
57    }
58
59    private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
60        @Override
61        public void onServiceStateChanged(final ServiceState serviceState) {
62            if (mCurrentServiceState != serviceState.getState()) {
63                mCurrentServiceState = serviceState.getState();
64                onPhoneStateChanged(mCurrentServiceState);
65            }
66        }
67
68        @Override
69        public void onDataConnectionStateChanged(final int state) {
70            mCurrentServiceState = (state == TelephonyManager.DATA_DISCONNECTED) ?
71                    ServiceState.STATE_OUT_OF_SERVICE : ServiceState.STATE_IN_SERVICE;
72        }
73    };
74
75    private void onPhoneStateChanged(final int serviceState) {
76        final ConnectivityListener listener = mListener;
77        if (listener != null) {
78            listener.onPhoneStateChanged(mContext, serviceState);
79        }
80    }
81
82    private void onConnectivityChanged(final Context context, final Intent intent) {
83        final ConnectivityListener listener = mListener;
84        if (listener != null) {
85            listener.onConnectivityStateChanged(context, intent);
86        }
87    }
88
89    public void register(final ConnectivityListener listener) {
90        Assert.isTrue(mListener == null || mListener == listener);
91        if (mListener == null) {
92            if (mTelephonyManager != null) {
93                mCurrentServiceState = (PhoneUtils.getDefault().isAirplaneModeOn() ?
94                        ServiceState.STATE_POWER_OFF : ServiceState.STATE_IN_SERVICE);
95                mTelephonyManager.listen(mPhoneStateListener,
96                        PhoneStateListener.LISTEN_SERVICE_STATE);
97            }
98            if (mConnMgr != null) {
99                mContext.registerReceiver(mReceiver, mIntentFilter);
100            }
101        }
102        mListener = listener;
103    }
104
105    public void unregister() {
106        if (mListener != null) {
107            if (mTelephonyManager != null) {
108                mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
109                mCurrentServiceState = ServiceState.STATE_POWER_OFF;
110            }
111            if (mConnMgr != null) {
112                mContext.unregisterReceiver(mReceiver);
113            }
114        }
115        mListener = null;
116    }
117
118    /**
119     * Connectivity change broadcast receiver. This gets the network connectivity updates.
120     * In case we don't get the active connectivity when we first acquire the network,
121     * this receiver will notify us when it is connected, so to unblock the waiting thread
122     * which is sending the message.
123     */
124    public class ConnectivityBroadcastReceiver extends BroadcastReceiver {
125        @Override
126        public void onReceive(final Context context, final Intent intent) {
127            if (!intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
128                return;
129            }
130
131            onConnectivityChanged(context, intent);
132        }
133    }
134
135    private int mSignalLevel = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
136
137    // We use a separate instance than mPhoneStateListener because the lifetimes are different.
138    private final PhoneStateListener mSignalStrengthListener = new PhoneStateListener() {
139        @Override
140        public void onSignalStrengthsChanged(final SignalStrength signalStrength) {
141            mSignalLevel = getLevel(signalStrength);
142        }
143    };
144
145    public void registerForSignalStrength() {
146        mTelephonyManager.listen(
147                mSignalStrengthListener, PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);
148    }
149
150    public void unregisterForSignalStrength() {
151        mTelephonyManager.listen(mSignalStrengthListener, PhoneStateListener.LISTEN_NONE);
152    }
153
154    /**
155     * @param subId This is ignored because TelephonyManager does not support it.
156     * @return Signal strength as level 0..4
157     */
158    public int getSignalLevel(final int subId) {
159        return mSignalLevel;
160    }
161
162    private static final int SIGNAL_STRENGTH_NONE_OR_UNKNOWN = 0;
163    private static final int SIGNAL_STRENGTH_POOR = 1;
164    private static final int SIGNAL_STRENGTH_MODERATE = 2;
165    private static final int SIGNAL_STRENGTH_GOOD = 3;
166    private static final int SIGNAL_STRENGTH_GREAT = 4;
167
168    private static final int GSM_SIGNAL_STRENGTH_GREAT = 12;
169    private static final int GSM_SIGNAL_STRENGTH_GOOD = 8;
170    private static final int GSM_SIGNAL_STRENGTH_MODERATE = 8;
171
172    private static int getLevel(final SignalStrength signalStrength) {
173        if (signalStrength.isGsm()) {
174            // From frameworks/base/telephony/java/android/telephony/CellSignalStrengthGsm.java
175
176            // ASU ranges from 0 to 31 - TS 27.007 Sec 8.5
177            // asu = 0 (-113dB or less) is very weak
178            // signal, its better to show 0 bars to the user in such cases.
179            // asu = 99 is a special case, where the signal strength is unknown.
180            final int asu = signalStrength.getGsmSignalStrength();
181            if (asu <= 2 || asu == 99) return SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
182            else if (asu >= GSM_SIGNAL_STRENGTH_GREAT) return SIGNAL_STRENGTH_GREAT;
183            else if (asu >= GSM_SIGNAL_STRENGTH_GOOD) return SIGNAL_STRENGTH_GOOD;
184            else if (asu >= GSM_SIGNAL_STRENGTH_MODERATE) return SIGNAL_STRENGTH_MODERATE;
185            else return SIGNAL_STRENGTH_POOR;
186        } else {
187            // From frameworks/base/telephony/java/android/telephony/CellSignalStrengthCdma.java
188
189            final int cdmaLevel = getCdmaLevel(signalStrength);
190            final int evdoLevel = getEvdoLevel(signalStrength);
191            if (evdoLevel == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
192                /* We don't know evdo, use cdma */
193                return getCdmaLevel(signalStrength);
194            } else if (cdmaLevel == SIGNAL_STRENGTH_NONE_OR_UNKNOWN) {
195                /* We don't know cdma, use evdo */
196                return getEvdoLevel(signalStrength);
197            } else {
198                /* We know both, use the lowest level */
199                return cdmaLevel < evdoLevel ? cdmaLevel : evdoLevel;
200            }
201        }
202    }
203
204    /**
205     * Get cdma as level 0..4
206     */
207    private static int getCdmaLevel(final SignalStrength signalStrength) {
208        final int cdmaDbm = signalStrength.getCdmaDbm();
209        final int cdmaEcio = signalStrength.getCdmaEcio();
210        int levelDbm;
211        int levelEcio;
212        if (cdmaDbm >= -75) levelDbm = SIGNAL_STRENGTH_GREAT;
213        else if (cdmaDbm >= -85) levelDbm = SIGNAL_STRENGTH_GOOD;
214        else if (cdmaDbm >= -95) levelDbm = SIGNAL_STRENGTH_MODERATE;
215        else if (cdmaDbm >= -100) levelDbm = SIGNAL_STRENGTH_POOR;
216        else levelDbm = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
217        // Ec/Io are in dB*10
218        if (cdmaEcio >= -90) levelEcio = SIGNAL_STRENGTH_GREAT;
219        else if (cdmaEcio >= -110) levelEcio = SIGNAL_STRENGTH_GOOD;
220        else if (cdmaEcio >= -130) levelEcio = SIGNAL_STRENGTH_MODERATE;
221        else if (cdmaEcio >= -150) levelEcio = SIGNAL_STRENGTH_POOR;
222        else levelEcio = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
223        final int level = (levelDbm < levelEcio) ? levelDbm : levelEcio;
224        return level;
225    }
226    /**
227     * Get Evdo as level 0..4
228     */
229    private static int getEvdoLevel(final SignalStrength signalStrength) {
230        final int evdoDbm = signalStrength.getEvdoDbm();
231        final int evdoSnr = signalStrength.getEvdoSnr();
232        int levelEvdoDbm;
233        int levelEvdoSnr;
234        if (evdoDbm >= -65) levelEvdoDbm = SIGNAL_STRENGTH_GREAT;
235        else if (evdoDbm >= -75) levelEvdoDbm = SIGNAL_STRENGTH_GOOD;
236        else if (evdoDbm >= -90) levelEvdoDbm = SIGNAL_STRENGTH_MODERATE;
237        else if (evdoDbm >= -105) levelEvdoDbm = SIGNAL_STRENGTH_POOR;
238        else levelEvdoDbm = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
239        if (evdoSnr >= 7) levelEvdoSnr = SIGNAL_STRENGTH_GREAT;
240        else if (evdoSnr >= 5) levelEvdoSnr = SIGNAL_STRENGTH_GOOD;
241        else if (evdoSnr >= 3) levelEvdoSnr = SIGNAL_STRENGTH_MODERATE;
242        else if (evdoSnr >= 1) levelEvdoSnr = SIGNAL_STRENGTH_POOR;
243        else levelEvdoSnr = SIGNAL_STRENGTH_NONE_OR_UNKNOWN;
244        final int level = (levelEvdoDbm < levelEvdoSnr) ? levelEvdoDbm : levelEvdoSnr;
245        return level;
246    }
247}
248