• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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.systemui.qs.tiles.dialog;
18 
19 import static android.telephony.SubscriptionManager.PROFILE_CLASS_PROVISIONING;
20 
21 import static com.android.settingslib.mobile.MobileMappings.getIconKey;
22 import static com.android.settingslib.mobile.MobileMappings.mapIconSets;
23 import static com.android.settingslib.wifi.WifiUtils.getHotspotIconResource;
24 import static com.android.wifitrackerlib.WifiEntry.CONNECTED_STATE_CONNECTED;
25 
26 import android.animation.Animator;
27 import android.animation.AnimatorListenerAdapter;
28 import android.annotation.AnyThread;
29 import android.content.BroadcastReceiver;
30 import android.content.Context;
31 import android.content.Intent;
32 import android.content.IntentFilter;
33 import android.content.res.Resources;
34 import android.graphics.Color;
35 import android.graphics.PixelFormat;
36 import android.graphics.drawable.ColorDrawable;
37 import android.graphics.drawable.Drawable;
38 import android.graphics.drawable.LayerDrawable;
39 import android.net.ConnectivityManager;
40 import android.net.Network;
41 import android.net.NetworkCapabilities;
42 import android.net.wifi.WifiManager;
43 import android.os.Bundle;
44 import android.os.Handler;
45 import android.os.UserHandle;
46 import android.provider.Settings;
47 import android.telephony.AccessNetworkConstants;
48 import android.telephony.NetworkRegistrationInfo;
49 import android.telephony.ServiceState;
50 import android.telephony.SignalStrength;
51 import android.telephony.SubscriptionInfo;
52 import android.telephony.SubscriptionManager;
53 import android.telephony.TelephonyCallback;
54 import android.telephony.TelephonyDisplayInfo;
55 import android.telephony.TelephonyManager;
56 import android.text.TextUtils;
57 import android.util.Log;
58 import android.view.Gravity;
59 import android.view.View;
60 import android.view.WindowManager;
61 
62 import androidx.annotation.MainThread;
63 import androidx.annotation.NonNull;
64 import androidx.annotation.Nullable;
65 import androidx.annotation.VisibleForTesting;
66 import androidx.annotation.WorkerThread;
67 
68 import com.android.internal.logging.UiEventLogger;
69 import com.android.keyguard.KeyguardUpdateMonitor;
70 import com.android.keyguard.KeyguardUpdateMonitorCallback;
71 import com.android.settingslib.DeviceInfoUtils;
72 import com.android.settingslib.SignalIcon;
73 import com.android.settingslib.Utils;
74 import com.android.settingslib.graph.SignalDrawable;
75 import com.android.settingslib.mobile.MobileMappings;
76 import com.android.settingslib.mobile.TelephonyIcons;
77 import com.android.settingslib.net.SignalStrengthUtil;
78 import com.android.settingslib.wifi.WifiUtils;
79 import com.android.settingslib.wifi.dpp.WifiDppIntentHelper;
80 import com.android.systemui.animation.ActivityTransitionAnimator;
81 import com.android.systemui.animation.DialogTransitionAnimator;
82 import com.android.systemui.broadcast.BroadcastDispatcher;
83 import com.android.systemui.dagger.qualifiers.Background;
84 import com.android.systemui.dagger.qualifiers.Main;
85 import com.android.systemui.flags.FeatureFlags;
86 import com.android.systemui.flags.Flags;
87 import com.android.systemui.plugins.ActivityStarter;
88 import com.android.systemui.res.R;
89 import com.android.systemui.shade.ShadeDisplayAware;
90 import com.android.systemui.statusbar.connectivity.AccessPointController;
91 import com.android.systemui.statusbar.policy.KeyguardStateController;
92 import com.android.systemui.statusbar.policy.LocationController;
93 import com.android.systemui.toast.SystemUIToast;
94 import com.android.systemui.toast.ToastFactory;
95 import com.android.systemui.util.CarrierConfigTracker;
96 import com.android.systemui.util.settings.GlobalSettings;
97 import com.android.wifitrackerlib.HotspotNetworkEntry;
98 import com.android.wifitrackerlib.MergedCarrierEntry;
99 import com.android.wifitrackerlib.WifiEntry;
100 
101 import java.util.ArrayList;
102 import java.util.HashMap;
103 import java.util.HashSet;
104 import java.util.List;
105 import java.util.Map;
106 import java.util.Objects;
107 import java.util.Set;
108 import java.util.concurrent.Executor;
109 import java.util.concurrent.atomic.AtomicReference;
110 import java.util.function.Supplier;
111 import java.util.stream.Collectors;
112 import java.util.stream.Stream;
113 
114 import javax.inject.Inject;
115 
116 /**
117  * Controller for Internet Dialog.
118  */
119 public class InternetDetailsContentController implements AccessPointController.AccessPointCallback {
120 
121     private static final String TAG = "InternetDetailsContentController";
122     private static final String ACTION_WIFI_SCANNING_SETTINGS =
123             "android.settings.WIFI_SCANNING_SETTINGS";
124     /**
125      * Fragment "key" argument passed thru {@link #SETTINGS_EXTRA_SHOW_FRAGMENT_ARGUMENTS}
126      */
127     private static final String SETTINGS_EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
128     /**
129      * When starting this activity, this extra can also be specified to supply a Bundle of arguments
130      * to pass to that fragment when it is instantiated during the initial creation of the activity.
131      */
132     private static final String SETTINGS_EXTRA_SHOW_FRAGMENT_ARGUMENTS =
133             ":settings:show_fragment_args";
134     private static final String AUTO_DATA_SWITCH_SETTING_R_ID = "auto_data_switch";
135     public static final Drawable EMPTY_DRAWABLE = new ColorDrawable(Color.TRANSPARENT);
136     public static final int NO_CELL_DATA_TYPE_ICON = 0;
137     private static final int SUBTITLE_TEXT_WIFI_IS_OFF = R.string.wifi_is_off;
138     private static final int SUBTITLE_TEXT_TAP_A_NETWORK_TO_CONNECT =
139             R.string.tap_a_network_to_connect;
140     private static final int SUBTITLE_TEXT_UNLOCK_TO_VIEW_NETWORKS =
141             R.string.unlock_to_view_networks;
142     private static final int SUBTITLE_TEXT_SEARCHING_FOR_NETWORKS =
143             R.string.wifi_empty_list_wifi_on;
144     private static final int SUBTITLE_TEXT_NON_CARRIER_NETWORK_UNAVAILABLE =
145             R.string.non_carrier_network_unavailable;
146     private static final int SUBTITLE_TEXT_ALL_CARRIER_NETWORK_UNAVAILABLE =
147             R.string.all_network_unavailable;
148     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
149     private static final TelephonyDisplayInfo DEFAULT_TELEPHONY_DISPLAY_INFO =
150             new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_UNKNOWN,
151                     TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE, false, false, false);
152 
153     static final int MAX_WIFI_ENTRY_COUNT = 3;
154 
155     private final FeatureFlags mFeatureFlags;
156 
157     @VisibleForTesting
158     /** Should be accessible only to the main thread. */
159     final Map<Integer, TelephonyDisplayInfo> mSubIdTelephonyDisplayInfoMap = new HashMap<>();
160     @VisibleForTesting
161     /** Should be accessible only to the main thread. */
162     final Map<Integer, TelephonyManager> mSubIdTelephonyManagerMap = new HashMap<>();
163     @VisibleForTesting
164     /** Should be accessible only to the main thread. */
165     final Map<Integer, TelephonyCallback> mSubIdTelephonyCallbackMap = new HashMap<>();
166 
167     private WifiManager mWifiManager;
168     private Context mContext;
169     private SubscriptionManager mSubscriptionManager;
170     private TelephonyManager mTelephonyManager;
171     private ConnectivityManager mConnectivityManager;
172     private CarrierConfigTracker mCarrierConfigTracker;
173     private Handler mHandler;
174     private Handler mWorkerHandler;
175     private MobileMappings.Config mConfig = null;
176     private Executor mExecutor;
177     private AccessPointController mAccessPointController;
178     private IntentFilter mConnectionStateFilter;
179     @VisibleForTesting
180     @Nullable
181     InternetDialogCallback mCallback;
182     private UiEventLogger mUiEventLogger;
183     private BroadcastDispatcher mBroadcastDispatcher;
184     private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
185     private GlobalSettings mGlobalSettings;
186     private int mDefaultDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
187     private ConnectivityManager.NetworkCallback mConnectivityManagerNetworkCallback;
188     private WindowManager mWindowManager;
189     private ToastFactory mToastFactory;
190     private SignalDrawable mSignalDrawable;
191     private SignalDrawable mSecondarySignalDrawable; // For the secondary mobile data sub in DSDS
192     private LocationController mLocationController;
193     private DialogTransitionAnimator mDialogTransitionAnimator;
194     private boolean mHasWifiEntries;
195     private WifiStateWorker mWifiStateWorker;
196     private boolean mHasActiveSubIdOnDds;
197     private boolean mIsMobileDataEnabled = false;
198 
199     @VisibleForTesting
200     Map<Integer, ServiceState> mSubIdServiceState = new HashMap<>();
201     @VisibleForTesting
202     static final float TOAST_PARAMS_HORIZONTAL_WEIGHT = 1.0f;
203     @VisibleForTesting
204     static final float TOAST_PARAMS_VERTICAL_WEIGHT = 1.0f;
205     @VisibleForTesting
206     static final long SHORT_DURATION_TIMEOUT = 4000;
207     @VisibleForTesting
208     protected ActivityStarter mActivityStarter;
209     @VisibleForTesting
210     protected SubscriptionManager.OnSubscriptionsChangedListener mOnSubscriptionsChangedListener;
211     @VisibleForTesting
212     protected WifiUtils.InternetIconInjector mWifiIconInjector;
213     @VisibleForTesting
214     protected boolean mCanConfigWifi;
215     @VisibleForTesting
216     protected KeyguardStateController mKeyguardStateController;
217     @VisibleForTesting
218     protected boolean mHasEthernet = false;
219     @VisibleForTesting
220     protected ConnectedWifiInternetMonitor mConnectedWifiInternetMonitor;
221     @VisibleForTesting
222     protected boolean mCarrierNetworkChangeMode;
223 
224     private final KeyguardUpdateMonitorCallback mKeyguardUpdateCallback =
225             new KeyguardUpdateMonitorCallback() {
226                 @Override
227                 public void onRefreshCarrierInfo() {
228                     if (mCallback != null) {
229                         mCallback.onRefreshCarrierInfo();
230                     }
231                 }
232 
233                 @Override
234                 public void onSimStateChanged(int subId, int slotId, int simState) {
235                     if (mCallback != null) {
236                         mCallback.onSimStateChanged();
237                     }
238                 }
239             };
240 
getSubscriptionInfo()241     protected List<SubscriptionInfo> getSubscriptionInfo() {
242         return mKeyguardUpdateMonitor.getFilteredSubscriptionInfo();
243     }
244 
245     @Inject
InternetDetailsContentController(@hadeDisplayAware Context context, UiEventLogger uiEventLogger, ActivityStarter starter, AccessPointController accessPointController, SubscriptionManager subscriptionManager, TelephonyManager telephonyManager, @Nullable WifiManager wifiManager, ConnectivityManager connectivityManager, @Main Handler handler, @Main Executor mainExecutor, BroadcastDispatcher broadcastDispatcher, KeyguardUpdateMonitor keyguardUpdateMonitor, GlobalSettings globalSettings, KeyguardStateController keyguardStateController, @ShadeDisplayAware WindowManager windowManager, ToastFactory toastFactory, @Background Handler workerHandler, CarrierConfigTracker carrierConfigTracker, LocationController locationController, DialogTransitionAnimator dialogTransitionAnimator, WifiStateWorker wifiStateWorker, FeatureFlags featureFlags )246     public InternetDetailsContentController(@ShadeDisplayAware Context context,
247             UiEventLogger uiEventLogger,
248             ActivityStarter starter, AccessPointController accessPointController,
249             SubscriptionManager subscriptionManager, TelephonyManager telephonyManager,
250             @Nullable WifiManager wifiManager, ConnectivityManager connectivityManager,
251             @Main Handler handler, @Main Executor mainExecutor,
252             BroadcastDispatcher broadcastDispatcher, KeyguardUpdateMonitor keyguardUpdateMonitor,
253             GlobalSettings globalSettings, KeyguardStateController keyguardStateController,
254             @ShadeDisplayAware WindowManager windowManager, ToastFactory toastFactory,
255             @Background Handler workerHandler,
256             CarrierConfigTracker carrierConfigTracker,
257             LocationController locationController,
258             DialogTransitionAnimator dialogTransitionAnimator,
259             WifiStateWorker wifiStateWorker,
260             FeatureFlags featureFlags
261     ) {
262         if (DEBUG) {
263             Log.d(TAG, "Init InternetDetailsContentController");
264         }
265         mHandler = handler;
266         mWorkerHandler = workerHandler;
267         mExecutor = mainExecutor;
268         mContext = context;
269         mGlobalSettings = globalSettings;
270         mWifiManager = wifiManager;
271         mTelephonyManager = telephonyManager;
272         mConnectivityManager = connectivityManager;
273         mSubscriptionManager = subscriptionManager;
274         mCarrierConfigTracker = carrierConfigTracker;
275         mBroadcastDispatcher = broadcastDispatcher;
276         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
277         mKeyguardStateController = keyguardStateController;
278         mConnectionStateFilter = new IntentFilter();
279         mConnectionStateFilter.addAction(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
280         mConnectionStateFilter.addAction(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
281         mUiEventLogger = uiEventLogger;
282         mActivityStarter = starter;
283         mAccessPointController = accessPointController;
284         mWifiIconInjector = new WifiUtils.InternetIconInjector(mContext);
285         mConnectivityManagerNetworkCallback = new DataConnectivityListener();
286         mWindowManager = windowManager;
287         mToastFactory = toastFactory;
288         mSignalDrawable = new SignalDrawable(mContext);
289         mSecondarySignalDrawable = new SignalDrawable(mContext);
290         mLocationController = locationController;
291         mDialogTransitionAnimator = dialogTransitionAnimator;
292         mConnectedWifiInternetMonitor = new ConnectedWifiInternetMonitor();
293         mWifiStateWorker = wifiStateWorker;
294         mFeatureFlags = featureFlags;
295     }
296 
onStart(@onNull InternetDialogCallback callback, boolean canConfigWifi)297     void onStart(@NonNull InternetDialogCallback callback, boolean canConfigWifi) {
298         if (DEBUG) {
299             Log.d(TAG, "onStart");
300         }
301         mCallback = callback;
302         mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateCallback);
303         mAccessPointController.addAccessPointCallback(this);
304         mBroadcastDispatcher.registerReceiver(mConnectionStateReceiver, mConnectionStateFilter,
305                 mExecutor);
306         // Listen the subscription changes
307         mOnSubscriptionsChangedListener = new InternetOnSubscriptionChangedListener();
308         refreshHasActiveSubIdOnDds();
309         mSubscriptionManager.addOnSubscriptionsChangedListener(mExecutor,
310                 mOnSubscriptionsChangedListener);
311         mDefaultDataSubId = getDefaultDataSubscriptionId();
312         if (DEBUG) {
313             Log.d(TAG, "Init, SubId: " + mDefaultDataSubId);
314         }
315         mConfig = MobileMappings.Config.readConfig(mContext);
316         mTelephonyManager = mTelephonyManager.createForSubscriptionId(mDefaultDataSubId);
317         mSubIdTelephonyManagerMap.put(mDefaultDataSubId, mTelephonyManager);
318         registerInternetTelephonyCallback(mTelephonyManager, mDefaultDataSubId);
319         // Listen the connectivity changes
320         mConnectivityManager.registerDefaultNetworkCallback(mConnectivityManagerNetworkCallback);
321         mCanConfigWifi = canConfigWifi;
322         scanWifiAccessPoints();
323     }
324 
onStop()325     void onStop() {
326         if (DEBUG) {
327             Log.d(TAG, "onStop");
328         }
329         mBroadcastDispatcher.unregisterReceiver(mConnectionStateReceiver);
330         for (TelephonyManager tm : mSubIdTelephonyManagerMap.values()) {
331             TelephonyCallback callback = mSubIdTelephonyCallbackMap.get(tm.getSubscriptionId());
332             if (callback != null) {
333                 tm.unregisterTelephonyCallback(callback);
334             } else if (DEBUG) {
335                 Log.e(TAG, "Unexpected null telephony call back for Sub " + tm.getSubscriptionId());
336             }
337         }
338         mSubIdTelephonyManagerMap.clear();
339         mSubIdTelephonyCallbackMap.clear();
340         mSubIdTelephonyDisplayInfoMap.clear();
341         mSubscriptionManager.removeOnSubscriptionsChangedListener(
342                 mOnSubscriptionsChangedListener);
343         mAccessPointController.removeAccessPointCallback(this);
344         mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateCallback);
345         mConnectivityManager.unregisterNetworkCallback(mConnectivityManagerNetworkCallback);
346         mConnectedWifiInternetMonitor.unregisterCallback();
347         mCallback = null;
348     }
349 
350     /**
351      * This is to generate and register the new callback to Telephony for uncached subscription id,
352      * then cache it. Telephony also cached this callback into
353      * {@link com.android.server.TelephonyRegistry}, so if subscription id and callback were cached
354      * already, it shall do nothing to avoid registering redundant callback to Telephony.
355      */
registerInternetTelephonyCallback( TelephonyManager telephonyManager, int subId)356     private void registerInternetTelephonyCallback(
357             TelephonyManager telephonyManager, int subId) {
358         if (mSubIdTelephonyCallbackMap.containsKey(subId)) {
359             // Avoid to generate and register unnecessary callback to Telephony.
360             return;
361         }
362         InternetTelephonyCallback telephonyCallback = new InternetTelephonyCallback(subId);
363         mSubIdTelephonyCallbackMap.put(subId, telephonyCallback);
364         telephonyManager.registerTelephonyCallback(mExecutor, telephonyCallback);
365     }
366 
isAirplaneModeEnabled()367     boolean isAirplaneModeEnabled() {
368         return mGlobalSettings.getInt(Settings.Global.AIRPLANE_MODE_ON, 0) != 0;
369     }
370 
setAirplaneModeDisabled()371     void setAirplaneModeDisabled() {
372         mConnectivityManager.setAirplaneMode(false);
373     }
374 
getDefaultDataSubscriptionId()375     protected int getDefaultDataSubscriptionId() {
376         return mSubscriptionManager.getDefaultDataSubscriptionId();
377     }
378 
379     @VisibleForTesting
getSettingsIntent()380     protected Intent getSettingsIntent() {
381         return new Intent(Settings.ACTION_NETWORK_PROVIDER_SETTINGS).addFlags(
382                 Intent.FLAG_ACTIVITY_NEW_TASK);
383     }
384 
385     @Nullable
getWifiDetailsSettingsIntent(String key)386     protected Intent getWifiDetailsSettingsIntent(String key) {
387         if (TextUtils.isEmpty(key)) {
388             if (DEBUG) {
389                 Log.d(TAG, "connected entry's key is empty");
390             }
391             return null;
392         }
393         return WifiUtils.getWifiDetailsSettingsIntent(key);
394     }
395 
getDialogTitleText()396     CharSequence getDialogTitleText() {
397         if (isAirplaneModeEnabled()) {
398             return mContext.getText(R.string.airplane_mode);
399         }
400         return mContext.getText(R.string.quick_settings_internet_label);
401     }
402 
403     @Nullable
getSubtitleText(boolean isProgressBarVisible)404     CharSequence getSubtitleText(boolean isProgressBarVisible) {
405         if (mCanConfigWifi && !isWifiEnabled()) {
406             // When Wi-Fi is disabled.
407             //   Sub-Title: Wi-Fi is off
408             if (DEBUG) {
409                 Log.d(TAG, "Wi-Fi off.");
410             }
411             return mContext.getText(SUBTITLE_TEXT_WIFI_IS_OFF);
412         }
413 
414         if (isDeviceLocked()) {
415             // When the device is locked.
416             //   Sub-Title: Unlock to view networks
417             if (DEBUG) {
418                 Log.d(TAG, "The device is locked.");
419             }
420             return mContext.getText(SUBTITLE_TEXT_UNLOCK_TO_VIEW_NETWORKS);
421         }
422 
423         if (mHasWifiEntries) {
424             return mCanConfigWifi ? mContext.getText(SUBTITLE_TEXT_TAP_A_NETWORK_TO_CONNECT) : null;
425         }
426 
427         if (mCanConfigWifi && isProgressBarVisible) {
428             // When the Wi-Fi scan result callback is received
429             //   Sub-Title: Searching for networks...
430             return mContext.getText(SUBTITLE_TEXT_SEARCHING_FOR_NETWORKS);
431         }
432 
433         if (isCarrierNetworkActive()) {
434             return mContext.getText(SUBTITLE_TEXT_NON_CARRIER_NETWORK_UNAVAILABLE);
435         }
436 
437         // Sub-Title:
438         // show non_carrier_network_unavailable
439         //   - while Wi-Fi on + no Wi-Fi item
440         //   - while Wi-Fi on + no Wi-Fi item + mobile data off
441         // show all_network_unavailable:
442         //   - while Wi-Fi on + no Wi-Fi item + no carrier item
443         //   - while Wi-Fi on + no Wi-Fi item + service is out of service
444         //   - while Wi-Fi on + no Wi-Fi item + mobile data on + no carrier data.
445         if (DEBUG) {
446             Log.d(TAG, "No Wi-Fi item.");
447         }
448         boolean isActiveOnNonDds = getActiveAutoSwitchNonDdsSubId() != SubscriptionManager
449                 .INVALID_SUBSCRIPTION_ID;
450         if (!hasActiveSubIdOnDds() || (!isVoiceStateInService(mDefaultDataSubId)
451                 && !isDataStateInService(mDefaultDataSubId) && !isActiveOnNonDds)) {
452             if (DEBUG) {
453                 Log.d(TAG, "No carrier or service is out of service.");
454             }
455             return mContext.getText(SUBTITLE_TEXT_ALL_CARRIER_NETWORK_UNAVAILABLE);
456         }
457 
458         if (mCanConfigWifi && !mIsMobileDataEnabled) {
459             if (DEBUG) {
460                 Log.d(TAG, "Mobile data off");
461             }
462             return mContext.getText(SUBTITLE_TEXT_NON_CARRIER_NETWORK_UNAVAILABLE);
463         }
464 
465         if (!activeNetworkIsCellular()) {
466             if (DEBUG) {
467                 Log.d(TAG, "No carrier data.");
468             }
469             return mContext.getText(SUBTITLE_TEXT_ALL_CARRIER_NETWORK_UNAVAILABLE);
470         }
471 
472         if (mCanConfigWifi) {
473             return mContext.getText(SUBTITLE_TEXT_NON_CARRIER_NETWORK_UNAVAILABLE);
474         }
475         return null;
476     }
477 
478     @Nullable
getInternetWifiDrawable(@onNull WifiEntry wifiEntry)479     Drawable getInternetWifiDrawable(@NonNull WifiEntry wifiEntry) {
480         Drawable drawable = getWifiDrawable(wifiEntry);
481         if (drawable == null) {
482             return null;
483         }
484         drawable.setTint(mContext.getColor(R.color.connected_network_primary_color));
485         return drawable;
486     }
487 
488     /**
489      * Returns a Wi-Fi icon {@link Drawable}.
490      *
491      * @param wifiEntry {@link WifiEntry}
492      */
493     @Nullable
getWifiDrawable(@onNull WifiEntry wifiEntry)494     Drawable getWifiDrawable(@NonNull WifiEntry wifiEntry) {
495         if (wifiEntry instanceof HotspotNetworkEntry) {
496             int deviceType = ((HotspotNetworkEntry) wifiEntry).getDeviceType();
497             return mContext.getDrawable(getHotspotIconResource(deviceType));
498         }
499         // If the Wi-Fi level is equal to WIFI_LEVEL_UNREACHABLE(-1), then a null drawable
500         // will be returned.
501         if (wifiEntry.getLevel() == WifiEntry.WIFI_LEVEL_UNREACHABLE) {
502             return null;
503         }
504         return mWifiIconInjector.getIcon(wifiEntry.shouldShowXLevelIcon(), wifiEntry.getLevel());
505     }
506 
getSignalStrengthDrawable(int subId)507     Drawable getSignalStrengthDrawable(int subId) {
508         Drawable drawable = mContext.getDrawable(
509                 R.drawable.ic_signal_strength_zero_bar_no_internet);
510         try {
511             if (mTelephonyManager == null) {
512                 if (DEBUG) {
513                     Log.d(TAG, "TelephonyManager is null");
514                 }
515                 return drawable;
516             }
517 
518             boolean isCarrierNetworkActive = isCarrierNetworkActive();
519             if (isDataStateInService(subId) || isVoiceStateInService(subId)
520                     || isCarrierNetworkActive) {
521                 AtomicReference<Drawable> shared = new AtomicReference<>();
522                 shared.set(getSignalStrengthDrawableWithLevel(isCarrierNetworkActive, subId));
523                 drawable = shared.get();
524             }
525 
526             int tintColor = Utils.getColorAttrDefaultColor(mContext,
527                     android.R.attr.textColorTertiary);
528             if (activeNetworkIsCellular() || isCarrierNetworkActive) {
529                 tintColor = mContext.getColor(R.color.connected_network_primary_color);
530             }
531             drawable.setTint(tintColor);
532         } catch (Throwable e) {
533             e.printStackTrace();
534         }
535         return drawable;
536     }
537 
538     /**
539      * To get the signal bar icon with level.
540      *
541      * @return The Drawable which is a signal bar icon with level.
542      */
getSignalStrengthDrawableWithLevel(boolean isCarrierNetworkActive, int subId)543     Drawable getSignalStrengthDrawableWithLevel(boolean isCarrierNetworkActive, int subId) {
544         TelephonyManager tm = mSubIdTelephonyManagerMap.getOrDefault(subId, mTelephonyManager);
545         final SignalStrength strength = tm.getSignalStrength();
546         int level = (strength == null) ? 0 : strength.getLevel();
547         int numLevels = SignalStrength.NUM_SIGNAL_STRENGTH_BINS;
548         if (isCarrierNetworkActive) {
549             level = getCarrierNetworkLevel();
550             numLevels = WifiEntry.WIFI_LEVEL_MAX + 1;
551         } else if (mSubscriptionManager != null && shouldInflateSignalStrength(subId)) {
552             level += 1;
553             numLevels += 1;
554         }
555         return getSignalStrengthIcon(subId, mContext, level, numLevels, NO_CELL_DATA_TYPE_ICON,
556                 !mIsMobileDataEnabled);
557     }
558 
getSignalStrengthIcon(int subId, Context context, int level, int numLevels, int iconType, boolean cutOut)559     Drawable getSignalStrengthIcon(int subId, Context context, int level, int numLevels,
560             int iconType, boolean cutOut) {
561         boolean isForDds = subId == mDefaultDataSubId;
562         int levelDrawable =
563                 mCarrierNetworkChangeMode ? SignalDrawable.getCarrierChangeState(numLevels)
564                         : SignalDrawable.getState(level, numLevels, cutOut);
565         if (isForDds) {
566             mSignalDrawable.setLevel(levelDrawable);
567         } else {
568             mSecondarySignalDrawable.setLevel(levelDrawable);
569         }
570 
571         // Make the network type drawable
572         final Drawable networkDrawable =
573                 iconType == NO_CELL_DATA_TYPE_ICON
574                         ? EMPTY_DRAWABLE
575                         : context.getResources().getDrawable(iconType, context.getTheme());
576 
577         // Overlay the two drawables
578         final Drawable[] layers = {networkDrawable, isForDds
579                 ? mSignalDrawable : mSecondarySignalDrawable};
580         final int iconSize =
581                 context.getResources().getDimensionPixelSize(R.dimen.signal_strength_icon_size);
582 
583         final LayerDrawable icons = new LayerDrawable(layers);
584         // Set the network type icon at the top left
585         icons.setLayerGravity(0 /* index of networkDrawable */, Gravity.TOP | Gravity.LEFT);
586         // Set the signal strength icon at the bottom right
587         icons.setLayerGravity(1 /* index of SignalDrawable */, Gravity.BOTTOM | Gravity.RIGHT);
588         icons.setLayerSize(1 /* index of SignalDrawable */, iconSize, iconSize);
589         icons.setTintList(Utils.getColorAttr(context, android.R.attr.textColorTertiary));
590         return icons;
591     }
592 
shouldInflateSignalStrength(int subId)593     private boolean shouldInflateSignalStrength(int subId) {
594         return SignalStrengthUtil.shouldInflateSignalStrength(mContext, subId);
595     }
596 
getUniqueSubscriptionDisplayName(int subscriptionId, Context context)597     private CharSequence getUniqueSubscriptionDisplayName(int subscriptionId, Context context) {
598         final Map<Integer, CharSequence> displayNames = getUniqueSubscriptionDisplayNames(context);
599         return displayNames.getOrDefault(subscriptionId, "");
600     }
601 
getUniqueSubscriptionDisplayNames(Context context)602     private Map<Integer, CharSequence> getUniqueSubscriptionDisplayNames(Context context) {
603         class DisplayInfo {
604             DisplayInfo(SubscriptionInfo subscriptionInfo, CharSequence originalName) {
605                 this.subscriptionInfo = subscriptionInfo;
606                 this.originalName = originalName;
607             }
608 
609             public SubscriptionInfo subscriptionInfo;
610             public CharSequence originalName;
611             public CharSequence uniqueName;
612         }
613 
614         // Map of SubscriptionId to DisplayName
615         final Supplier<Stream<DisplayInfo>> originalInfos =
616                 () -> getSubscriptionInfo()
617                         .stream()
618                         .filter(i -> {
619                             // Filter out null values.
620                             return (i != null && i.getDisplayName() != null);
621                         })
622                         .map(i -> new DisplayInfo(i, i.getDisplayName().toString().trim()));
623 
624         // A Unique set of display names
625         Set<CharSequence> uniqueNames = new HashSet<>();
626         // Return the set of duplicate names
627         final Set<CharSequence> duplicateOriginalNames = originalInfos.get()
628                 .filter(info -> !uniqueNames.add(info.originalName))
629                 .map(info -> info.originalName)
630                 .collect(Collectors.toSet());
631 
632         // If a display name is duplicate, append the final 4 digits of the phone number.
633         // Creates a mapping of Subscription id to original display name + phone number display name
634         final Supplier<Stream<DisplayInfo>> uniqueInfos = () -> originalInfos.get().map(info -> {
635             if (duplicateOriginalNames.contains(info.originalName)) {
636                 // This may return null, if the user cannot view the phone number itself.
637                 final String phoneNumber = DeviceInfoUtils.getBidiFormattedPhoneNumber(context,
638                         info.subscriptionInfo);
639                 String lastFourDigits = "";
640                 if (phoneNumber != null) {
641                     lastFourDigits = (phoneNumber.length() > 4)
642                             ? phoneNumber.substring(phoneNumber.length() - 4) : phoneNumber;
643                 }
644 
645                 if (TextUtils.isEmpty(lastFourDigits)) {
646                     info.uniqueName = info.originalName;
647                 } else {
648                     info.uniqueName = info.originalName + " " + lastFourDigits;
649                 }
650 
651             } else {
652                 info.uniqueName = info.originalName;
653             }
654             return info;
655         });
656 
657         // Check uniqueness a second time.
658         // We might not have had permission to view the phone numbers.
659         // There might also be multiple phone numbers whose last 4 digits the same.
660         uniqueNames.clear();
661         final Set<CharSequence> duplicatePhoneNames = uniqueInfos.get()
662                 .filter(info -> !uniqueNames.add(info.uniqueName))
663                 .map(info -> info.uniqueName)
664                 .collect(Collectors.toSet());
665 
666         return uniqueInfos.get().map(info -> {
667             if (duplicatePhoneNames.contains(info.uniqueName)) {
668                 info.uniqueName = info.originalName + " "
669                         + info.subscriptionInfo.getSubscriptionId();
670             }
671             return info;
672         }).collect(Collectors.toMap(
673                 info -> info.subscriptionInfo.getSubscriptionId(),
674                 info -> info.uniqueName));
675     }
676 
677     /**
678      * @return the subId of the visible non-DDS if it's actively being used for data, otherwise
679      * return {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID}.
680      */
681     int getActiveAutoSwitchNonDdsSubId() {
682         if (!mFeatureFlags.isEnabled(Flags.QS_SECONDARY_DATA_SUB_INFO)) {
683             // sets the non-DDS to be not found to hide its visual
684             return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
685         }
686         int activeDataSubId = SubscriptionManager.getActiveDataSubscriptionId();
687         if (activeDataSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID
688                 || mDefaultDataSubId == activeDataSubId) {
689             return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
690         }
691 
692         SubscriptionInfo subInfo = mSubscriptionManager.getActiveSubscriptionInfo(
693                 SubscriptionManager.getActiveDataSubscriptionId());
694         if (subInfo != null && subInfo.getSubscriptionId() != mDefaultDataSubId
695                 && !subInfo.isOpportunistic()) {
696             int subId = subInfo.getSubscriptionId();
697             if (mSubIdTelephonyManagerMap.get(subId) == null) {
698                 TelephonyManager secondaryTm = mTelephonyManager.createForSubscriptionId(subId);
699                 registerInternetTelephonyCallback(secondaryTm, subId);
700                 mSubIdTelephonyManagerMap.put(subId, secondaryTm);
701             }
702             return subId;
703         }
704         return SubscriptionManager.INVALID_SUBSCRIPTION_ID;
705 
706     }
707 
708     CharSequence getMobileNetworkTitle(int subId) {
709         return getUniqueSubscriptionDisplayName(subId, mContext);
710     }
711 
712     String getMobileNetworkSummary(int subId) {
713         String description = getNetworkTypeDescription(mContext, mConfig, subId);
714         return getMobileSummary(mContext, description, subId);
715     }
716 
717     /**
718      * Get currently description of mobile network type.
719      */
720     private String getNetworkTypeDescription(Context context, MobileMappings.Config config,
721             int subId) {
722         TelephonyDisplayInfo telephonyDisplayInfo =
723                 mSubIdTelephonyDisplayInfoMap.getOrDefault(subId, DEFAULT_TELEPHONY_DISPLAY_INFO);
724         String iconKey = getIconKey(telephonyDisplayInfo);
725 
726         if (mapIconSets(config) == null || mapIconSets(config).get(iconKey) == null) {
727             if (DEBUG) {
728                 Log.d(TAG, "The description of network type is empty.");
729             }
730             return "";
731         }
732 
733         int resId = Objects.requireNonNull(mapIconSets(config).get(iconKey)).dataContentDescription;
734         SignalIcon.MobileIconGroup iconGroup;
735         if (isCarrierNetworkActive()) {
736             iconGroup = TelephonyIcons.CARRIER_MERGED_WIFI;
737             resId = iconGroup.dataContentDescription;
738         } else if (mCarrierNetworkChangeMode) {
739             iconGroup = TelephonyIcons.CARRIER_NETWORK_CHANGE;
740             resId = iconGroup.dataContentDescription;
741         }
742 
743         return resId != 0
744                 ? SubscriptionManager.getResourcesForSubId(context, subId).getString(resId) : "";
745     }
746 
747     private String getMobileSummary(Context context, String networkTypeDescription, int subId) {
748         if (!isMobileDataEnabled()) {
749             return context.getString(R.string.mobile_data_off_summary);
750         }
751         String summary = networkTypeDescription;
752         boolean isForDds = subId == mDefaultDataSubId;
753         int activeSubId = getActiveAutoSwitchNonDdsSubId();
754         boolean isOnNonDds = activeSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID;
755         // Set network description for the carrier network when connecting to the carrier network
756         // under the airplane mode ON.
757         if (activeNetworkIsCellular() || isCarrierNetworkActive()) {
758             summary = context.getString(
759                     com.android.settingslib.R.string.preference_summary_default_combination,
760                     context.getString(
761                             isForDds // if nonDds is active, explains Dds status as poor connection
762                                     ? (isOnNonDds ? R.string.mobile_data_poor_connection
763                                     : R.string.mobile_data_connection_active)
764                                     : R.string.mobile_data_temp_connection_active),
765                     networkTypeDescription);
766         } else if (!isDataStateInService(subId)) {
767             summary = context.getString(R.string.mobile_data_no_connection);
768         }
769         return summary;
770     }
771 
772     void startActivity(Intent intent, View view) {
773         ActivityTransitionAnimator.Controller controller =
774                 mDialogTransitionAnimator.createActivityTransitionController(view);
775 
776         if (controller == null && mCallback != null) {
777             mCallback.dismissDialog();
778         }
779 
780         mActivityStarter.postStartActivityDismissingKeyguard(intent, 0, controller);
781     }
782 
783     void startActivityForDialog(Intent intent) {
784         mActivityStarter.startActivity(intent, false /* dismissShade */);
785     }
786 
787     // Closes the dialog first, as the WEP dialog is in a different process and can have weird
788     // interactions otherwise.
789     void startActivityForDialogDismissDialogFirst(Intent intent, View view) {
790         ActivityTransitionAnimator.Controller controller =
791                 mDialogTransitionAnimator.createActivityTransitionController(view);
792         if (mCallback != null) {
793             mCallback.dismissDialog();
794         }
795         mActivityStarter.startActivity(intent, false /* dismissShade */, controller);
796     }
797 
798     void launchNetworkSetting(View view) {
799         startActivity(getSettingsIntent(), view);
800     }
801 
802     void launchWifiDetailsSetting(String key, View view) {
803         Intent intent = getWifiDetailsSettingsIntent(key);
804         if (intent != null) {
805             startActivity(intent, view);
806         }
807     }
808 
809     void launchMobileNetworkSettings(View view) {
810         final int subId = getActiveAutoSwitchNonDdsSubId();
811         if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
812             Log.w(TAG, "launchMobileNetworkSettings fail, invalid subId:" + subId);
813             return;
814         }
815         startActivity(getSubSettingIntent(subId), view);
816     }
817 
818     Intent getSubSettingIntent(int subId) {
819         final Intent intent = new Intent(Settings.ACTION_NETWORK_OPERATOR_SETTINGS);
820         final Bundle fragmentArgs = new Bundle();
821         // Special contract for Settings to highlight permission row
822         fragmentArgs.putString(SETTINGS_EXTRA_FRAGMENT_ARG_KEY, AUTO_DATA_SWITCH_SETTING_R_ID);
823         intent.putExtra(Settings.EXTRA_SUB_ID, subId);
824         intent.putExtra(SETTINGS_EXTRA_SHOW_FRAGMENT_ARGUMENTS, fragmentArgs);
825         return intent;
826     }
827 
828     void launchWifiScanningSetting(View view) {
829         final Intent intent = new Intent(ACTION_WIFI_SCANNING_SETTINGS);
830         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
831         startActivity(intent, view);
832     }
833 
834     /**
835      * Enable or disable Wi-Fi.
836      *
837      * @param enabled {@code true} to enable, {@code false} to disable.
838      */
839     @AnyThread
840     public void setWifiEnabled(boolean enabled) {
841         mWifiStateWorker.setWifiEnabled(enabled);
842     }
843 
844     /**
845      * Return whether Wi-Fi is enabled or disabled.
846      *
847      * @return {@code true} if Wi-Fi is enabled or enabling
848      * @see WifiManager#getWifiState()
849      */
850     @AnyThread
851     public boolean isWifiEnabled() {
852         return mWifiStateWorker.isWifiEnabled();
853     }
854 
855     void connectCarrierNetwork() {
856         String errorLogPrefix = "Fail to connect carrier network : ";
857 
858         if (!isMobileDataEnabled()) {
859             if (DEBUG) {
860                 Log.d(TAG, errorLogPrefix + "settings OFF");
861             }
862             return;
863         }
864         if (isDeviceLocked()) {
865             if (DEBUG) {
866                 Log.d(TAG, errorLogPrefix + "device locked");
867             }
868             return;
869         }
870         if (activeNetworkIsCellular()) {
871             Log.d(TAG, errorLogPrefix + "already active");
872             return;
873         }
874 
875         MergedCarrierEntry mergedCarrierEntry =
876                 mAccessPointController.getMergedCarrierEntry();
877         if (mergedCarrierEntry == null) {
878             Log.e(TAG, errorLogPrefix + "no merged entry");
879             return;
880         }
881 
882         if (!mergedCarrierEntry.canConnect()) {
883             Log.w(TAG, errorLogPrefix + "merged entry connect state "
884                     + mergedCarrierEntry.getConnectedState());
885             return;
886         }
887 
888         mergedCarrierEntry.connect(null /* ConnectCallback */, false);
889         makeOverlayToast(R.string.wifi_wont_autoconnect_for_now);
890     }
891 
892     boolean isCarrierNetworkActive() {
893         final MergedCarrierEntry mergedCarrierEntry =
894                 mAccessPointController.getMergedCarrierEntry();
895         return mergedCarrierEntry != null && mergedCarrierEntry.isDefaultNetwork();
896     }
897 
898     int getCarrierNetworkLevel() {
899         final MergedCarrierEntry mergedCarrierEntry =
900                 mAccessPointController.getMergedCarrierEntry();
901         if (mergedCarrierEntry == null) return WifiEntry.WIFI_LEVEL_MIN;
902 
903         int level = mergedCarrierEntry.getLevel();
904         // To avoid icons not found with WIFI_LEVEL_UNREACHABLE(-1), use WIFI_LEVEL_MIN(0) instead.
905         if (level < WifiEntry.WIFI_LEVEL_MIN) level = WifiEntry.WIFI_LEVEL_MIN;
906         return level;
907     }
908 
909     @WorkerThread
910     void setMergedCarrierWifiEnabledIfNeed(int subId, boolean enabled) {
911         // If the Carrier Provisions Wi-Fi Merged Networks enabled, do not set the merged carrier
912         // Wi-Fi state together.
913         if (mCarrierConfigTracker.getCarrierProvisionsWifiMergedNetworksBool(subId)) {
914             return;
915         }
916 
917         final MergedCarrierEntry entry = mAccessPointController.getMergedCarrierEntry();
918         if (entry == null) {
919             if (DEBUG) {
920                 Log.d(TAG, "MergedCarrierEntry is null, can not set the status.");
921             }
922             return;
923         }
924         entry.setEnabled(enabled);
925     }
926 
927     WifiManager getWifiManager() {
928         return mWifiManager;
929     }
930 
931     TelephonyManager getTelephonyManager() {
932         return mTelephonyManager;
933     }
934 
935     SubscriptionManager getSubscriptionManager() {
936         return mSubscriptionManager;
937     }
938 
939     /**
940      * @return whether there is the carrier item in the slice.
941      */
942     boolean hasActiveSubIdOnDds() {
943         if (isAirplaneModeEnabled() || mTelephonyManager == null) {
944             return false;
945         }
946 
947         return mHasActiveSubIdOnDds;
948     }
949 
950     private static boolean isEmbeddedSubscriptionVisible(@NonNull SubscriptionInfo subInfo) {
951         if (subInfo.isEmbedded() && subInfo.getProfileClass() == PROFILE_CLASS_PROVISIONING) {
952             return false;
953         }
954         return true;
955     }
956 
957     private void refreshHasActiveSubIdOnDds() {
958         if (mSubscriptionManager == null) {
959             mHasActiveSubIdOnDds = false;
960             Log.e(TAG, "SubscriptionManager is null, set mHasActiveSubId = false");
961             return;
962         }
963         int dds = getDefaultDataSubscriptionId();
964         if (dds == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
965             mHasActiveSubIdOnDds = false;
966             Log.d(TAG, "DDS is INVALID_SUBSCRIPTION_ID");
967             return;
968         }
969         SubscriptionInfo ddsSubInfo = mSubscriptionManager.getActiveSubscriptionInfo(dds);
970         if (ddsSubInfo == null) {
971             mHasActiveSubIdOnDds = false;
972             Log.e(TAG, "Can't get DDS subscriptionInfo");
973             return;
974         } else if (ddsSubInfo.isOnlyNonTerrestrialNetwork()) {
975             mHasActiveSubIdOnDds = false;
976             Log.d(TAG, "This is NTN, so do not show mobile data");
977             return;
978         }
979 
980         mHasActiveSubIdOnDds = isEmbeddedSubscriptionVisible(ddsSubInfo);
981         Log.i(TAG, "mHasActiveSubId:" + mHasActiveSubIdOnDds);
982     }
983 
984     /**
985      * Return {@code true} if mobile data is enabled
986      */
987     boolean isMobileDataEnabled() {
988         return mIsMobileDataEnabled;
989     }
990 
991     /**
992      * Set whether to enable data for {@code subId}, also whether to disable data for other
993      * subscription
994      */
995     void setMobileDataEnabled(Context context, int subId, boolean enabled,
996             boolean disableOtherSubscriptions) {
997         if (mTelephonyManager == null) {
998             if (DEBUG) {
999                 Log.d(TAG, "TelephonyManager is null, can not set mobile data.");
1000             }
1001             return;
1002         }
1003 
1004         if (mSubscriptionManager == null) {
1005             if (DEBUG) {
1006                 Log.d(TAG, "SubscriptionManager is null, can not set mobile data.");
1007             }
1008             return;
1009         }
1010 
1011         mTelephonyManager.setDataEnabledForReason(
1012                 TelephonyManager.DATA_ENABLED_REASON_USER, enabled);
1013         if (disableOtherSubscriptions) {
1014             final List<SubscriptionInfo> subInfoList =
1015                     mSubscriptionManager.getActiveSubscriptionInfoList();
1016             if (subInfoList != null) {
1017                 for (SubscriptionInfo subInfo : subInfoList) {
1018                     // We never disable mobile data for opportunistic subscriptions.
1019                     if (subInfo.getSubscriptionId() != subId && !subInfo.isOpportunistic()) {
1020                         context.getSystemService(TelephonyManager.class).createForSubscriptionId(
1021                                 subInfo.getSubscriptionId()).setDataEnabled(false);
1022                     }
1023                 }
1024             }
1025         }
1026         mWorkerHandler.post(() -> setMergedCarrierWifiEnabledIfNeed(subId, enabled));
1027     }
1028 
1029     void setAutoDataSwitchMobileDataPolicy(int subId, boolean enable) {
1030         TelephonyManager tm = mSubIdTelephonyManagerMap.getOrDefault(subId, mTelephonyManager);
1031         if (tm == null) {
1032             if (DEBUG) {
1033                 Log.d(TAG, "TelephonyManager is null, can not set mobile data.");
1034             }
1035             return;
1036         }
1037         tm.setMobileDataPolicyEnabled(TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH, enable);
1038     }
1039 
1040     boolean isDataStateInService(int subId) {
1041         final ServiceState serviceState = mSubIdServiceState.getOrDefault(subId,
1042                 new ServiceState());
1043         NetworkRegistrationInfo regInfo =
1044                 (serviceState == null) ? null : serviceState.getNetworkRegistrationInfo(
1045                         NetworkRegistrationInfo.DOMAIN_PS,
1046                         AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
1047         return (regInfo == null) ? false : regInfo.isRegistered();
1048     }
1049 
1050     boolean isVoiceStateInService(int subId) {
1051         if (mTelephonyManager == null) {
1052             if (DEBUG) {
1053                 Log.d(TAG, "TelephonyManager is null, can not detect voice state.");
1054             }
1055             return false;
1056         }
1057 
1058         final ServiceState serviceState = mSubIdServiceState.getOrDefault(subId,
1059                 new ServiceState());
1060         return serviceState != null
1061                 && serviceState.getState() == serviceState.STATE_IN_SERVICE;
1062     }
1063 
1064     public boolean isDeviceLocked() {
1065         return !mKeyguardStateController.isUnlocked();
1066     }
1067 
1068     boolean activeNetworkIsCellular() {
1069         if (mConnectivityManager == null) {
1070             if (DEBUG) {
1071                 Log.d(TAG, "ConnectivityManager is null, can not check active network.");
1072             }
1073             return false;
1074         }
1075 
1076         final Network activeNetwork = mConnectivityManager.getActiveNetwork();
1077         if (activeNetwork == null) {
1078             Log.d(TAG, "getActiveNetwork is null.");
1079             return false;
1080         }
1081         final NetworkCapabilities networkCapabilities =
1082                 mConnectivityManager.getNetworkCapabilities(activeNetwork);
1083         if (networkCapabilities == null) {
1084             return false;
1085         }
1086         return networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR);
1087     }
1088 
1089     boolean connect(WifiEntry ap) {
1090         if (ap == null) {
1091             if (DEBUG) {
1092                 Log.d(TAG, "No Wi-Fi ap to connect.");
1093             }
1094             return false;
1095         }
1096 
1097         if (ap.getWifiConfiguration() != null) {
1098             if (DEBUG) {
1099                 Log.d(TAG, "connect networkId=" + ap.getWifiConfiguration().networkId);
1100             }
1101         } else {
1102             if (DEBUG) {
1103                 Log.d(TAG, "connect to unsaved network " + ap.getTitle());
1104             }
1105         }
1106         ap.connect(new WifiEntryConnectCallback(mActivityStarter, ap, this));
1107         return false;
1108     }
1109 
1110     @WorkerThread
1111     boolean isWifiScanEnabled() {
1112         if (!mLocationController.isLocationEnabled()) {
1113             return false;
1114         }
1115         return mWifiManager != null && mWifiManager.isScanAlwaysAvailable();
1116     }
1117 
1118     static class WifiEntryConnectCallback implements WifiEntry.ConnectCallback {
1119         final ActivityStarter mActivityStarter;
1120         final WifiEntry mWifiEntry;
1121         final InternetDetailsContentController mInternetDetailsContentController;
1122 
1123         WifiEntryConnectCallback(ActivityStarter activityStarter, WifiEntry connectWifiEntry,
1124                 InternetDetailsContentController internetDetailsContentController) {
1125             mActivityStarter = activityStarter;
1126             mWifiEntry = connectWifiEntry;
1127             mInternetDetailsContentController = internetDetailsContentController;
1128         }
1129 
1130         @Override
1131         public void onConnectResult(@ConnectStatus int status) {
1132             if (DEBUG) {
1133                 Log.d(TAG, "onConnectResult " + status);
1134             }
1135 
1136             if (status == WifiEntry.ConnectCallback.CONNECT_STATUS_FAILURE_NO_CONFIG) {
1137                 final Intent intent = WifiUtils.getWifiDialogIntent(mWifiEntry.getKey(),
1138                         true /* connectForCaller */);
1139                 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1140                 mActivityStarter.startActivity(intent, false /* dismissShade */);
1141             } else if (status == CONNECT_STATUS_FAILURE_UNKNOWN) {
1142                 mInternetDetailsContentController.makeOverlayToast(
1143                         R.string.wifi_failed_connect_message);
1144             } else {
1145                 if (DEBUG) {
1146                     Log.d(TAG, "connect failure reason=" + status);
1147                 }
1148             }
1149         }
1150     }
1151 
1152     private void scanWifiAccessPoints() {
1153         if (mCanConfigWifi) {
1154             mAccessPointController.scanForAccessPoints();
1155         }
1156     }
1157 
1158     @Override
1159     @WorkerThread
1160     public void onAccessPointsChanged(List<WifiEntry> accessPoints) {
1161         if (!mCanConfigWifi) {
1162             return;
1163         }
1164 
1165         WifiEntry connectedEntry = null;
1166         List<WifiEntry> wifiEntries = null;
1167         final int accessPointsSize = (accessPoints == null) ? 0 : accessPoints.size();
1168         final boolean hasMoreWifiEntries = (accessPointsSize > MAX_WIFI_ENTRY_COUNT);
1169         if (accessPointsSize > 0) {
1170             wifiEntries = new ArrayList<>();
1171             final int count = hasMoreWifiEntries ? MAX_WIFI_ENTRY_COUNT : accessPointsSize;
1172             mConnectedWifiInternetMonitor.unregisterCallback();
1173             for (int i = 0; i < count; i++) {
1174                 WifiEntry entry = accessPoints.get(i);
1175                 mConnectedWifiInternetMonitor.registerCallbackIfNeed(entry);
1176                 if (connectedEntry == null && entry.isDefaultNetwork()
1177                         && entry.hasInternetAccess()) {
1178                     connectedEntry = entry;
1179                 } else {
1180                     wifiEntries.add(entry);
1181                 }
1182             }
1183             mHasWifiEntries = true;
1184         } else {
1185             mHasWifiEntries = false;
1186         }
1187 
1188         if (mCallback != null) {
1189             mCallback.onAccessPointsChanged(wifiEntries, connectedEntry, hasMoreWifiEntries);
1190         }
1191     }
1192 
1193     @Override
1194     public void onSettingsActivityTriggered(Intent settingsIntent) {
1195     }
1196 
1197     @Override
1198     public void onWifiScan(boolean isScan) {
1199         if (!isWifiEnabled() || isDeviceLocked()) {
1200             mCallback.onWifiScan(false);
1201             return;
1202         }
1203         mCallback.onWifiScan(isScan);
1204     }
1205 
1206     private class InternetTelephonyCallback extends TelephonyCallback implements
1207             TelephonyCallback.DataEnabledListener,
1208             TelephonyCallback.DataConnectionStateListener,
1209             TelephonyCallback.DisplayInfoListener,
1210             TelephonyCallback.ServiceStateListener,
1211             TelephonyCallback.SignalStrengthsListener,
1212             TelephonyCallback.UserMobileDataStateListener,
1213             TelephonyCallback.CarrierNetworkListener {
1214 
1215         private final int mSubId;
1216 
1217         private InternetTelephonyCallback(int subId) {
1218             mSubId = subId;
1219         }
1220 
1221         @Override
1222         public void onServiceStateChanged(@NonNull ServiceState serviceState) {
1223             if (mCallback != null) {
1224                 mCallback.onServiceStateChanged(serviceState);
1225             }
1226             mSubIdServiceState.put(mSubId, serviceState);
1227         }
1228 
1229         @Override
1230         public void onDataConnectionStateChanged(int state, int networkType) {
1231             if (mCallback != null) {
1232                 mCallback.onDataConnectionStateChanged(state, networkType);
1233             }
1234         }
1235 
1236         @Override
1237         public void onSignalStrengthsChanged(@NonNull SignalStrength signalStrength) {
1238             if (mCallback != null) {
1239                 mCallback.onSignalStrengthsChanged(signalStrength);
1240             }
1241         }
1242 
1243         @Override
1244         public void onDisplayInfoChanged(@NonNull TelephonyDisplayInfo telephonyDisplayInfo) {
1245             mSubIdTelephonyDisplayInfoMap.put(mSubId, telephonyDisplayInfo);
1246             if (mCallback != null) {
1247                 mCallback.onDisplayInfoChanged(telephonyDisplayInfo);
1248             }
1249         }
1250 
1251         @Override
1252         public void onUserMobileDataStateChanged(boolean enabled) {
1253             if (mCallback != null) {
1254                 mCallback.onUserMobileDataStateChanged(enabled);
1255             }
1256         }
1257 
1258         @Override
1259         public void onCarrierNetworkChange(boolean active) {
1260             mCarrierNetworkChangeMode = active;
1261             if (mCallback != null) {
1262                 mCallback.onCarrierNetworkChange(active);
1263             }
1264         }
1265 
1266         @Override
1267         public void onDataEnabledChanged(boolean b, int i) {
1268             if (mSubId == mDefaultDataSubId) {
1269                 mIsMobileDataEnabled = b;
1270             }
1271         }
1272     }
1273 
1274     private class InternetOnSubscriptionChangedListener
1275             extends SubscriptionManager.OnSubscriptionsChangedListener {
1276         InternetOnSubscriptionChangedListener() {
1277             super();
1278         }
1279 
1280         @Override
1281         public void onSubscriptionsChanged() {
1282             refreshHasActiveSubIdOnDds();
1283             updateListener();
1284         }
1285     }
1286 
1287     private class DataConnectivityListener extends ConnectivityManager.NetworkCallback {
1288         @Override
1289         @WorkerThread
1290         public void onCapabilitiesChanged(@NonNull Network network,
1291                 @NonNull NetworkCapabilities capabilities) {
1292             mHasEthernet = capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET);
1293             if (mCanConfigWifi && (mHasEthernet || capabilities.hasTransport(
1294                     NetworkCapabilities.TRANSPORT_WIFI))) {
1295                 scanWifiAccessPoints();
1296             }
1297             // update UI
1298             if (mCallback != null) {
1299                 mCallback.onCapabilitiesChanged(network, capabilities);
1300             }
1301         }
1302 
1303         @Override
1304         @WorkerThread
1305         public void onLost(@NonNull Network network) {
1306             mHasEthernet = false;
1307             if (mCallback != null) {
1308                 mCallback.onLost(network);
1309             }
1310         }
1311     }
1312 
1313     /**
1314      * Helper class for monitoring the Internet access of the connected WifiEntry.
1315      */
1316     @VisibleForTesting
1317     protected class ConnectedWifiInternetMonitor implements WifiEntry.WifiEntryCallback {
1318 
1319         private WifiEntry mWifiEntry;
1320 
1321         public void registerCallbackIfNeed(WifiEntry entry) {
1322             if (entry == null || mWifiEntry != null) {
1323                 return;
1324             }
1325             // If the Wi-Fi is not connected yet, or it's the connected Wi-Fi with Internet
1326             // access. Then we don't need to listen to the callback to update the Wi-Fi entries.
1327             if (entry.getConnectedState() != CONNECTED_STATE_CONNECTED
1328                     || (entry.isDefaultNetwork() && entry.hasInternetAccess())) {
1329                 return;
1330             }
1331             mWifiEntry = entry;
1332             entry.setListener(this);
1333         }
1334 
1335         public void unregisterCallback() {
1336             if (mWifiEntry == null) {
1337                 return;
1338             }
1339             mWifiEntry.setListener(null);
1340             mWifiEntry = null;
1341         }
1342 
1343         @MainThread
1344         @Override
1345         public void onUpdated() {
1346             if (mWifiEntry == null) {
1347                 return;
1348             }
1349             WifiEntry entry = mWifiEntry;
1350             if (entry.getConnectedState() != CONNECTED_STATE_CONNECTED) {
1351                 unregisterCallback();
1352                 return;
1353             }
1354             if (entry.isDefaultNetwork() && entry.hasInternetAccess()) {
1355                 unregisterCallback();
1356                 // Trigger onAccessPointsChanged() to update the Wi-Fi entries.
1357                 scanWifiAccessPoints();
1358             }
1359         }
1360     }
1361 
1362     /**
1363      * Return {@code true} If the Ethernet exists
1364      */
1365     @MainThread
1366     public boolean hasEthernet() {
1367         return mHasEthernet;
1368     }
1369 
1370     private final BroadcastReceiver mConnectionStateReceiver = new BroadcastReceiver() {
1371         @Override
1372         public void onReceive(Context context, Intent intent) {
1373             final String action = intent.getAction();
1374             if (TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED.equals(action)) {
1375                 if (DEBUG) {
1376                     Log.d(TAG, "ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED");
1377                 }
1378                 mConfig = MobileMappings.Config.readConfig(context);
1379                 refreshHasActiveSubIdOnDds();
1380                 updateListener();
1381             } else if (WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION.equals(action)) {
1382                 updateListener();
1383             }
1384         }
1385     };
1386 
1387     private void updateListener() {
1388         int defaultDataSubId = getDefaultDataSubscriptionId();
1389         if (mDefaultDataSubId == getDefaultDataSubscriptionId()) {
1390             if (DEBUG) {
1391                 Log.d(TAG, "DDS: no change");
1392             }
1393             return;
1394         }
1395         if (DEBUG) {
1396             Log.d(TAG, "DDS: defaultDataSubId:" + defaultDataSubId);
1397         }
1398 
1399         if (SubscriptionManager.isUsableSubscriptionId(defaultDataSubId)) {
1400             // clean up old defaultDataSubId
1401             TelephonyCallback oldCallback = mSubIdTelephonyCallbackMap.get(mDefaultDataSubId);
1402             if (oldCallback != null) {
1403                 mTelephonyManager.unregisterTelephonyCallback(oldCallback);
1404             } else if (DEBUG) {
1405                 Log.e(TAG, "Unexpected null telephony call back for Sub " + mDefaultDataSubId);
1406             }
1407             mSubIdTelephonyCallbackMap.remove(mDefaultDataSubId);
1408             mSubIdTelephonyDisplayInfoMap.remove(mDefaultDataSubId);
1409             mSubIdTelephonyManagerMap.remove(mDefaultDataSubId);
1410 
1411             // create for new defaultDataSubId
1412             mTelephonyManager = mTelephonyManager.createForSubscriptionId(defaultDataSubId);
1413             mSubIdTelephonyManagerMap.put(defaultDataSubId, mTelephonyManager);
1414             registerInternetTelephonyCallback(mTelephonyManager, defaultDataSubId);
1415             mCallback.onSubscriptionsChanged(defaultDataSubId);
1416         }
1417         mDefaultDataSubId = defaultDataSubId;
1418     }
1419 
1420     boolean mayLaunchShareWifiSettings(WifiEntry wifiEntry, View view) {
1421         Intent intent = getConfiguratorQrCodeGeneratorIntentOrNull(wifiEntry);
1422         if (intent == null) {
1423             return false;
1424         }
1425         startActivity(intent, view);
1426         return true;
1427     }
1428 
1429     interface InternetDialogCallback {
1430 
1431         void onRefreshCarrierInfo();
1432 
1433         void onSimStateChanged();
1434 
1435         void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities);
1436 
1437         void onLost(@NonNull Network network);
1438 
1439         void onSubscriptionsChanged(int defaultDataSubId);
1440 
1441         void onServiceStateChanged(ServiceState serviceState);
1442 
1443         void onDataConnectionStateChanged(int state, int networkType);
1444 
1445         void onSignalStrengthsChanged(SignalStrength signalStrength);
1446 
1447         void onUserMobileDataStateChanged(boolean enabled);
1448 
1449         void onDisplayInfoChanged(TelephonyDisplayInfo telephonyDisplayInfo);
1450 
1451         void onCarrierNetworkChange(boolean active);
1452 
1453         void dismissDialog();
1454 
1455         void onAccessPointsChanged(@Nullable List<WifiEntry> wifiEntries,
1456                 @Nullable WifiEntry connectedEntry, boolean hasMoreWifiEntries);
1457 
1458         void onWifiScan(boolean isScan);
1459     }
1460 
1461     void makeOverlayToast(int stringId) {
1462         final Resources res = mContext.getResources();
1463 
1464         final SystemUIToast systemUIToast = mToastFactory.createToast(mContext, mContext,
1465                 res.getString(stringId), mContext.getPackageName(), UserHandle.myUserId(),
1466                 res.getConfiguration().orientation);
1467         if (systemUIToast == null) {
1468             return;
1469         }
1470 
1471         View toastView = systemUIToast.getView();
1472 
1473         final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
1474         params.height = WindowManager.LayoutParams.WRAP_CONTENT;
1475         params.width = WindowManager.LayoutParams.WRAP_CONTENT;
1476         params.format = PixelFormat.TRANSLUCENT;
1477         params.type = WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL;
1478         params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
1479                 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
1480                 | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
1481         params.y = systemUIToast.getYOffset();
1482 
1483         int absGravity = Gravity.getAbsoluteGravity(systemUIToast.getGravity(),
1484                 res.getConfiguration().getLayoutDirection());
1485         params.gravity = absGravity;
1486         if ((absGravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) {
1487             params.horizontalWeight = TOAST_PARAMS_HORIZONTAL_WEIGHT;
1488         }
1489         if ((absGravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL) {
1490             params.verticalWeight = TOAST_PARAMS_VERTICAL_WEIGHT;
1491         }
1492 
1493         mWindowManager.addView(toastView, params);
1494 
1495         Animator inAnimator = systemUIToast.getInAnimation();
1496         if (inAnimator != null) {
1497             inAnimator.start();
1498         }
1499 
1500         mHandler.postDelayed(new Runnable() {
1501             @Override
1502             public void run() {
1503                 Animator outAnimator = systemUIToast.getOutAnimation();
1504                 if (outAnimator != null) {
1505                     outAnimator.start();
1506                     outAnimator.addListener(new AnimatorListenerAdapter() {
1507                         @Override
1508                         public void onAnimationEnd(Animator animator) {
1509                             mWindowManager.removeViewImmediate(toastView);
1510                         }
1511                     });
1512                 }
1513             }
1514         }, SHORT_DURATION_TIMEOUT);
1515     }
1516 
1517     Intent getConfiguratorQrCodeGeneratorIntentOrNull(WifiEntry wifiEntry) {
1518         if (!mFeatureFlags.isEnabled(Flags.SHARE_WIFI_QS_BUTTON) || wifiEntry == null
1519                 || mWifiManager == null || !wifiEntry.canShare()) {
1520             return null;
1521         }
1522         var wifiConfiguration = wifiEntry.getWifiConfiguration();
1523         if (wifiConfiguration == null) {
1524             return null;
1525         }
1526         Intent intent = new Intent();
1527         intent.setAction(WifiDppIntentHelper.ACTION_CONFIGURATOR_AUTH_QR_CODE_GENERATOR);
1528         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
1529         WifiDppIntentHelper.setConfiguratorIntentExtra(intent, mWifiManager, wifiConfiguration);
1530         return intent;
1531     }
1532 }
1533