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