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 
17 package com.android.messaging.util;
18 
19 import android.content.BroadcastReceiver;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.IntentFilter;
23 import android.net.ConnectivityManager;
24 import android.telephony.PhoneStateListener;
25 import android.telephony.ServiceState;
26 import android.telephony.SignalStrength;
27 import android.telephony.TelephonyManager;
28 
29 public 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 {
onConnectivityStateChanged(final Context context, final Intent intent)42         public void onConnectivityStateChanged(final Context context, final Intent intent);
onPhoneStateChanged(final Context context, int serviceState)43         public void onPhoneStateChanged(final Context context, int serviceState);
44     }
45 
ConnectivityUtil(final Context context)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 
getCurrentServiceState()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 
onPhoneStateChanged(final int serviceState)75     private void onPhoneStateChanged(final int serviceState) {
76         final ConnectivityListener listener = mListener;
77         if (listener != null) {
78             listener.onPhoneStateChanged(mContext, serviceState);
79         }
80     }
81 
onConnectivityChanged(final Context context, final Intent intent)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 
register(final ConnectivityListener listener)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 
unregister()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
onReceive(final Context context, final Intent intent)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 
registerForSignalStrength()145     public void registerForSignalStrength() {
146         mTelephonyManager.listen(
147                 mSignalStrengthListener, PhoneStateListener.LISTEN_SIGNAL_STRENGTHS);
148     }
149 
unregisterForSignalStrength()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      */
getSignalLevel(final int subId)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 
getLevel(final SignalStrength signalStrength)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      */
getCdmaLevel(final SignalStrength signalStrength)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      */
getEvdoLevel(final SignalStrength signalStrength)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