blob: 5cf6a6ea157be505aa1308c443c4e1cdb9698975 [file] [log] [blame]
John Spurlockaf8d6c42014-05-07 17:49:08 -04001/*
2 * Copyright (C) 2010 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.systemui.statusbar.policy;
18
Lorenzo Colitti403aa262014-11-28 11:21:30 +090019import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
Jason Monkda68f592015-01-07 10:55:58 -050020
John Spurlockaf8d6c42014-05-07 17:49:08 -040021import android.content.BroadcastReceiver;
22import android.content.Context;
23import android.content.Intent;
24import android.content.IntentFilter;
25import android.content.res.Resources;
26import android.net.ConnectivityManager;
Lorenzo Colitti403aa262014-11-28 11:21:30 +090027import android.net.NetworkCapabilities;
John Spurlockaf8d6c42014-05-07 17:49:08 -040028import android.net.wifi.WifiManager;
John Spurlockaf8d6c42014-05-07 17:49:08 -040029import android.os.AsyncTask;
30import android.os.Bundle;
John Spurlockaf8d6c42014-05-07 17:49:08 -040031import android.provider.Settings;
Jason Monkb5746272014-11-12 16:50:31 -050032import android.telephony.SubscriptionInfo;
33import android.telephony.SubscriptionManager;
34import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
John Spurlockaf8d6c42014-05-07 17:49:08 -040035import android.telephony.TelephonyManager;
Jason Monkb5746272014-11-12 16:50:31 -050036import android.text.TextUtils;
John Spurlockaf8d6c42014-05-07 17:49:08 -040037import android.util.Log;
John Spurlockaf8d6c42014-05-07 17:49:08 -040038
Jason Monke0cdb602014-11-05 12:39:45 -050039import com.android.internal.annotations.VisibleForTesting;
Jason Monkb5746272014-11-12 16:50:31 -050040import com.android.internal.telephony.PhoneConstants;
John Spurlockaf8d6c42014-05-07 17:49:08 -040041import com.android.internal.telephony.TelephonyIntents;
John Spurlockaf8d6c42014-05-07 17:49:08 -040042import com.android.systemui.DemoMode;
43import com.android.systemui.R;
44
45import java.io.FileDescriptor;
46import java.io.PrintWriter;
47import java.util.ArrayList;
Lorenzo Colitti403aa262014-11-28 11:21:30 +090048import java.util.BitSet;
Jason Monkb5746272014-11-12 16:50:31 -050049import java.util.Collections;
50import java.util.Comparator;
Jason Monkd2263cd2014-11-10 14:22:56 -050051import java.util.HashMap;
John Spurlockaf8d6c42014-05-07 17:49:08 -040052import java.util.List;
53import java.util.Locale;
Jason Monkd2263cd2014-11-10 14:22:56 -050054import java.util.Map;
John Spurlockaf8d6c42014-05-07 17:49:08 -040055
56/** Platform implementation of the network controller. **/
57public class NetworkControllerImpl extends BroadcastReceiver
58 implements NetworkController, DemoMode {
59 // debug
Jason Monkd2263cd2014-11-10 14:22:56 -050060 static final String TAG = "NetworkController";
61 static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
62 // additional diagnostics, but not logspew
63 static final boolean CHATTY = Log.isLoggable(TAG + ".Chat", Log.DEBUG);
John Spurlockaf8d6c42014-05-07 17:49:08 -040064
Jason Monkd2263cd2014-11-10 14:22:56 -050065 private final Context mContext;
66 private final TelephonyManager mPhone;
67 private final WifiManager mWifiManager;
68 private final ConnectivityManager mConnectivityManager;
Jason Monkb5746272014-11-12 16:50:31 -050069 private final SubscriptionManager mSubscriptionManager;
Jason Monkd2263cd2014-11-10 14:22:56 -050070 private final boolean mHasMobileDataFeature;
Jason Monk3aaabd72014-12-12 11:11:44 -050071 private Config mConfig;
John Spurlockaf8d6c42014-05-07 17:49:08 -040072
Jason Monkd2263cd2014-11-10 14:22:56 -050073 // Subcontrollers.
74 @VisibleForTesting
75 final WifiSignalController mWifiSignalController;
76 @VisibleForTesting
Jason Monkb5746272014-11-12 16:50:31 -050077 final Map<Integer, MobileSignalController> mMobileSignalControllers =
78 new HashMap<Integer, MobileSignalController>();
79 // When no SIMs are around at setup, and one is added later, it seems to default to the first
80 // SIM for most actions. This may be null if there aren't any SIMs around.
81 private MobileSignalController mDefaultSignalController;
Jason Monk0e2400f2014-11-21 11:08:55 -050082 private final AccessPointControllerImpl mAccessPoints;
Jason Monkd2263cd2014-11-10 14:22:56 -050083 private final MobileDataControllerImpl mMobileDataController;
John Spurlockaf8d6c42014-05-07 17:49:08 -040084
Jason Monkd2263cd2014-11-10 14:22:56 -050085 private boolean mInetCondition; // Used for Logging and demo.
Lorenzo Colitti23dd1762014-09-10 22:58:54 +090086
Lorenzo Colitti403aa262014-11-28 11:21:30 +090087 // BitSets indicating which network transport types (e.g., TRANSPORT_WIFI, TRANSPORT_MOBILE) are
88 // connected and validated, respectively.
89 private final BitSet mConnectedTransports = new BitSet();
90 private final BitSet mValidatedTransports = new BitSet();
91
Jason Monkd2263cd2014-11-10 14:22:56 -050092 // States that don't belong to a subcontroller.
John Spurlockaf8d6c42014-05-07 17:49:08 -040093 private boolean mAirplaneMode = false;
Jason Monkb5746272014-11-12 16:50:31 -050094 private boolean mHasNoSims;
John Spurlockaf8d6c42014-05-07 17:49:08 -040095 private Locale mLocale = null;
Jason Monkb5746272014-11-12 16:50:31 -050096 // This list holds our ordering.
97 private List<SubscriptionInfo> mCurrentSubscriptions
98 = new ArrayList<SubscriptionInfo>();
John Spurlockaf8d6c42014-05-07 17:49:08 -040099
Jason Monkd2263cd2014-11-10 14:22:56 -0500100 // All the callbacks.
101 private ArrayList<EmergencyListener> mEmergencyListeners = new ArrayList<EmergencyListener>();
Jason Monkd2263cd2014-11-10 14:22:56 -0500102 private ArrayList<SignalCluster> mSignalClusters = new ArrayList<SignalCluster>();
103 private ArrayList<NetworkSignalChangedCallback> mSignalsChangedCallbacks =
John Spurlockaf8d6c42014-05-07 17:49:08 -0400104 new ArrayList<NetworkSignalChangedCallback>();
Jason Monkc014dec2014-12-12 11:49:55 -0500105 @VisibleForTesting
106 boolean mListening;
John Spurlock7f8f22a2014-07-02 18:54:17 -0400107
Lorenzo Colitti403aa262014-11-28 11:21:30 +0900108 // The current user ID.
109 private int mCurrentUserId;
110
John Spurlockaf8d6c42014-05-07 17:49:08 -0400111 /**
112 * Construct this controller object and register for updates.
113 */
114 public NetworkControllerImpl(Context context) {
Jason Monkf13b4b32014-11-07 16:39:34 -0500115 this(context, (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE),
116 (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE),
Jason Monk29f7a7b2014-11-17 14:40:56 -0500117 (WifiManager) context.getSystemService(Context.WIFI_SERVICE),
Jason Monkb5746272014-11-12 16:50:31 -0500118 SubscriptionManager.from(context), Config.readConfig(context),
119 new AccessPointControllerImpl(context), new MobileDataControllerImpl(context));
Jason Monkf13b4b32014-11-07 16:39:34 -0500120 registerListeners();
121 }
122
123 @VisibleForTesting
124 NetworkControllerImpl(Context context, ConnectivityManager connectivityManager,
Jason Monkb5746272014-11-12 16:50:31 -0500125 TelephonyManager telephonyManager, WifiManager wifiManager,
126 SubscriptionManager subManager, Config config,
Jason Monkd2263cd2014-11-10 14:22:56 -0500127 AccessPointControllerImpl accessPointController,
128 MobileDataControllerImpl mobileDataController) {
John Spurlockaf8d6c42014-05-07 17:49:08 -0400129 mContext = context;
Jason Monkb5746272014-11-12 16:50:31 -0500130 mConfig = config;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400131
Jason Monkb5746272014-11-12 16:50:31 -0500132 mSubscriptionManager = subManager;
Jason Monkf13b4b32014-11-07 16:39:34 -0500133 mConnectivityManager = connectivityManager;
134 mHasMobileDataFeature =
135 mConnectivityManager.isNetworkSupported(ConnectivityManager.TYPE_MOBILE);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400136
John Spurlockaf8d6c42014-05-07 17:49:08 -0400137 // telephony
Jason Monke0cdb602014-11-05 12:39:45 -0500138 mPhone = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400139
140 // wifi
Jason Monkf13b4b32014-11-07 16:39:34 -0500141 mWifiManager = wifiManager;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400142
Jason Monkd2263cd2014-11-10 14:22:56 -0500143 mLocale = mContext.getResources().getConfiguration().locale;
Jason Monk29f7a7b2014-11-17 14:40:56 -0500144 mAccessPoints = accessPointController;
145 mMobileDataController = mobileDataController;
Jason Monkd2263cd2014-11-10 14:22:56 -0500146 mMobileDataController.setNetworkController(this);
147 // TODO: Find a way to move this into MobileDataController.
148 mMobileDataController.setCallback(new MobileDataControllerImpl.Callback() {
Jason Monke0cdb602014-11-05 12:39:45 -0500149 @Override
150 public void onMobileDataEnabled(boolean enabled) {
151 notifyMobileDataEnabled(enabled);
152 }
153 });
Jason Monkd2263cd2014-11-10 14:22:56 -0500154 mWifiSignalController = new WifiSignalController(mContext, mHasMobileDataFeature,
155 mSignalsChangedCallbacks, mSignalClusters, this);
Jason Monkd2263cd2014-11-10 14:22:56 -0500156
157 // AIRPLANE_MODE_CHANGED is sent at boot; we've probably already missed it
Jason Monkb5746272014-11-12 16:50:31 -0500158 updateAirplaneMode(true /* force callback */);
Jason Monke0cdb602014-11-05 12:39:45 -0500159 }
160
Jason Monkf13b4b32014-11-07 16:39:34 -0500161 private void registerListeners() {
Jason Monkb5746272014-11-12 16:50:31 -0500162 for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) {
163 mobileSignalController.registerListener();
164 }
Wink Saville071743f2015-01-12 17:11:04 -0800165 mSubscriptionManager.addOnSubscriptionsChangedListener(mSubscriptionListener);
Jason Monke0cdb602014-11-05 12:39:45 -0500166
John Spurlockaf8d6c42014-05-07 17:49:08 -0400167 // broadcasts
168 IntentFilter filter = new IntentFilter();
169 filter.addAction(WifiManager.RSSI_CHANGED_ACTION);
170 filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
171 filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
172 filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED);
Jason Monkb5746272014-11-12 16:50:31 -0500173 filter.addAction(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
174 filter.addAction(TelephonyIntents.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400175 filter.addAction(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION);
Lorenzo Colitti23dd1762014-09-10 22:58:54 +0900176 filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400177 filter.addAction(ConnectivityManager.INET_CONDITION_ACTION);
178 filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
179 filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
Jason Monke0cdb602014-11-05 12:39:45 -0500180 mContext.registerReceiver(this, filter);
Jason Monkb5746272014-11-12 16:50:31 -0500181 mListening = true;
182
183 updateMobileControllers();
John Spurlockb98f7472014-07-08 17:09:42 -0400184 }
185
Jason Monkd2263cd2014-11-10 14:22:56 -0500186 private void unregisterListeners() {
Jason Monkb5746272014-11-12 16:50:31 -0500187 mListening = false;
188 for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) {
189 mobileSignalController.unregisterListener();
190 }
Wink Saville071743f2015-01-12 17:11:04 -0800191 mSubscriptionManager.removeOnSubscriptionsChangedListener(mSubscriptionListener);
Jason Monkd2263cd2014-11-10 14:22:56 -0500192 mContext.unregisterReceiver(this);
Jason Monk5d325072014-10-27 11:38:47 -0400193 }
194
Jason Monk0e2400f2014-11-21 11:08:55 -0500195 public int getConnectedWifiLevel() {
196 return mWifiSignalController.getState().level;
197 }
198
Jason Monk5d325072014-10-27 11:38:47 -0400199 @Override
Jason Monkd2263cd2014-11-10 14:22:56 -0500200 public AccessPointController getAccessPointController() {
201 return mAccessPoints;
202 }
203
204 @Override
205 public MobileDataController getMobileDataController() {
206 return mMobileDataController;
207 }
208
209 public void addEmergencyListener(EmergencyListener listener) {
210 mEmergencyListeners.add(listener);
Jason Monkb5746272014-11-12 16:50:31 -0500211 listener.setEmergencyCallsOnly(isEmergencyOnly());
Jason Monkd2263cd2014-11-10 14:22:56 -0500212 }
213
John Spurlockb98f7472014-07-08 17:09:42 -0400214 private void notifyMobileDataEnabled(boolean enabled) {
Jason Monkb5746272014-11-12 16:50:31 -0500215 final int length = mSignalsChangedCallbacks.size();
Jason Monkd2263cd2014-11-10 14:22:56 -0500216 for (int i = 0; i < length; i++) {
217 mSignalsChangedCallbacks.get(i).onMobileDataEnabled(enabled);
John Spurlockb98f7472014-07-08 17:09:42 -0400218 }
John Spurlockaf8d6c42014-05-07 17:49:08 -0400219 }
220
221 public boolean hasMobileDataFeature() {
222 return mHasMobileDataFeature;
223 }
224
225 public boolean hasVoiceCallingFeature() {
226 return mPhone.getPhoneType() != TelephonyManager.PHONE_TYPE_NONE;
227 }
228
Jason Monkb5746272014-11-12 16:50:31 -0500229 private MobileSignalController getDataController() {
230 int dataSubId = SubscriptionManager.getDefaultDataSubId();
Wink Savillea54bf652014-12-11 13:37:50 -0800231 if (!SubscriptionManager.isValidSubscriptionId(dataSubId)) {
Jason Monkb5746272014-11-12 16:50:31 -0500232 if (DEBUG) Log.e(TAG, "No data sim selected");
233 return mDefaultSignalController;
234 }
235 if (mMobileSignalControllers.containsKey(dataSubId)) {
236 return mMobileSignalControllers.get(dataSubId);
237 }
Dan Sandlerc8466322014-12-08 15:42:52 -0500238 if (DEBUG) Log.e(TAG, "Cannot find controller for data sub: " + dataSubId);
Jason Monkb5746272014-11-12 16:50:31 -0500239 return mDefaultSignalController;
240 }
241
Jason Monkd2263cd2014-11-10 14:22:56 -0500242 public String getMobileNetworkName() {
Jason Monkb5746272014-11-12 16:50:31 -0500243 MobileSignalController controller = getDataController();
244 return controller != null ? controller.getState().networkName : "";
Jason Monkd2263cd2014-11-10 14:22:56 -0500245 }
246
John Spurlockaf8d6c42014-05-07 17:49:08 -0400247 public boolean isEmergencyOnly() {
Jason Monkb5746272014-11-12 16:50:31 -0500248 int voiceSubId = SubscriptionManager.getDefaultVoiceSubId();
Wink Savillea54bf652014-12-11 13:37:50 -0800249 if (!SubscriptionManager.isValidSubscriptionId(voiceSubId)) {
250 for (MobileSignalController mobileSignalController :
251 mMobileSignalControllers.values()) {
Jason Monkb5746272014-11-12 16:50:31 -0500252 if (!mobileSignalController.isEmergencyOnly()) {
253 return false;
254 }
255 }
256 }
257 if (mMobileSignalControllers.containsKey(voiceSubId)) {
258 return mMobileSignalControllers.get(voiceSubId).isEmergencyOnly();
259 }
Dan Sandlerc8466322014-12-08 15:42:52 -0500260 if (DEBUG) Log.e(TAG, "Cannot find controller for voice sub: " + voiceSubId);
Jason Monkb5746272014-11-12 16:50:31 -0500261 // Something is wrong, better assume we can't make calls...
262 return true;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400263 }
264
Jason Monkd2263cd2014-11-10 14:22:56 -0500265 /**
266 * Emergency status may have changed (triggered by MobileSignalController),
267 * so we should recheck and send out the state to listeners.
268 */
269 void recalculateEmergency() {
270 final boolean emergencyOnly = isEmergencyOnly();
Jason Monkb5746272014-11-12 16:50:31 -0500271 final int length = mEmergencyListeners.size();
Jason Monkd2263cd2014-11-10 14:22:56 -0500272 for (int i = 0; i < length; i++) {
273 mEmergencyListeners.get(i).setEmergencyCallsOnly(emergencyOnly);
274 }
John Spurlockaf8d6c42014-05-07 17:49:08 -0400275 }
276
277 public void addSignalCluster(SignalCluster cluster) {
278 mSignalClusters.add(cluster);
Jason Monkb5746272014-11-12 16:50:31 -0500279 cluster.setSubs(mCurrentSubscriptions);
Jason Monkd2263cd2014-11-10 14:22:56 -0500280 cluster.setIsAirplaneMode(mAirplaneMode, TelephonyIcons.FLIGHT_MODE_ICON,
281 R.string.accessibility_airplane_mode);
Jason Monkb5746272014-11-12 16:50:31 -0500282 cluster.setNoSims(mHasNoSims);
Jason Monkd2263cd2014-11-10 14:22:56 -0500283 mWifiSignalController.notifyListeners();
Jason Monkb5746272014-11-12 16:50:31 -0500284 for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) {
285 mobileSignalController.notifyListeners();
286 }
John Spurlockaf8d6c42014-05-07 17:49:08 -0400287 }
288
289 public void addNetworkSignalChangedCallback(NetworkSignalChangedCallback cb) {
290 mSignalsChangedCallbacks.add(cb);
Jason Monkd2263cd2014-11-10 14:22:56 -0500291 cb.onAirplaneModeChanged(mAirplaneMode);
Jason Monkb5746272014-11-12 16:50:31 -0500292 cb.onNoSimVisibleChanged(mHasNoSims);
Jason Monkd2263cd2014-11-10 14:22:56 -0500293 mWifiSignalController.notifyListeners();
Jason Monkb5746272014-11-12 16:50:31 -0500294 for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) {
295 mobileSignalController.notifyListeners();
296 }
John Spurlockaf8d6c42014-05-07 17:49:08 -0400297 }
298
299 public void removeNetworkSignalChangedCallback(NetworkSignalChangedCallback cb) {
300 mSignalsChangedCallbacks.remove(cb);
301 }
302
303 @Override
304 public void setWifiEnabled(final boolean enabled) {
305 new AsyncTask<Void, Void, Void>() {
306 @Override
307 protected Void doInBackground(Void... args) {
308 // Disable tethering if enabling Wifi
309 final int wifiApState = mWifiManager.getWifiApState();
310 if (enabled && ((wifiApState == WifiManager.WIFI_AP_STATE_ENABLING) ||
Jason Monkd2263cd2014-11-10 14:22:56 -0500311 (wifiApState == WifiManager.WIFI_AP_STATE_ENABLED))) {
John Spurlockaf8d6c42014-05-07 17:49:08 -0400312 mWifiManager.setWifiApEnabled(null, false);
313 }
314
315 mWifiManager.setWifiEnabled(enabled);
316 return null;
317 }
318 }.execute();
319 }
320
John Spurlockb98f7472014-07-08 17:09:42 -0400321 @Override
Lorenzo Colitti403aa262014-11-28 11:21:30 +0900322 public void onUserSwitched(int newUserId) {
323 mCurrentUserId = newUserId;
324 mAccessPoints.onUserSwitched(newUserId);
325 updateConnectivity();
Lorenzo Colitti403aa262014-11-28 11:21:30 +0900326 }
327
328 @Override
John Spurlockaf8d6c42014-05-07 17:49:08 -0400329 public void onReceive(Context context, Intent intent) {
Jason Monkd2263cd2014-11-10 14:22:56 -0500330 if (CHATTY) {
331 Log.d(TAG, "onReceive: intent=" + intent);
332 }
John Spurlockaf8d6c42014-05-07 17:49:08 -0400333 final String action = intent.getAction();
Jason Monkd2263cd2014-11-10 14:22:56 -0500334 if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE) ||
335 action.equals(ConnectivityManager.INET_CONDITION_ACTION)) {
Lorenzo Colitti403aa262014-11-28 11:21:30 +0900336 updateConnectivity();
John Spurlockaf8d6c42014-05-07 17:49:08 -0400337 } else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
Jason Monk3aaabd72014-12-12 11:11:44 -0500338 mConfig = Config.readConfig(mContext);
339 handleConfigurationChanged();
John Spurlockaf8d6c42014-05-07 17:49:08 -0400340 } else if (action.equals(Intent.ACTION_AIRPLANE_MODE_CHANGED)) {
341 refreshLocale();
Jason Monkd2263cd2014-11-10 14:22:56 -0500342 updateAirplaneMode(false);
Jason Monkb5746272014-11-12 16:50:31 -0500343 } else if (action.equals(TelephonyIntents.ACTION_DEFAULT_VOICE_SUBSCRIPTION_CHANGED)) {
344 // We are using different subs now, we might be able to make calls.
345 recalculateEmergency();
346 } else if (action.equals(TelephonyIntents.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)) {
347 // Notify every MobileSignalController so they can know whether they are the
348 // data sim or not.
349 for (MobileSignalController controller : mMobileSignalControllers.values()) {
350 controller.handleBroadcast(intent);
351 }
352 } else if (action.equals(TelephonyIntents.ACTION_SIM_STATE_CHANGED)) {
353 // Might have different subscriptions now.
354 updateMobileControllers();
355 } else {
356 int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
357 SubscriptionManager.INVALID_SUBSCRIPTION_ID);
Wink Savillea54bf652014-12-11 13:37:50 -0800358 if (SubscriptionManager.isValidSubscriptionId(subId)) {
Jason Monkb5746272014-11-12 16:50:31 -0500359 if (mMobileSignalControllers.containsKey(subId)) {
360 mMobileSignalControllers.get(subId).handleBroadcast(intent);
361 } else {
362 // Can't find this subscription... We must be out of date.
363 updateMobileControllers();
364 }
365 } else {
366 // No sub id, must be for the wifi.
367 mWifiSignalController.handleBroadcast(intent);
368 }
John Spurlockaf8d6c42014-05-07 17:49:08 -0400369 }
Jason Monkb5746272014-11-12 16:50:31 -0500370 }
371
Jason Monk3aaabd72014-12-12 11:11:44 -0500372 @VisibleForTesting
373 void handleConfigurationChanged() {
374 for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) {
375 mobileSignalController.setConfiguration(mConfig);
376 }
377 refreshLocale();
Jason Monk3aaabd72014-12-12 11:11:44 -0500378 }
379
Jason Monkb5746272014-11-12 16:50:31 -0500380 private void updateMobileControllers() {
381 if (!mListening) {
382 return;
383 }
384 List<SubscriptionInfo> subscriptions = mSubscriptionManager.getActiveSubscriptionInfoList();
Jason Monkc9f05712014-12-15 12:24:10 -0500385 if (subscriptions == null) {
386 subscriptions = Collections.emptyList();
387 }
Jason Monkb5746272014-11-12 16:50:31 -0500388 // If there have been no relevant changes to any of the subscriptions, we can leave as is.
389 if (hasCorrectMobileControllers(subscriptions)) {
390 // Even if the controllers are correct, make sure we have the right no sims state.
391 // Such as on boot, don't need any controllers, because there are no sims,
392 // but we still need to update the no sim state.
393 updateNoSims();
394 return;
395 }
396 setCurrentSubscriptions(subscriptions);
397 updateNoSims();
398 }
399
Jason Monk21d05a02014-12-02 12:46:58 -0500400 @VisibleForTesting
401 protected void updateNoSims() {
402 boolean hasNoSims = mHasMobileDataFeature && mMobileSignalControllers.size() == 0;
Jason Monkb5746272014-11-12 16:50:31 -0500403 if (hasNoSims != mHasNoSims) {
404 mHasNoSims = hasNoSims;
405 notifyListeners();
406 }
407 }
408
409 @VisibleForTesting
410 void setCurrentSubscriptions(List<SubscriptionInfo> subscriptions) {
411 Collections.sort(subscriptions, new Comparator<SubscriptionInfo>() {
412 @Override
413 public int compare(SubscriptionInfo lhs, SubscriptionInfo rhs) {
414 return lhs.getSimSlotIndex() == rhs.getSimSlotIndex()
415 ? lhs.getSubscriptionId() - rhs.getSubscriptionId()
416 : lhs.getSimSlotIndex() - rhs.getSimSlotIndex();
417 }
418 });
419 final int length = mSignalClusters.size();
420 for (int i = 0; i < length; i++) {
421 mSignalClusters.get(i).setSubs(subscriptions);
422 }
423 mCurrentSubscriptions = subscriptions;
424
425 HashMap<Integer, MobileSignalController> cachedControllers =
426 new HashMap<Integer, MobileSignalController>(mMobileSignalControllers);
427 mMobileSignalControllers.clear();
428 final int num = subscriptions.size();
429 for (int i = 0; i < num; i++) {
430 int subId = subscriptions.get(i).getSubscriptionId();
431 // If we have a copy of this controller already reuse it, otherwise make a new one.
432 if (cachedControllers.containsKey(subId)) {
Jason Monkc014dec2014-12-12 11:49:55 -0500433 mMobileSignalControllers.put(subId, cachedControllers.remove(subId));
Jason Monkb5746272014-11-12 16:50:31 -0500434 } else {
435 MobileSignalController controller = new MobileSignalController(mContext, mConfig,
436 mHasMobileDataFeature, mPhone, mSignalsChangedCallbacks, mSignalClusters,
437 this, subscriptions.get(i));
438 mMobileSignalControllers.put(subId, controller);
439 if (subscriptions.get(i).getSimSlotIndex() == 0) {
440 mDefaultSignalController = controller;
441 }
442 if (mListening) {
443 controller.registerListener();
444 }
445 }
446 }
447 if (mListening) {
448 for (Integer key : cachedControllers.keySet()) {
449 if (cachedControllers.get(key) == mDefaultSignalController) {
450 mDefaultSignalController = null;
451 }
452 cachedControllers.get(key).unregisterListener();
453 }
454 }
Jason Monk25d8a482014-12-09 12:27:24 -0500455 // There may be new MobileSignalControllers around, make sure they get the current
456 // inet condition and airplane mode.
457 pushConnectivityToSignals();
458 updateAirplaneMode(true /* force */);
Jason Monkb5746272014-11-12 16:50:31 -0500459 }
460
Jason Monkc014dec2014-12-12 11:49:55 -0500461 @VisibleForTesting
462 boolean hasCorrectMobileControllers(List<SubscriptionInfo> allSubscriptions) {
Jason Monkc9f05712014-12-15 12:24:10 -0500463 if (allSubscriptions.size() != mMobileSignalControllers.size()) {
464 return false;
Jason Monkb5746272014-11-12 16:50:31 -0500465 }
466 for (SubscriptionInfo info : allSubscriptions) {
467 if (!mMobileSignalControllers.containsKey(info.getSubscriptionId())) {
468 return false;
469 }
470 }
471 return true;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400472 }
473
Jason Monkd2263cd2014-11-10 14:22:56 -0500474 private void updateAirplaneMode(boolean force) {
475 boolean airplaneMode = (Settings.Global.getInt(mContext.getContentResolver(),
476 Settings.Global.AIRPLANE_MODE_ON, 0) == 1);
477 if (airplaneMode != mAirplaneMode || force) {
478 mAirplaneMode = airplaneMode;
Jason Monkb5746272014-11-12 16:50:31 -0500479 for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) {
480 mobileSignalController.setAirplaneMode(mAirplaneMode);
481 }
482 notifyListeners();
John Spurlockaf8d6c42014-05-07 17:49:08 -0400483 }
John Spurlockaf8d6c42014-05-07 17:49:08 -0400484 }
485
486 private void refreshLocale() {
Jason Monkd2263cd2014-11-10 14:22:56 -0500487 Locale current = mContext.getResources().getConfiguration().locale;
Jason Monkb5746272014-11-12 16:50:31 -0500488 if (!current.equals(mLocale)) {
Jason Monkd2263cd2014-11-10 14:22:56 -0500489 mLocale = current;
490 notifyAllListeners();
John Spurlockaf8d6c42014-05-07 17:49:08 -0400491 }
492 }
493
Jason Monkd2263cd2014-11-10 14:22:56 -0500494 /**
Jason Monkb5746272014-11-12 16:50:31 -0500495 * Forces update of all callbacks on both SignalClusters and
496 * NetworkSignalChangedCallbacks.
497 */
Jason Monkd2263cd2014-11-10 14:22:56 -0500498 private void notifyAllListeners() {
Jason Monkb5746272014-11-12 16:50:31 -0500499 notifyListeners();
500 for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) {
501 mobileSignalController.notifyListeners();
502 }
Jason Monkd2263cd2014-11-10 14:22:56 -0500503 mWifiSignalController.notifyListeners();
504 }
John Spurlockaf8d6c42014-05-07 17:49:08 -0400505
Jason Monkb5746272014-11-12 16:50:31 -0500506 /**
507 * Notifies listeners of changes in state of to the NetworkController, but
508 * does not notify for any info on SignalControllers, for that call
509 * notifyAllListeners.
510 */
511 private void notifyListeners() {
Jason Monkd2263cd2014-11-10 14:22:56 -0500512 int length = mSignalClusters.size();
513 for (int i = 0; i < length; i++) {
514 mSignalClusters.get(i).setIsAirplaneMode(mAirplaneMode, TelephonyIcons.FLIGHT_MODE_ICON,
515 R.string.accessibility_airplane_mode);
Jason Monkb5746272014-11-12 16:50:31 -0500516 mSignalClusters.get(i).setNoSims(mHasNoSims);
Jason Monkd2263cd2014-11-10 14:22:56 -0500517 }
Jason Monkd2263cd2014-11-10 14:22:56 -0500518 int signalsChangedLength = mSignalsChangedCallbacks.size();
519 for (int i = 0; i < signalsChangedLength; i++) {
520 mSignalsChangedCallbacks.get(i).onAirplaneModeChanged(mAirplaneMode);
Jason Monkb5746272014-11-12 16:50:31 -0500521 mSignalsChangedCallbacks.get(i).onNoSimVisibleChanged(mHasNoSims);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400522 }
523 }
524
Jason Monkd2263cd2014-11-10 14:22:56 -0500525 /**
526 * Update the Inet conditions and what network we are connected to.
527 */
Lorenzo Colitti403aa262014-11-28 11:21:30 +0900528 private void updateConnectivity() {
529 mConnectedTransports.clear();
530 mValidatedTransports.clear();
531 for (NetworkCapabilities nc :
532 mConnectivityManager.getDefaultNetworkCapabilitiesForUser(mCurrentUserId)) {
533 for (int transportType : nc.getTransportTypes()) {
534 mConnectedTransports.set(transportType);
535 if (nc.hasCapability(NET_CAPABILITY_VALIDATED)) {
536 mValidatedTransports.set(transportType);
537 }
538 }
John Spurlockaf8d6c42014-05-07 17:49:08 -0400539 }
540
John Spurlockaf8d6c42014-05-07 17:49:08 -0400541 if (CHATTY) {
Lorenzo Colitti403aa262014-11-28 11:21:30 +0900542 Log.d(TAG, "updateConnectivity: mConnectedTransports=" + mConnectedTransports);
543 Log.d(TAG, "updateConnectivity: mValidatedTransports=" + mValidatedTransports);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400544 }
545
Lorenzo Colitti403aa262014-11-28 11:21:30 +0900546 mInetCondition = !mValidatedTransports.isEmpty();
John Spurlockaf8d6c42014-05-07 17:49:08 -0400547
Jason Monk25d8a482014-12-09 12:27:24 -0500548 pushConnectivityToSignals();
549 }
550
551 /**
552 * Pushes the current connectivity state to all SignalControllers.
553 */
554 private void pushConnectivityToSignals() {
John Spurlockaf8d6c42014-05-07 17:49:08 -0400555 // We want to update all the icons, all at once, for any condition change
Jason Monkb5746272014-11-12 16:50:31 -0500556 for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) {
Lorenzo Colitti403aa262014-11-28 11:21:30 +0900557 mobileSignalController.setInetCondition(
558 mInetCondition ? 1 : 0,
559 mValidatedTransports.get(mobileSignalController.getTransportType()) ? 1 : 0);
Jason Monkb5746272014-11-12 16:50:31 -0500560 }
Jason Monkd2263cd2014-11-10 14:22:56 -0500561 mWifiSignalController.setInetCondition(
Lorenzo Colitti403aa262014-11-28 11:21:30 +0900562 mValidatedTransports.get(mWifiSignalController.getTransportType()) ? 1 : 0);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400563 }
564
John Spurlockaf8d6c42014-05-07 17:49:08 -0400565 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
566 pw.println("NetworkController state:");
Lorenzo Colitti403aa262014-11-28 11:21:30 +0900567
John Spurlockaf8d6c42014-05-07 17:49:08 -0400568 pw.println(" - telephony ------");
569 pw.print(" hasVoiceCallingFeature()=");
570 pw.println(hasVoiceCallingFeature());
John Spurlockaf8d6c42014-05-07 17:49:08 -0400571
John Spurlockaf8d6c42014-05-07 17:49:08 -0400572 pw.println(" - connectivity ------");
Lorenzo Colitti403aa262014-11-28 11:21:30 +0900573 pw.print(" mConnectedTransports=");
574 pw.println(mConnectedTransports);
575 pw.print(" mValidatedTransports=");
576 pw.println(mValidatedTransports);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400577 pw.print(" mInetCondition=");
578 pw.println(mInetCondition);
Jason Monkd2263cd2014-11-10 14:22:56 -0500579 pw.print(" mAirplaneMode=");
580 pw.println(mAirplaneMode);
581 pw.print(" mLocale=");
582 pw.println(mLocale);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400583
Jason Monkb5746272014-11-12 16:50:31 -0500584 for (MobileSignalController mobileSignalController : mMobileSignalControllers.values()) {
585 mobileSignalController.dump(pw);
586 }
Jason Monkd2263cd2014-11-10 14:22:56 -0500587 mWifiSignalController.dump(pw);
Jason Monkd52356a2015-01-28 10:40:41 -0500588
589 mAccessPoints.dump(pw);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400590 }
591
592 private boolean mDemoMode;
593 private int mDemoInetCondition;
Jason Monkd2263cd2014-11-10 14:22:56 -0500594 private WifiSignalController.WifiState mDemoWifiState;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400595
596 @Override
597 public void dispatchDemoCommand(String command, Bundle args) {
598 if (!mDemoMode && command.equals(COMMAND_ENTER)) {
Jason Monkd2263cd2014-11-10 14:22:56 -0500599 if (DEBUG) Log.d(TAG, "Entering demo mode");
600 unregisterListeners();
John Spurlockaf8d6c42014-05-07 17:49:08 -0400601 mDemoMode = true;
Jason Monkd2263cd2014-11-10 14:22:56 -0500602 mDemoInetCondition = mInetCondition ? 1 : 0;
603 mDemoWifiState = mWifiSignalController.getState();
John Spurlockaf8d6c42014-05-07 17:49:08 -0400604 } else if (mDemoMode && command.equals(COMMAND_EXIT)) {
Jason Monkd2263cd2014-11-10 14:22:56 -0500605 if (DEBUG) Log.d(TAG, "Exiting demo mode");
John Spurlockaf8d6c42014-05-07 17:49:08 -0400606 mDemoMode = false;
Jason Monkb5746272014-11-12 16:50:31 -0500607 // Update what MobileSignalControllers, because they may change
608 // to set the number of sim slots.
609 updateMobileControllers();
610 for (MobileSignalController controller : mMobileSignalControllers.values()) {
611 controller.resetLastState();
612 }
Jason Monkd2263cd2014-11-10 14:22:56 -0500613 mWifiSignalController.resetLastState();
Jason Monkd2263cd2014-11-10 14:22:56 -0500614 registerListeners();
615 notifyAllListeners();
John Spurlockaf8d6c42014-05-07 17:49:08 -0400616 } else if (mDemoMode && command.equals(COMMAND_NETWORK)) {
617 String airplane = args.getString("airplane");
618 if (airplane != null) {
619 boolean show = airplane.equals("show");
Jason Monkd2263cd2014-11-10 14:22:56 -0500620 int length = mSignalClusters.size();
621 for (int i = 0; i < length; i++) {
622 mSignalClusters.get(i).setIsAirplaneMode(show, TelephonyIcons.FLIGHT_MODE_ICON,
623 R.string.accessibility_airplane_mode);
John Spurlockaf8d6c42014-05-07 17:49:08 -0400624 }
625 }
626 String fully = args.getString("fully");
627 if (fully != null) {
628 mDemoInetCondition = Boolean.parseBoolean(fully) ? 1 : 0;
Jason Monkd2263cd2014-11-10 14:22:56 -0500629 mWifiSignalController.setInetCondition(mDemoInetCondition);
Jason Monkb5746272014-11-12 16:50:31 -0500630 for (MobileSignalController controller : mMobileSignalControllers.values()) {
631 controller.setInetCondition(mDemoInetCondition, mDemoInetCondition);
632 }
John Spurlockaf8d6c42014-05-07 17:49:08 -0400633 }
634 String wifi = args.getString("wifi");
635 if (wifi != null) {
636 boolean show = wifi.equals("show");
637 String level = args.getString("level");
638 if (level != null) {
Jason Monkd2263cd2014-11-10 14:22:56 -0500639 mDemoWifiState.level = level.equals("null") ? -1
John Spurlockaf8d6c42014-05-07 17:49:08 -0400640 : Math.min(Integer.parseInt(level), WifiIcons.WIFI_LEVEL_COUNT - 1);
Jason Monkd2263cd2014-11-10 14:22:56 -0500641 mDemoWifiState.connected = mDemoWifiState.level >= 0;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400642 }
Jason Monkd2263cd2014-11-10 14:22:56 -0500643 mDemoWifiState.enabled = show;
644 mWifiSignalController.notifyListeners();
John Spurlockaf8d6c42014-05-07 17:49:08 -0400645 }
Jason Monkb5746272014-11-12 16:50:31 -0500646 String sims = args.getString("sims");
647 if (sims != null) {
648 int num = Integer.parseInt(sims);
649 List<SubscriptionInfo> subs = new ArrayList<SubscriptionInfo>();
650 if (num != mMobileSignalControllers.size()) {
651 mMobileSignalControllers.clear();
652 int start = mSubscriptionManager.getActiveSubscriptionInfoCountMax();
653 for (int i = start /* get out of normal index range */; i < start + num; i++) {
654 SubscriptionInfo info = new SubscriptionInfo(i, "", i, "", "", 0, 0, "", 0,
655 null, 0, 0, "");
656 subs.add(info);
657 mMobileSignalControllers.put(i, new MobileSignalController(mContext,
658 mConfig, mHasMobileDataFeature, mPhone, mSignalsChangedCallbacks,
659 mSignalClusters, this, info));
660 }
661 }
662 final int n = mSignalClusters.size();
663 for (int i = 0; i < n; i++) {
664 mSignalClusters.get(i).setSubs(subs);
665 }
666 }
667 String nosim = args.getString("nosim");
668 if (nosim != null) {
669 boolean show = nosim.equals("show");
670 final int n = mSignalClusters.size();
671 for (int i = 0; i < n; i++) {
672 mSignalClusters.get(i).setNoSims(show);
673 }
674 }
John Spurlockaf8d6c42014-05-07 17:49:08 -0400675 String mobile = args.getString("mobile");
676 if (mobile != null) {
677 boolean show = mobile.equals("show");
678 String datatype = args.getString("datatype");
Jason Monkb5746272014-11-12 16:50:31 -0500679 String slotString = args.getString("slot");
680 int slot = TextUtils.isEmpty(slotString) ? 0 : Integer.parseInt(slotString);
681 // Hack to index linearly for easy use.
682 MobileSignalController controller = mMobileSignalControllers
683 .values().toArray(new MobileSignalController[0])[slot];
684 controller.getState().dataSim = datatype != null;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400685 if (datatype != null) {
Jason Monkb5746272014-11-12 16:50:31 -0500686 controller.getState().iconGroup =
Jason Monkd2263cd2014-11-10 14:22:56 -0500687 datatype.equals("1x") ? TelephonyIcons.ONE_X :
688 datatype.equals("3g") ? TelephonyIcons.THREE_G :
689 datatype.equals("4g") ? TelephonyIcons.FOUR_G :
690 datatype.equals("e") ? TelephonyIcons.E :
691 datatype.equals("g") ? TelephonyIcons.G :
692 datatype.equals("h") ? TelephonyIcons.H :
693 datatype.equals("lte") ? TelephonyIcons.LTE :
694 datatype.equals("roam") ? TelephonyIcons.ROAMING :
695 TelephonyIcons.UNKNOWN;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400696 }
697 int[][] icons = TelephonyIcons.TELEPHONY_SIGNAL_STRENGTH;
698 String level = args.getString("level");
699 if (level != null) {
Jason Monkb5746272014-11-12 16:50:31 -0500700 controller.getState().level = level.equals("null") ? -1
John Spurlockaf8d6c42014-05-07 17:49:08 -0400701 : Math.min(Integer.parseInt(level), icons[0].length - 1);
Jason Monkb5746272014-11-12 16:50:31 -0500702 controller.getState().connected = controller.getState().level >= 0;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400703 }
Jason Monkb5746272014-11-12 16:50:31 -0500704 controller.getState().enabled = show;
705 controller.notifyListeners();
John Spurlockaf8d6c42014-05-07 17:49:08 -0400706 }
Andrew Flynna478d702015-04-14 23:33:45 -0400707 String carrierNetworkChange = args.getString("carriernetworkchange");
708 if (carrierNetworkChange != null) {
709 boolean show = carrierNetworkChange.equals("show");
710 for (MobileSignalController controller : mMobileSignalControllers.values()) {
711 controller.setCarrierNetworkChangeMode(show);
712 }
713 }
Jason Monkd2263cd2014-11-10 14:22:56 -0500714 }
715 }
716
Jason Monkb5746272014-11-12 16:50:31 -0500717 private final OnSubscriptionsChangedListener mSubscriptionListener =
718 new OnSubscriptionsChangedListener() {
Jason Monkc9f05712014-12-15 12:24:10 -0500719 @Override
720 public void onSubscriptionsChanged() {
Jason Monkb5746272014-11-12 16:50:31 -0500721 updateMobileControllers();
722 };
723 };
724
Jason Monkd2263cd2014-11-10 14:22:56 -0500725 public interface SignalCluster {
726 void setWifiIndicators(boolean visible, int strengthIcon, String contentDescription);
727
Andrew Flynna478d702015-04-14 23:33:45 -0400728 void setMobileDataIndicators(boolean visible, int strengthIcon, int darkStrengthIcon,
729 int typeIcon, String contentDescription, String typeContentDescription,
730 boolean isTypeIconWide, int subId);
Jason Monkb5746272014-11-12 16:50:31 -0500731 void setSubs(List<SubscriptionInfo> subs);
732 void setNoSims(boolean show);
Jason Monkd2263cd2014-11-10 14:22:56 -0500733
734 void setIsAirplaneMode(boolean is, int airplaneIcon, int contentDescription);
735 }
736
737 public interface EmergencyListener {
738 void setEmergencyCallsOnly(boolean emergencyOnly);
739 }
740
741 public interface CarrierLabelListener {
742 void setCarrierLabel(String label);
743 }
744
745 @VisibleForTesting
746 static class Config {
Jason Monkb5746272014-11-12 16:50:31 -0500747 boolean showAtLeast3G = false;
Jason Monkd2263cd2014-11-10 14:22:56 -0500748 boolean alwaysShowCdmaRssi = false;
749 boolean show4gForLte = false;
750 boolean hspaDataDistinguishable;
751
752 static Config readConfig(Context context) {
753 Config config = new Config();
754 Resources res = context.getResources();
755
Jason Monkb5746272014-11-12 16:50:31 -0500756 config.showAtLeast3G = res.getBoolean(R.bool.config_showMin3G);
Jason Monkd2263cd2014-11-10 14:22:56 -0500757 config.alwaysShowCdmaRssi =
758 res.getBoolean(com.android.internal.R.bool.config_alwaysUseCdmaRssi);
759 config.show4gForLte = res.getBoolean(R.bool.config_show4GForLTE);
760 config.hspaDataDistinguishable =
761 res.getBoolean(R.bool.config_hspa_data_distinguishable);
762 return config;
John Spurlockaf8d6c42014-05-07 17:49:08 -0400763 }
764 }
765}