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 com.android.settingslib.mobile.MobileMappings.getIconKey;
20 import static com.android.settingslib.mobile.MobileMappings.mapIconSets;
21 import static com.android.wifitrackerlib.WifiEntry.CONNECTED_STATE_CONNECTED;
22 
23 import android.animation.Animator;
24 import android.animation.AnimatorListenerAdapter;
25 import android.content.BroadcastReceiver;
26 import android.content.Context;
27 import android.content.Intent;
28 import android.content.IntentFilter;
29 import android.content.res.Resources;
30 import android.graphics.Color;
31 import android.graphics.PixelFormat;
32 import android.graphics.drawable.ColorDrawable;
33 import android.graphics.drawable.Drawable;
34 import android.graphics.drawable.LayerDrawable;
35 import android.net.ConnectivityManager;
36 import android.net.Network;
37 import android.net.NetworkCapabilities;
38 import android.net.wifi.WifiManager;
39 import android.os.Handler;
40 import android.os.UserHandle;
41 import android.provider.Settings;
42 import android.telephony.AccessNetworkConstants;
43 import android.telephony.NetworkRegistrationInfo;
44 import android.telephony.ServiceState;
45 import android.telephony.SignalStrength;
46 import android.telephony.SubscriptionInfo;
47 import android.telephony.SubscriptionManager;
48 import android.telephony.TelephonyCallback;
49 import android.telephony.TelephonyDisplayInfo;
50 import android.telephony.TelephonyManager;
51 import android.text.TextUtils;
52 import android.util.Log;
53 import android.view.Gravity;
54 import android.view.View;
55 import android.view.WindowManager;
56 
57 import androidx.annotation.MainThread;
58 import androidx.annotation.NonNull;
59 import androidx.annotation.Nullable;
60 import androidx.annotation.VisibleForTesting;
61 import androidx.annotation.WorkerThread;
62 
63 import com.android.internal.logging.UiEventLogger;
64 import com.android.keyguard.KeyguardUpdateMonitor;
65 import com.android.keyguard.KeyguardUpdateMonitorCallback;
66 import com.android.settingslib.DeviceInfoUtils;
67 import com.android.settingslib.SignalIcon;
68 import com.android.settingslib.Utils;
69 import com.android.settingslib.graph.SignalDrawable;
70 import com.android.settingslib.mobile.MobileMappings;
71 import com.android.settingslib.mobile.TelephonyIcons;
72 import com.android.settingslib.net.SignalStrengthUtil;
73 import com.android.settingslib.wifi.WifiUtils;
74 import com.android.systemui.R;
75 import com.android.systemui.animation.DialogLaunchAnimator;
76 import com.android.systemui.broadcast.BroadcastDispatcher;
77 import com.android.systemui.dagger.qualifiers.Background;
78 import com.android.systemui.dagger.qualifiers.Main;
79 import com.android.systemui.plugins.ActivityStarter;
80 import com.android.systemui.statusbar.connectivity.AccessPointController;
81 import com.android.systemui.statusbar.policy.KeyguardStateController;
82 import com.android.systemui.statusbar.policy.LocationController;
83 import com.android.systemui.toast.SystemUIToast;
84 import com.android.systemui.toast.ToastFactory;
85 import com.android.systemui.util.CarrierConfigTracker;
86 import com.android.systemui.util.settings.GlobalSettings;
87 import com.android.wifitrackerlib.MergedCarrierEntry;
88 import com.android.wifitrackerlib.WifiEntry;
89 
90 import java.util.ArrayList;
91 import java.util.HashSet;
92 import java.util.List;
93 import java.util.Map;
94 import java.util.Set;
95 import java.util.concurrent.Executor;
96 import java.util.concurrent.atomic.AtomicReference;
97 import java.util.function.Supplier;
98 import java.util.stream.Collectors;
99 import java.util.stream.Stream;
100 
101 import javax.inject.Inject;
102 
103 /**
104  * Controller for Internet Dialog.
105  */
106 public class InternetDialogController implements AccessPointController.AccessPointCallback {
107 
108     private static final String TAG = "InternetDialogController";
109     private static final String ACTION_NETWORK_PROVIDER_SETTINGS =
110             "android.settings.NETWORK_PROVIDER_SETTINGS";
111     private static final String ACTION_WIFI_SCANNING_SETTINGS =
112             "android.settings.WIFI_SCANNING_SETTINGS";
113     private static final String EXTRA_CHOSEN_WIFI_ENTRY_KEY = "key_chosen_wifientry_key";
114     public static final Drawable EMPTY_DRAWABLE = new ColorDrawable(Color.TRANSPARENT);
115     public static final int NO_CELL_DATA_TYPE_ICON = 0;
116     private static final int SUBTITLE_TEXT_WIFI_IS_OFF = R.string.wifi_is_off;
117     private static final int SUBTITLE_TEXT_TAP_A_NETWORK_TO_CONNECT =
118             R.string.tap_a_network_to_connect;
119     private static final int SUBTITLE_TEXT_UNLOCK_TO_VIEW_NETWORKS =
120             R.string.unlock_to_view_networks;
121     private static final int SUBTITLE_TEXT_SEARCHING_FOR_NETWORKS =
122             R.string.wifi_empty_list_wifi_on;
123     private static final int SUBTITLE_TEXT_NON_CARRIER_NETWORK_UNAVAILABLE =
124             R.string.non_carrier_network_unavailable;
125     private static final int SUBTITLE_TEXT_ALL_CARRIER_NETWORK_UNAVAILABLE =
126             R.string.all_network_unavailable;
127     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
128 
129     static final int MAX_WIFI_ENTRY_COUNT = 3;
130 
131     private WifiManager mWifiManager;
132     private Context mContext;
133     private SubscriptionManager mSubscriptionManager;
134     private TelephonyManager mTelephonyManager;
135     private ConnectivityManager mConnectivityManager;
136     private CarrierConfigTracker mCarrierConfigTracker;
137     private TelephonyDisplayInfo mTelephonyDisplayInfo =
138             new TelephonyDisplayInfo(TelephonyManager.NETWORK_TYPE_UNKNOWN,
139                     TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE);
140     private Handler mHandler;
141     private Handler mWorkerHandler;
142     private MobileMappings.Config mConfig = null;
143     private Executor mExecutor;
144     private AccessPointController mAccessPointController;
145     private IntentFilter mConnectionStateFilter;
146     private InternetDialogCallback mCallback;
147     private UiEventLogger mUiEventLogger;
148     private BroadcastDispatcher mBroadcastDispatcher;
149     private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
150     private GlobalSettings mGlobalSettings;
151     private int mDefaultDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
152     private ConnectivityManager.NetworkCallback mConnectivityManagerNetworkCallback;
153     private WindowManager mWindowManager;
154     private ToastFactory mToastFactory;
155     private SignalDrawable mSignalDrawable;
156     private LocationController mLocationController;
157     private DialogLaunchAnimator mDialogLaunchAnimator;
158     private boolean mHasWifiEntries;
159 
160     @VisibleForTesting
161     static final float TOAST_PARAMS_HORIZONTAL_WEIGHT = 1.0f;
162     @VisibleForTesting
163     static final float TOAST_PARAMS_VERTICAL_WEIGHT = 1.0f;
164     @VisibleForTesting
165     static final long SHORT_DURATION_TIMEOUT = 4000;
166     @VisibleForTesting
167     protected ActivityStarter mActivityStarter;
168     @VisibleForTesting
169     protected SubscriptionManager.OnSubscriptionsChangedListener mOnSubscriptionsChangedListener;
170     @VisibleForTesting
171     protected InternetTelephonyCallback mInternetTelephonyCallback;
172     @VisibleForTesting
173     protected WifiUtils.InternetIconInjector mWifiIconInjector;
174     @VisibleForTesting
175     protected boolean mCanConfigWifi;
176     @VisibleForTesting
177     protected KeyguardStateController mKeyguardStateController;
178     @VisibleForTesting
179     protected boolean mHasEthernet = false;
180     @VisibleForTesting
181     protected ConnectedWifiInternetMonitor mConnectedWifiInternetMonitor;
182 
183     private final KeyguardUpdateMonitorCallback mKeyguardUpdateCallback =
184             new KeyguardUpdateMonitorCallback() {
185                 @Override
186                 public void onRefreshCarrierInfo() {
187                     mCallback.onRefreshCarrierInfo();
188                 }
189 
190                 @Override
191                 public void onSimStateChanged(int subId, int slotId, int simState) {
192                     mCallback.onSimStateChanged();
193                 }
194             };
195 
getSubscriptionInfo()196     protected List<SubscriptionInfo> getSubscriptionInfo() {
197         return mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(false);
198     }
199 
200     @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, DialogLaunchAnimator dialogLaunchAnimator)201     public InternetDialogController(@NonNull Context context, UiEventLogger uiEventLogger,
202             ActivityStarter starter, AccessPointController accessPointController,
203             SubscriptionManager subscriptionManager, TelephonyManager telephonyManager,
204             @Nullable WifiManager wifiManager, ConnectivityManager connectivityManager,
205             @Main Handler handler, @Main Executor mainExecutor,
206             BroadcastDispatcher broadcastDispatcher, KeyguardUpdateMonitor keyguardUpdateMonitor,
207             GlobalSettings globalSettings, KeyguardStateController keyguardStateController,
208             WindowManager windowManager, ToastFactory toastFactory,
209             @Background Handler workerHandler,
210             CarrierConfigTracker carrierConfigTracker,
211             LocationController locationController,
212             DialogLaunchAnimator dialogLaunchAnimator) {
213         if (DEBUG) {
214             Log.d(TAG, "Init InternetDialogController");
215         }
216         mHandler = handler;
217         mWorkerHandler = workerHandler;
218         mExecutor = mainExecutor;
219         mContext = context;
220         mGlobalSettings = globalSettings;
221         mWifiManager = wifiManager;
222         mTelephonyManager = telephonyManager;
223         mConnectivityManager = connectivityManager;
224         mSubscriptionManager = subscriptionManager;
225         mCarrierConfigTracker = carrierConfigTracker;
226         mBroadcastDispatcher = broadcastDispatcher;
227         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
228         mKeyguardStateController = keyguardStateController;
229         mConnectionStateFilter = new IntentFilter();
230         mConnectionStateFilter.addAction(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
231         mConnectionStateFilter.addAction(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
232         mUiEventLogger = uiEventLogger;
233         mActivityStarter = starter;
234         mAccessPointController = accessPointController;
235         mWifiIconInjector = new WifiUtils.InternetIconInjector(mContext);
236         mConnectivityManagerNetworkCallback = new DataConnectivityListener();
237         mWindowManager = windowManager;
238         mToastFactory = toastFactory;
239         mSignalDrawable = new SignalDrawable(mContext);
240         mLocationController = locationController;
241         mDialogLaunchAnimator = dialogLaunchAnimator;
242         mConnectedWifiInternetMonitor = new ConnectedWifiInternetMonitor();
243     }
244 
onStart(@onNull InternetDialogCallback callback, boolean canConfigWifi)245     void onStart(@NonNull InternetDialogCallback callback, boolean canConfigWifi) {
246         if (DEBUG) {
247             Log.d(TAG, "onStart");
248         }
249         mCallback = callback;
250         mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateCallback);
251         mAccessPointController.addAccessPointCallback(this);
252         mBroadcastDispatcher.registerReceiver(mConnectionStateReceiver, mConnectionStateFilter,
253                 mExecutor);
254         // Listen the subscription changes
255         mOnSubscriptionsChangedListener = new InternetOnSubscriptionChangedListener();
256         mSubscriptionManager.addOnSubscriptionsChangedListener(mExecutor,
257                 mOnSubscriptionsChangedListener);
258         mDefaultDataSubId = getDefaultDataSubscriptionId();
259         if (DEBUG) {
260             Log.d(TAG, "Init, SubId: " + mDefaultDataSubId);
261         }
262         mConfig = MobileMappings.Config.readConfig(mContext);
263         mTelephonyManager = mTelephonyManager.createForSubscriptionId(mDefaultDataSubId);
264         mInternetTelephonyCallback = new InternetTelephonyCallback();
265         mTelephonyManager.registerTelephonyCallback(mExecutor, mInternetTelephonyCallback);
266         // Listen the connectivity changes
267         mConnectivityManager.registerDefaultNetworkCallback(mConnectivityManagerNetworkCallback);
268         mCanConfigWifi = canConfigWifi;
269         scanWifiAccessPoints();
270     }
271 
onStop()272     void onStop() {
273         if (DEBUG) {
274             Log.d(TAG, "onStop");
275         }
276         mBroadcastDispatcher.unregisterReceiver(mConnectionStateReceiver);
277         mTelephonyManager.unregisterTelephonyCallback(mInternetTelephonyCallback);
278         mSubscriptionManager.removeOnSubscriptionsChangedListener(
279                 mOnSubscriptionsChangedListener);
280         mAccessPointController.removeAccessPointCallback(this);
281         mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateCallback);
282         mConnectivityManager.unregisterNetworkCallback(mConnectivityManagerNetworkCallback);
283         mConnectedWifiInternetMonitor.unregisterCallback();
284     }
285 
286     @VisibleForTesting
isAirplaneModeEnabled()287     boolean isAirplaneModeEnabled() {
288         return mGlobalSettings.getInt(Settings.Global.AIRPLANE_MODE_ON, 0) != 0;
289     }
290 
setAirplaneModeDisabled()291     void setAirplaneModeDisabled() {
292         mConnectivityManager.setAirplaneMode(false);
293     }
294 
295     @VisibleForTesting
getDefaultDataSubscriptionId()296     protected int getDefaultDataSubscriptionId() {
297         return mSubscriptionManager.getDefaultDataSubscriptionId();
298     }
299 
300     @VisibleForTesting
getSettingsIntent()301     protected Intent getSettingsIntent() {
302         return new Intent(ACTION_NETWORK_PROVIDER_SETTINGS).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
303     }
304 
getWifiDetailsSettingsIntent(String key)305     protected Intent getWifiDetailsSettingsIntent(String key) {
306         if (TextUtils.isEmpty(key)) {
307             if (DEBUG) {
308                 Log.d(TAG, "connected entry's key is empty");
309             }
310             return null;
311         }
312         return WifiUtils.getWifiDetailsSettingsIntent(key);
313     }
314 
getDialogTitleText()315     CharSequence getDialogTitleText() {
316         if (isAirplaneModeEnabled()) {
317             return mContext.getText(R.string.airplane_mode);
318         }
319         return mContext.getText(R.string.quick_settings_internet_label);
320     }
321 
getSubtitleText(boolean isProgressBarVisible)322     CharSequence getSubtitleText(boolean isProgressBarVisible) {
323         if (mCanConfigWifi && !mWifiManager.isWifiEnabled()) {
324             // When Wi-Fi is disabled.
325             //   Sub-Title: Wi-Fi is off
326             if (DEBUG) {
327                 Log.d(TAG, "Wi-Fi off.");
328             }
329             return mContext.getText(SUBTITLE_TEXT_WIFI_IS_OFF);
330         }
331 
332         if (isDeviceLocked()) {
333             // When the device is locked.
334             //   Sub-Title: Unlock to view networks
335             if (DEBUG) {
336                 Log.d(TAG, "The device is locked.");
337             }
338             return mContext.getText(SUBTITLE_TEXT_UNLOCK_TO_VIEW_NETWORKS);
339         }
340 
341         if (mHasWifiEntries) {
342             return mCanConfigWifi ? mContext.getText(SUBTITLE_TEXT_TAP_A_NETWORK_TO_CONNECT) : null;
343         }
344 
345         if (mCanConfigWifi && isProgressBarVisible) {
346             // When the Wi-Fi scan result callback is received
347             //   Sub-Title: Searching for networks...
348             return mContext.getText(SUBTITLE_TEXT_SEARCHING_FOR_NETWORKS);
349         }
350 
351         if (isCarrierNetworkActive()) {
352             return mContext.getText(SUBTITLE_TEXT_NON_CARRIER_NETWORK_UNAVAILABLE);
353         }
354 
355         // Sub-Title:
356         // show non_carrier_network_unavailable
357         //   - while Wi-Fi on + no Wi-Fi item
358         //   - while Wi-Fi on + no Wi-Fi item + mobile data off
359         // show all_network_unavailable:
360         //   - while Wi-Fi on + no Wi-Fi item + no carrier item
361         //   - while Wi-Fi on + no Wi-Fi item + service is out of service
362         //   - while Wi-Fi on + no Wi-Fi item + mobile data on + no carrier data.
363         if (DEBUG) {
364             Log.d(TAG, "No Wi-Fi item.");
365         }
366         if (!hasActiveSubId() || (!isVoiceStateInService() && !isDataStateInService())) {
367             if (DEBUG) {
368                 Log.d(TAG, "No carrier or service is out of service.");
369             }
370             return mContext.getText(SUBTITLE_TEXT_ALL_CARRIER_NETWORK_UNAVAILABLE);
371         }
372 
373         if (mCanConfigWifi && !isMobileDataEnabled()) {
374             if (DEBUG) {
375                 Log.d(TAG, "Mobile data off");
376             }
377             return mContext.getText(SUBTITLE_TEXT_NON_CARRIER_NETWORK_UNAVAILABLE);
378         }
379 
380         if (!activeNetworkIsCellular()) {
381             if (DEBUG) {
382                 Log.d(TAG, "No carrier data.");
383             }
384             return mContext.getText(SUBTITLE_TEXT_ALL_CARRIER_NETWORK_UNAVAILABLE);
385         }
386 
387         if (mCanConfigWifi) {
388             return mContext.getText(SUBTITLE_TEXT_NON_CARRIER_NETWORK_UNAVAILABLE);
389         }
390         return null;
391     }
392 
getInternetWifiDrawable(@onNull WifiEntry wifiEntry)393     Drawable getInternetWifiDrawable(@NonNull WifiEntry wifiEntry) {
394         if (wifiEntry.getLevel() == WifiEntry.WIFI_LEVEL_UNREACHABLE) {
395             return null;
396         }
397         final Drawable drawable =
398                 mWifiIconInjector.getIcon(wifiEntry.shouldShowXLevelIcon(), wifiEntry.getLevel());
399         if (drawable == null) {
400             return null;
401         }
402         drawable.setTint(mContext.getColor(R.color.connected_network_primary_color));
403         return drawable;
404     }
405 
getSignalStrengthDrawable()406     Drawable getSignalStrengthDrawable() {
407         Drawable drawable = mContext.getDrawable(
408                 R.drawable.ic_signal_strength_zero_bar_no_internet);
409         try {
410             if (mTelephonyManager == null) {
411                 if (DEBUG) {
412                     Log.d(TAG, "TelephonyManager is null");
413                 }
414                 return drawable;
415             }
416 
417             boolean isCarrierNetworkActive = isCarrierNetworkActive();
418             if (isDataStateInService() || isVoiceStateInService() || isCarrierNetworkActive) {
419                 AtomicReference<Drawable> shared = new AtomicReference<>();
420                 shared.set(getSignalStrengthDrawableWithLevel(isCarrierNetworkActive));
421                 drawable = shared.get();
422             }
423 
424             int tintColor = Utils.getColorAttrDefaultColor(mContext,
425                     android.R.attr.textColorTertiary);
426             if (activeNetworkIsCellular() || isCarrierNetworkActive) {
427                 tintColor = mContext.getColor(R.color.connected_network_primary_color);
428             }
429             drawable.setTint(tintColor);
430         } catch (Throwable e) {
431             e.printStackTrace();
432         }
433         return drawable;
434     }
435 
436     /**
437      * To get the signal bar icon with level.
438      *
439      * @return The Drawable which is a signal bar icon with level.
440      */
getSignalStrengthDrawableWithLevel(boolean isCarrierNetworkActive)441     Drawable getSignalStrengthDrawableWithLevel(boolean isCarrierNetworkActive) {
442         final SignalStrength strength = mTelephonyManager.getSignalStrength();
443         int level = (strength == null) ? 0 : strength.getLevel();
444         int numLevels = SignalStrength.NUM_SIGNAL_STRENGTH_BINS;
445         if ((mSubscriptionManager != null && shouldInflateSignalStrength(mDefaultDataSubId))
446                 || isCarrierNetworkActive) {
447             level = isCarrierNetworkActive
448                     ? SignalStrength.NUM_SIGNAL_STRENGTH_BINS
449                     : (level + 1);
450             numLevels += 1;
451         }
452         return getSignalStrengthIcon(mContext, level, numLevels, NO_CELL_DATA_TYPE_ICON,
453                 !isMobileDataEnabled());
454     }
455 
getSignalStrengthIcon(Context context, int level, int numLevels, int iconType, boolean cutOut)456     Drawable getSignalStrengthIcon(Context context, int level, int numLevels,
457             int iconType, boolean cutOut) {
458         mSignalDrawable.setLevel(SignalDrawable.getState(level, numLevels, cutOut));
459 
460         // Make the network type drawable
461         final Drawable networkDrawable =
462                 iconType == NO_CELL_DATA_TYPE_ICON
463                         ? EMPTY_DRAWABLE
464                         : context.getResources().getDrawable(iconType, context.getTheme());
465 
466         // Overlay the two drawables
467         final Drawable[] layers = {networkDrawable, mSignalDrawable};
468         final int iconSize =
469                 context.getResources().getDimensionPixelSize(R.dimen.signal_strength_icon_size);
470 
471         final LayerDrawable icons = new LayerDrawable(layers);
472         // Set the network type icon at the top left
473         icons.setLayerGravity(0 /* index of networkDrawable */, Gravity.TOP | Gravity.LEFT);
474         // Set the signal strength icon at the bottom right
475         icons.setLayerGravity(1 /* index of SignalDrawable */, Gravity.BOTTOM | Gravity.RIGHT);
476         icons.setLayerSize(1 /* index of SignalDrawable */, iconSize, iconSize);
477         icons.setTintList(Utils.getColorAttr(context, android.R.attr.textColorTertiary));
478         return icons;
479     }
480 
shouldInflateSignalStrength(int subId)481     private boolean shouldInflateSignalStrength(int subId) {
482         return SignalStrengthUtil.shouldInflateSignalStrength(mContext, subId);
483     }
484 
getUniqueSubscriptionDisplayName(int subscriptionId, Context context)485     private CharSequence getUniqueSubscriptionDisplayName(int subscriptionId, Context context) {
486         final Map<Integer, CharSequence> displayNames = getUniqueSubscriptionDisplayNames(context);
487         return displayNames.getOrDefault(subscriptionId, "");
488     }
489 
getUniqueSubscriptionDisplayNames(Context context)490     private Map<Integer, CharSequence> getUniqueSubscriptionDisplayNames(Context context) {
491         class DisplayInfo {
492             public SubscriptionInfo subscriptionInfo;
493             public CharSequence originalName;
494             public CharSequence uniqueName;
495         }
496 
497         // Map of SubscriptionId to DisplayName
498         final Supplier<Stream<DisplayInfo>> originalInfos =
499                 () -> getSubscriptionInfo()
500                         .stream()
501                         .filter(i -> {
502                             // Filter out null values.
503                             return (i != null && i.getDisplayName() != null);
504                         })
505                         .map(i -> {
506                             DisplayInfo info = new DisplayInfo();
507                             info.subscriptionInfo = i;
508                             info.originalName = i.getDisplayName().toString().trim();
509                             return info;
510                         });
511 
512         // A Unique set of display names
513         Set<CharSequence> uniqueNames = new HashSet<>();
514         // Return the set of duplicate names
515         final Set<CharSequence> duplicateOriginalNames = originalInfos.get()
516                 .filter(info -> !uniqueNames.add(info.originalName))
517                 .map(info -> info.originalName)
518                 .collect(Collectors.toSet());
519 
520         // If a display name is duplicate, append the final 4 digits of the phone number.
521         // Creates a mapping of Subscription id to original display name + phone number display name
522         final Supplier<Stream<DisplayInfo>> uniqueInfos = () -> originalInfos.get().map(info -> {
523             if (duplicateOriginalNames.contains(info.originalName)) {
524                 // This may return null, if the user cannot view the phone number itself.
525                 final String phoneNumber = DeviceInfoUtils.getBidiFormattedPhoneNumber(context,
526                         info.subscriptionInfo);
527                 String lastFourDigits = "";
528                 if (phoneNumber != null) {
529                     lastFourDigits = (phoneNumber.length() > 4)
530                             ? phoneNumber.substring(phoneNumber.length() - 4) : phoneNumber;
531                 }
532 
533                 if (TextUtils.isEmpty(lastFourDigits)) {
534                     info.uniqueName = info.originalName;
535                 } else {
536                     info.uniqueName = info.originalName + " " + lastFourDigits;
537                 }
538 
539             } else {
540                 info.uniqueName = info.originalName;
541             }
542             return info;
543         });
544 
545         // Check uniqueness a second time.
546         // We might not have had permission to view the phone numbers.
547         // There might also be multiple phone numbers whose last 4 digits the same.
548         uniqueNames.clear();
549         final Set<CharSequence> duplicatePhoneNames = uniqueInfos.get()
550                 .filter(info -> !uniqueNames.add(info.uniqueName))
551                 .map(info -> info.uniqueName)
552                 .collect(Collectors.toSet());
553 
554         return uniqueInfos.get().map(info -> {
555             if (duplicatePhoneNames.contains(info.uniqueName)) {
556                 info.uniqueName = info.originalName + " "
557                         + info.subscriptionInfo.getSubscriptionId();
558             }
559             return info;
560         }).collect(Collectors.toMap(
561                 info -> info.subscriptionInfo.getSubscriptionId(),
562                 info -> info.uniqueName));
563     }
564 
565     CharSequence getMobileNetworkTitle() {
566         return getUniqueSubscriptionDisplayName(mDefaultDataSubId, mContext);
567     }
568 
569     String getMobileNetworkSummary() {
570         String description = getNetworkTypeDescription(mContext, mConfig,
571                 mTelephonyDisplayInfo, mDefaultDataSubId);
572         return getMobileSummary(mContext, description);
573     }
574 
575     /**
576      * Get currently description of mobile network type.
577      */
578     private String getNetworkTypeDescription(Context context, MobileMappings.Config config,
579             TelephonyDisplayInfo telephonyDisplayInfo, int subId) {
580         String iconKey = getIconKey(telephonyDisplayInfo);
581 
582         if (mapIconSets(config) == null || mapIconSets(config).get(iconKey) == null) {
583             if (DEBUG) {
584                 Log.d(TAG, "The description of network type is empty.");
585             }
586             return "";
587         }
588 
589         int resId = mapIconSets(config).get(iconKey).dataContentDescription;
590         if (isCarrierNetworkActive()) {
591             SignalIcon.MobileIconGroup carrierMergedWifiIconGroup =
592                     TelephonyIcons.CARRIER_MERGED_WIFI;
593             resId = carrierMergedWifiIconGroup.dataContentDescription;
594         }
595 
596         return resId != 0
597                 ? SubscriptionManager.getResourcesForSubId(context, subId).getString(resId) : "";
598     }
599 
600     private String getMobileSummary(Context context, String networkTypeDescription) {
601         if (!isMobileDataEnabled()) {
602             return context.getString(R.string.mobile_data_off_summary);
603         }
604 
605         String summary = networkTypeDescription;
606         // Set network description for the carrier network when connecting to the carrier network
607         // under the airplane mode ON.
608         if (activeNetworkIsCellular() || isCarrierNetworkActive()) {
609             summary = context.getString(R.string.preference_summary_default_combination,
610                     context.getString(R.string.mobile_data_connection_active),
611                     networkTypeDescription);
612         } else if (!isDataStateInService()) {
613             summary = context.getString(R.string.mobile_data_no_connection);
614         }
615 
616         return summary;
617     }
618 
619     void launchNetworkSetting() {
620         // Dismissing a dialog into its touch surface and starting an activity at the same time
621         // looks bad, so let's make sure the dialog just fades out quickly.
622         mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
623         mCallback.dismissDialog();
624 
625         mActivityStarter.postStartActivityDismissingKeyguard(getSettingsIntent(), 0);
626     }
627 
628     void launchWifiNetworkDetailsSetting(String key) {
629         Intent intent = getWifiDetailsSettingsIntent(key);
630         if (intent != null) {
631             // Dismissing a dialog into its touch surface and starting an activity at the same time
632             // looks bad, so let's make sure the dialog just fades out quickly.
633             mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
634             mCallback.dismissDialog();
635 
636             mActivityStarter.postStartActivityDismissingKeyguard(intent, 0);
637         }
638     }
639 
640     void launchWifiScanningSetting() {
641         // Dismissing a dialog into its touch surface and starting an activity at the same time
642         // looks bad, so let's make sure the dialog just fades out quickly.
643         mDialogLaunchAnimator.disableAllCurrentDialogsExitAnimations();
644         mCallback.dismissDialog();
645 
646         final Intent intent = new Intent(ACTION_WIFI_SCANNING_SETTINGS);
647         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
648         mActivityStarter.postStartActivityDismissingKeyguard(intent, 0);
649     }
650 
651     void connectCarrierNetwork() {
652         final MergedCarrierEntry mergedCarrierEntry =
653                 mAccessPointController.getMergedCarrierEntry();
654         if (mergedCarrierEntry != null && mergedCarrierEntry.canConnect()) {
655             mergedCarrierEntry.connect(null /* ConnectCallback */, false);
656             makeOverlayToast(R.string.wifi_wont_autoconnect_for_now);
657         }
658     }
659 
660     boolean isCarrierNetworkActive() {
661         final MergedCarrierEntry mergedCarrierEntry =
662                 mAccessPointController.getMergedCarrierEntry();
663         return mergedCarrierEntry != null && mergedCarrierEntry.isDefaultNetwork();
664     }
665 
666     @WorkerThread
667     void setMergedCarrierWifiEnabledIfNeed(int subId, boolean enabled) {
668         // If the Carrier Provisions Wi-Fi Merged Networks enabled, do not set the merged carrier
669         // Wi-Fi state together.
670         if (mCarrierConfigTracker.getCarrierProvisionsWifiMergedNetworksBool(subId)) {
671             return;
672         }
673 
674         final MergedCarrierEntry entry = mAccessPointController.getMergedCarrierEntry();
675         if (entry == null) {
676             if (DEBUG) {
677                 Log.d(TAG, "MergedCarrierEntry is null, can not set the status.");
678             }
679             return;
680         }
681         entry.setEnabled(enabled);
682     }
683 
684     WifiManager getWifiManager() {
685         return mWifiManager;
686     }
687 
688     TelephonyManager getTelephonyManager() {
689         return mTelephonyManager;
690     }
691 
692     SubscriptionManager getSubscriptionManager() {
693         return mSubscriptionManager;
694     }
695 
696     /**
697      * @return whether there is the carrier item in the slice.
698      */
699     boolean hasActiveSubId() {
700         if (mSubscriptionManager == null) {
701             if (DEBUG) {
702                 Log.d(TAG, "SubscriptionManager is null, can not check carrier.");
703             }
704             return false;
705         }
706 
707         if (isAirplaneModeEnabled() || mTelephonyManager == null
708                 || mSubscriptionManager.getActiveSubscriptionIdList().length <= 0) {
709             return false;
710         }
711         return true;
712     }
713 
714     /**
715      * Return {@code true} if mobile data is enabled
716      */
717     boolean isMobileDataEnabled() {
718         if (mTelephonyManager == null || !mTelephonyManager.isDataEnabled()) {
719             return false;
720         }
721         return true;
722     }
723 
724     /**
725      * Set whether to enable data for {@code subId}, also whether to disable data for other
726      * subscription
727      */
728     void setMobileDataEnabled(Context context, int subId, boolean enabled,
729             boolean disableOtherSubscriptions) {
730         if (mTelephonyManager == null) {
731             if (DEBUG) {
732                 Log.d(TAG, "TelephonyManager is null, can not set mobile data.");
733             }
734             return;
735         }
736 
737         if (mSubscriptionManager == null) {
738             if (DEBUG) {
739                 Log.d(TAG, "SubscriptionManager is null, can not set mobile data.");
740             }
741             return;
742         }
743 
744         mTelephonyManager.setDataEnabled(enabled);
745         if (disableOtherSubscriptions) {
746             final List<SubscriptionInfo> subInfoList =
747                     mSubscriptionManager.getActiveSubscriptionInfoList();
748             if (subInfoList != null) {
749                 for (SubscriptionInfo subInfo : subInfoList) {
750                     // We never disable mobile data for opportunistic subscriptions.
751                     if (subInfo.getSubscriptionId() != subId && !subInfo.isOpportunistic()) {
752                         context.getSystemService(TelephonyManager.class).createForSubscriptionId(
753                                 subInfo.getSubscriptionId()).setDataEnabled(false);
754                     }
755                 }
756             }
757         }
758         mWorkerHandler.post(() -> setMergedCarrierWifiEnabledIfNeed(subId, enabled));
759     }
760 
761     boolean isDataStateInService() {
762         final ServiceState serviceState = mTelephonyManager.getServiceState();
763         NetworkRegistrationInfo regInfo =
764                 (serviceState == null) ? null : serviceState.getNetworkRegistrationInfo(
765                         NetworkRegistrationInfo.DOMAIN_PS,
766                         AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
767         return (regInfo == null) ? false : regInfo.isRegistered();
768     }
769 
770     boolean isVoiceStateInService() {
771         if (mTelephonyManager == null) {
772             if (DEBUG) {
773                 Log.d(TAG, "TelephonyManager is null, can not detect voice state.");
774             }
775             return false;
776         }
777 
778         final ServiceState serviceState = mTelephonyManager.getServiceState();
779         return serviceState != null
780                 && serviceState.getState() == serviceState.STATE_IN_SERVICE;
781     }
782 
783     public boolean isDeviceLocked() {
784         return !mKeyguardStateController.isUnlocked();
785     }
786 
787     boolean activeNetworkIsCellular() {
788         if (mConnectivityManager == null) {
789             if (DEBUG) {
790                 Log.d(TAG, "ConnectivityManager is null, can not check active network.");
791             }
792             return false;
793         }
794 
795         final Network activeNetwork = mConnectivityManager.getActiveNetwork();
796         if (activeNetwork == null) {
797             return false;
798         }
799         final NetworkCapabilities networkCapabilities =
800                 mConnectivityManager.getNetworkCapabilities(activeNetwork);
801         if (networkCapabilities == null) {
802             return false;
803         }
804         return networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR);
805     }
806 
807     boolean connect(WifiEntry ap) {
808         if (ap == null) {
809             if (DEBUG) {
810                 Log.d(TAG, "No Wi-Fi ap to connect.");
811             }
812             return false;
813         }
814 
815         if (ap.getWifiConfiguration() != null) {
816             if (DEBUG) {
817                 Log.d(TAG, "connect networkId=" + ap.getWifiConfiguration().networkId);
818             }
819         } else {
820             if (DEBUG) {
821                 Log.d(TAG, "connect to unsaved network " + ap.getTitle());
822             }
823         }
824         ap.connect(new WifiEntryConnectCallback(mActivityStarter, ap, this));
825         return false;
826     }
827 
828     @WorkerThread
829     boolean isWifiScanEnabled() {
830         if (!mLocationController.isLocationEnabled()) {
831             return false;
832         }
833         return mWifiManager.isScanAlwaysAvailable();
834     }
835 
836     static class WifiEntryConnectCallback implements WifiEntry.ConnectCallback {
837         final ActivityStarter mActivityStarter;
838         final WifiEntry mWifiEntry;
839         final InternetDialogController mInternetDialogController;
840 
841         WifiEntryConnectCallback(ActivityStarter activityStarter, WifiEntry connectWifiEntry,
842                 InternetDialogController internetDialogController) {
843             mActivityStarter = activityStarter;
844             mWifiEntry = connectWifiEntry;
845             mInternetDialogController = internetDialogController;
846         }
847 
848         @Override
849         public void onConnectResult(@ConnectStatus int status) {
850             if (DEBUG) {
851                 Log.d(TAG, "onConnectResult " + status);
852             }
853 
854             if (status == WifiEntry.ConnectCallback.CONNECT_STATUS_FAILURE_NO_CONFIG) {
855                 final Intent intent = new Intent("com.android.settings.WIFI_DIALOG")
856                         .putExtra(EXTRA_CHOSEN_WIFI_ENTRY_KEY, mWifiEntry.getKey());
857                 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
858                 mActivityStarter.startActivity(intent, false /* dismissShade */);
859             } else if (status == CONNECT_STATUS_FAILURE_UNKNOWN) {
860                 mInternetDialogController.makeOverlayToast(R.string.wifi_failed_connect_message);
861             } else {
862                 if (DEBUG) {
863                     Log.d(TAG, "connect failure reason=" + status);
864                 }
865             }
866         }
867     }
868 
869     private void scanWifiAccessPoints() {
870         if (mCanConfigWifi) {
871             mAccessPointController.scanForAccessPoints();
872         }
873     }
874 
875     @Override
876     @WorkerThread
877     public void onAccessPointsChanged(List<WifiEntry> accessPoints) {
878         if (!mCanConfigWifi) {
879             return;
880         }
881 
882         WifiEntry connectedEntry = null;
883         List<WifiEntry> wifiEntries = null;
884         final int accessPointsSize = (accessPoints == null) ? 0 : accessPoints.size();
885         final boolean hasMoreWifiEntries = (accessPointsSize > MAX_WIFI_ENTRY_COUNT);
886         if (accessPointsSize > 0) {
887             wifiEntries = new ArrayList<>();
888             final int count = hasMoreWifiEntries ? MAX_WIFI_ENTRY_COUNT : accessPointsSize;
889             mConnectedWifiInternetMonitor.unregisterCallback();
890             for (int i = 0; i < count; i++) {
891                 WifiEntry entry = accessPoints.get(i);
892                 mConnectedWifiInternetMonitor.registerCallbackIfNeed(entry);
893                 if (connectedEntry == null && entry.isDefaultNetwork()
894                         && entry.hasInternetAccess()) {
895                     connectedEntry = entry;
896                 } else {
897                     wifiEntries.add(entry);
898                 }
899             }
900             mHasWifiEntries = true;
901         } else {
902             mHasWifiEntries = false;
903         }
904 
905         mCallback.onAccessPointsChanged(wifiEntries, connectedEntry, hasMoreWifiEntries);
906     }
907 
908     @Override
909     public void onSettingsActivityTriggered(Intent settingsIntent) {
910     }
911 
912     private class InternetTelephonyCallback extends TelephonyCallback implements
913             TelephonyCallback.DataConnectionStateListener,
914             TelephonyCallback.DisplayInfoListener,
915             TelephonyCallback.ServiceStateListener,
916             TelephonyCallback.SignalStrengthsListener,
917             TelephonyCallback.UserMobileDataStateListener {
918 
919         @Override
920         public void onServiceStateChanged(@NonNull ServiceState serviceState) {
921             mCallback.onServiceStateChanged(serviceState);
922         }
923 
924         @Override
925         public void onDataConnectionStateChanged(int state, int networkType) {
926             mCallback.onDataConnectionStateChanged(state, networkType);
927         }
928 
929         @Override
930         public void onSignalStrengthsChanged(@NonNull SignalStrength signalStrength) {
931             mCallback.onSignalStrengthsChanged(signalStrength);
932         }
933 
934         @Override
935         public void onDisplayInfoChanged(@NonNull TelephonyDisplayInfo telephonyDisplayInfo) {
936             mTelephonyDisplayInfo = telephonyDisplayInfo;
937             mCallback.onDisplayInfoChanged(telephonyDisplayInfo);
938         }
939 
940         @Override
941         public void onUserMobileDataStateChanged(boolean enabled) {
942             mCallback.onUserMobileDataStateChanged(enabled);
943         }
944     }
945 
946     private class InternetOnSubscriptionChangedListener
947             extends SubscriptionManager.OnSubscriptionsChangedListener {
948         InternetOnSubscriptionChangedListener() {
949             super();
950         }
951 
952         @Override
953         public void onSubscriptionsChanged() {
954             updateListener();
955         }
956     }
957 
958     private class DataConnectivityListener extends ConnectivityManager.NetworkCallback {
959         @Override
960         @WorkerThread
961         public void onCapabilitiesChanged(@NonNull Network network,
962                 @NonNull NetworkCapabilities capabilities) {
963             mHasEthernet = capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET);
964             if (mCanConfigWifi && (mHasEthernet || capabilities.hasTransport(
965                     NetworkCapabilities.TRANSPORT_WIFI))) {
966                 scanWifiAccessPoints();
967             }
968             // update UI
969             mCallback.onCapabilitiesChanged(network, capabilities);
970         }
971 
972         @Override
973         @WorkerThread
974         public void onLost(@NonNull Network network) {
975             mHasEthernet = false;
976             mCallback.onLost(network);
977         }
978     }
979 
980     /**
981      * Helper class for monitoring the Internet access of the connected WifiEntry.
982      */
983     @VisibleForTesting
984     protected class ConnectedWifiInternetMonitor implements WifiEntry.WifiEntryCallback {
985 
986         private WifiEntry mWifiEntry;
987 
988         public void registerCallbackIfNeed(WifiEntry entry) {
989             if (entry == null || mWifiEntry != null) {
990                 return;
991             }
992             // If the Wi-Fi is not connected yet, or it's the connected Wi-Fi with Internet
993             // access. Then we don't need to listen to the callback to update the Wi-Fi entries.
994             if (entry.getConnectedState() != CONNECTED_STATE_CONNECTED
995                     || (entry.isDefaultNetwork() && entry.hasInternetAccess())) {
996                 return;
997             }
998             mWifiEntry = entry;
999             entry.setListener(this);
1000         }
1001 
1002         public void unregisterCallback() {
1003             if (mWifiEntry == null) {
1004                 return;
1005             }
1006             mWifiEntry.setListener(null);
1007             mWifiEntry = null;
1008         }
1009 
1010         @MainThread
1011         @Override
1012         public void onUpdated() {
1013             if (mWifiEntry == null) {
1014                 return;
1015             }
1016             WifiEntry entry = mWifiEntry;
1017             if (entry.getConnectedState() != CONNECTED_STATE_CONNECTED) {
1018                 unregisterCallback();
1019                 return;
1020             }
1021             if (entry.isDefaultNetwork() && entry.hasInternetAccess()) {
1022                 unregisterCallback();
1023                 // Trigger onAccessPointsChanged() to update the Wi-Fi entries.
1024                 scanWifiAccessPoints();
1025             }
1026         }
1027     }
1028 
1029     /**
1030      * Return {@code true} If the Ethernet exists
1031      */
1032     @MainThread
1033     public boolean hasEthernet() {
1034         return mHasEthernet;
1035     }
1036 
1037     private final BroadcastReceiver mConnectionStateReceiver = new BroadcastReceiver() {
1038         @Override
1039         public void onReceive(Context context, Intent intent) {
1040             final String action = intent.getAction();
1041             if (TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED.equals(action)) {
1042                 if (DEBUG) {
1043                     Log.d(TAG, "ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED");
1044                 }
1045                 mConfig = MobileMappings.Config.readConfig(context);
1046                 updateListener();
1047             } else if (WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION.equals(action)) {
1048                 updateListener();
1049             }
1050         }
1051     };
1052 
1053     private void updateListener() {
1054         int defaultDataSubId = getDefaultDataSubscriptionId();
1055         if (mDefaultDataSubId == getDefaultDataSubscriptionId()) {
1056             if (DEBUG) {
1057                 Log.d(TAG, "DDS: no change");
1058             }
1059             return;
1060         }
1061 
1062         mDefaultDataSubId = defaultDataSubId;
1063         if (DEBUG) {
1064             Log.d(TAG, "DDS: defaultDataSubId:" + mDefaultDataSubId);
1065         }
1066         if (SubscriptionManager.isUsableSubscriptionId(mDefaultDataSubId)) {
1067             mTelephonyManager.unregisterTelephonyCallback(mInternetTelephonyCallback);
1068             mTelephonyManager = mTelephonyManager.createForSubscriptionId(mDefaultDataSubId);
1069             mTelephonyManager.registerTelephonyCallback(mHandler::post,
1070                     mInternetTelephonyCallback);
1071             mCallback.onSubscriptionsChanged(mDefaultDataSubId);
1072         }
1073     }
1074 
1075     public WifiUtils.InternetIconInjector getWifiIconInjector() {
1076         return mWifiIconInjector;
1077     }
1078 
1079     interface InternetDialogCallback {
1080 
1081         void onRefreshCarrierInfo();
1082 
1083         void onSimStateChanged();
1084 
1085         void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities);
1086 
1087         void onLost(@NonNull Network network);
1088 
1089         void onSubscriptionsChanged(int defaultDataSubId);
1090 
1091         void onServiceStateChanged(ServiceState serviceState);
1092 
1093         void onDataConnectionStateChanged(int state, int networkType);
1094 
1095         void onSignalStrengthsChanged(SignalStrength signalStrength);
1096 
1097         void onUserMobileDataStateChanged(boolean enabled);
1098 
1099         void onDisplayInfoChanged(TelephonyDisplayInfo telephonyDisplayInfo);
1100 
1101         void dismissDialog();
1102 
1103         void onAccessPointsChanged(@Nullable List<WifiEntry> wifiEntries,
1104                 @Nullable WifiEntry connectedEntry, boolean hasMoreWifiEntries);
1105     }
1106 
1107     void makeOverlayToast(int stringId) {
1108         final Resources res = mContext.getResources();
1109 
1110         final SystemUIToast systemUIToast = mToastFactory.createToast(mContext,
1111                 res.getString(stringId), mContext.getPackageName(), UserHandle.myUserId(),
1112                 res.getConfiguration().orientation);
1113         if (systemUIToast == null) {
1114             return;
1115         }
1116 
1117         View toastView = systemUIToast.getView();
1118 
1119         final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
1120         params.height = WindowManager.LayoutParams.WRAP_CONTENT;
1121         params.width = WindowManager.LayoutParams.WRAP_CONTENT;
1122         params.format = PixelFormat.TRANSLUCENT;
1123         params.type = WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL;
1124         params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
1125                 | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
1126                 | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
1127         params.y = systemUIToast.getYOffset();
1128 
1129         int absGravity = Gravity.getAbsoluteGravity(systemUIToast.getGravity(),
1130                 res.getConfiguration().getLayoutDirection());
1131         params.gravity = absGravity;
1132         if ((absGravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) {
1133             params.horizontalWeight = TOAST_PARAMS_HORIZONTAL_WEIGHT;
1134         }
1135         if ((absGravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL) {
1136             params.verticalWeight = TOAST_PARAMS_VERTICAL_WEIGHT;
1137         }
1138 
1139         mWindowManager.addView(toastView, params);
1140 
1141         Animator inAnimator = systemUIToast.getInAnimation();
1142         if (inAnimator != null) {
1143             inAnimator.start();
1144         }
1145 
1146         mHandler.postDelayed(new Runnable() {
1147             @Override
1148             public void run() {
1149                 Animator outAnimator = systemUIToast.getOutAnimation();
1150                 if (outAnimator != null) {
1151                     outAnimator.start();
1152                     outAnimator.addListener(new AnimatorListenerAdapter() {
1153                         @Override
1154                         public void onAnimationEnd(Animator animator) {
1155                             mWindowManager.removeViewImmediate(toastView);
1156                         }
1157                     });
1158                 }
1159             }
1160         }, SHORT_DURATION_TIMEOUT);
1161     }
1162 }
1163