• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.settings.wifi.details;
17 
18 import static com.android.settings.network.NetworkProviderSettings.WIFI_DIALOG_ID;
19 import static com.android.settings.network.telephony.MobileNetworkUtils.NO_CELL_DATA_TYPE_ICON;
20 import static com.android.settingslib.Utils.formatPercentage;
21 
22 import android.app.Dialog;
23 import android.app.admin.DevicePolicyManager;
24 import android.app.settings.SettingsEnums;
25 import android.content.Context;
26 import android.graphics.drawable.Drawable;
27 import android.net.ConnectivityManager;
28 import android.net.wifi.WifiManager;
29 import android.net.wifi.sharedconnectivity.app.HotspotNetwork;
30 import android.os.Bundle;
31 import android.os.Handler;
32 import android.os.HandlerThread;
33 import android.os.Looper;
34 import android.os.Process;
35 import android.os.SimpleClock;
36 import android.os.SystemClock;
37 import android.os.UserHandle;
38 import android.os.UserManager;
39 import android.telephony.SignalStrength;
40 import android.view.Menu;
41 import android.view.MenuInflater;
42 import android.view.MenuItem;
43 
44 import androidx.annotation.NonNull;
45 import androidx.annotation.VisibleForTesting;
46 import androidx.preference.Preference;
47 import androidx.preference.PreferenceScreen;
48 
49 import com.android.settings.R;
50 import com.android.settings.Utils;
51 import com.android.settings.dashboard.RestrictedDashboardFragment;
52 import com.android.settings.network.telephony.MobileNetworkUtils;
53 import com.android.settings.overlay.FeatureFactory;
54 import com.android.settings.wifi.WepLessSecureWarningController;
55 import com.android.settings.wifi.WifiConfigUiBase2;
56 import com.android.settings.wifi.WifiDialog2;
57 import com.android.settings.wifi.WifiUtils;
58 import com.android.settings.wifi.details2.AddDevicePreferenceController2;
59 import com.android.settings.wifi.details2.CertificateDetailsPreferenceController;
60 import com.android.settings.wifi.details2.ServerNamePreferenceController;
61 import com.android.settings.wifi.details2.WifiAutoConnectPreferenceController2;
62 import com.android.settings.wifi.details2.WifiDetailPreferenceController2;
63 import com.android.settings.wifi.details2.WifiMeteredPreferenceController2;
64 import com.android.settings.wifi.details2.WifiPrivacyPreferenceController;
65 import com.android.settings.wifi.details2.WifiPrivacyPreferenceController2;
66 import com.android.settings.wifi.details2.WifiSecondSummaryController2;
67 import com.android.settings.wifi.details2.WifiSubscriptionDetailPreferenceController2;
68 import com.android.settings.wifi.repository.SharedConnectivityRepository;
69 import com.android.settingslib.RestrictedLockUtils;
70 import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
71 import com.android.settingslib.RestrictedLockUtilsInternal;
72 import com.android.settingslib.core.AbstractPreferenceController;
73 import com.android.wifitrackerlib.NetworkDetailsTracker;
74 import com.android.wifitrackerlib.WifiEntry;
75 
76 import java.time.Clock;
77 import java.time.ZoneOffset;
78 import java.util.ArrayList;
79 import java.util.List;
80 
81 /**
82  * Detail page for the currently connected wifi network.
83  *
84  * <p>The key of {@link WifiEntry} should be saved to the intent Extras when launching this class
85  * in order to properly render this page.
86  */
87 public class WifiNetworkDetailsFragment extends RestrictedDashboardFragment implements
88         WifiDialog2.WifiDialog2Listener {
89 
90     private static final String TAG = "WifiNetworkDetailsFrg";
91 
92     // Key of a Bundle to save/restore the selected WifiEntry
93     public static final String KEY_CHOSEN_WIFIENTRY_KEY = "key_chosen_wifientry_key";
94 
95     public static final String KEY_HOTSPOT_DEVICE_CATEGORY = "hotspot_device_details_category";
96     public static final String KEY_HOTSPOT_DEVICE_INTERNET_SOURCE =
97             "hotspot_device_details_internet_source";
98     public static final String KEY_HOTSPOT_DEVICE_BATTERY = "hotspot_device_details_battery";
99     public static final String KEY_HOTSPOT_CONNECTION_CATEGORY = "hotspot_connection_category";
100 
101     // Max age of tracked WifiEntries
102     private static final long MAX_SCAN_AGE_MILLIS = 15_000;
103     // Interval between initiating SavedNetworkTracker scans
104     private static final long SCAN_INTERVAL_MILLIS = 10_000;
105 
106     @VisibleForTesting
107     boolean mIsUiRestricted;
108     @VisibleForTesting
109     NetworkDetailsTracker mNetworkDetailsTracker;
110     private HandlerThread mWorkerThread;
111     @VisibleForTesting
112     WifiDetailPreferenceController2 mWifiDetailPreferenceController2;
113     private List<WifiDialog2.WifiDialog2Listener> mWifiDialogListeners = new ArrayList<>();
114     @VisibleForTesting
115     List<AbstractPreferenceController> mControllers;
116     private boolean mIsInstantHotspotFeatureEnabled =
117             SharedConnectivityRepository.isDeviceConfigEnabled();
118     @VisibleForTesting
119     WifiNetworkDetailsViewModel mWifiNetworkDetailsViewModel;
120 
WifiNetworkDetailsFragment()121     public WifiNetworkDetailsFragment() {
122         super(UserManager.DISALLOW_CONFIG_WIFI);
123     }
124 
125     @Override
onAttach(@onNull Context context)126     public void onAttach(@NonNull Context context) {
127         super.onAttach(context);
128         String wifiEntryKey = getArguments().getString(KEY_CHOSEN_WIFIENTRY_KEY);
129         setupNetworksDetailTracker();
130         use(WifiPrivacyPreferenceController.class)
131                 .setWifiEntryKey(wifiEntryKey);
132         use(CertificateDetailsPreferenceController.class)
133                 .setWifiEntry(mNetworkDetailsTracker.getWifiEntry());
134         use(ServerNamePreferenceController.class)
135                 .setWifiEntry(mNetworkDetailsTracker.getWifiEntry());
136         use(WepLessSecureWarningController.class)
137                 .setWifiEntry(mNetworkDetailsTracker.getWifiEntry());
138     }
139 
140     @Override
onCreate(Bundle icicle)141     public void onCreate(Bundle icicle) {
142         super.onCreate(icicle);
143         setIfOnlyAvailableForAdmins(true);
144         mIsUiRestricted = isUiRestricted();
145     }
146 
147     @Override
onStart()148     public void onStart() {
149         super.onStart();
150         if (mIsUiRestricted) {
151             restrictUi();
152         }
153     }
154 
155     @VisibleForTesting
restrictUi()156     void restrictUi() {
157         clearWifiEntryCallback();
158         if (!isUiRestrictedByOnlyAdmin()) {
159             getEmptyTextView().setText(R.string.wifi_empty_list_user_restricted);
160         }
161         getPreferenceScreen().removeAll();
162     }
163 
164     @Override
onDestroy()165     public void onDestroy() {
166         mWorkerThread.quit();
167 
168         super.onDestroy();
169     }
170 
171     @Override
getMetricsCategory()172     public int getMetricsCategory() {
173         return SettingsEnums.WIFI_NETWORK_DETAILS;
174     }
175 
176     @Override
getLogTag()177     protected String getLogTag() {
178         return TAG;
179     }
180 
181     @Override
getPreferenceScreenResId()182     protected int getPreferenceScreenResId() {
183         return R.xml.wifi_network_details_fragment2;
184     }
185 
186     @Override
getDialogMetricsCategory(int dialogId)187     public int getDialogMetricsCategory(int dialogId) {
188         if (dialogId == WIFI_DIALOG_ID) {
189             return SettingsEnums.DIALOG_WIFI_AP_EDIT;
190         }
191         return 0;
192     }
193 
194     @Override
onCreateDialog(int dialogId)195     public Dialog onCreateDialog(int dialogId) {
196         if (getActivity() == null || mWifiDetailPreferenceController2 == null) {
197             return null;
198         }
199 
200         final WifiEntry wifiEntry = mNetworkDetailsTracker.getWifiEntry();
201         return new WifiDialog2(
202                 getActivity(),
203                 this,
204                 wifiEntry,
205                 WifiConfigUiBase2.MODE_MODIFY,
206                 0,
207                 false,
208                 true);
209     }
210 
211     @Override
onCreateOptionsMenu(Menu menu, MenuInflater inflater)212     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
213         if (!mIsUiRestricted && isEditable()) {
214             MenuItem item = menu.add(0, Menu.FIRST, 0, R.string.wifi_modify);
215             item.setIcon(com.android.internal.R.drawable.ic_mode_edit);
216             item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
217         }
218         super.onCreateOptionsMenu(menu, inflater);
219     }
220 
221     @Override
onOptionsItemSelected(MenuItem menuItem)222     public boolean onOptionsItemSelected(MenuItem menuItem) {
223         switch (menuItem.getItemId()) {
224             case Menu.FIRST:
225                 if (!mWifiDetailPreferenceController2.canModifyNetwork()) {
226                     EnforcedAdmin admin = RestrictedLockUtilsInternal.getDeviceOwner(getContext());
227                     if (admin == null) {
228                         final DevicePolicyManager dpm = (DevicePolicyManager)
229                                 getContext().getSystemService(Context.DEVICE_POLICY_SERVICE);
230                         final UserManager um = (UserManager)
231                                 getContext().getSystemService(Context.USER_SERVICE);
232                         final int profileOwnerUserId = Utils.getManagedProfileId(
233                                 um, UserHandle.myUserId());
234                         if (profileOwnerUserId != UserHandle.USER_NULL) {
235                             admin = new EnforcedAdmin(dpm.getProfileOwnerAsUser(profileOwnerUserId),
236                                     null, UserHandle.of(profileOwnerUserId));
237                         }
238                     }
239                     RestrictedLockUtils.sendShowAdminSupportDetailsIntent(getContext(), admin);
240                 } else {
241                     showDialog(WIFI_DIALOG_ID);
242                 }
243                 return true;
244             default:
245                 return super.onOptionsItemSelected(menuItem);
246         }
247     }
248 
249     @Override
createPreferenceControllers(Context context)250     protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
251         mControllers = new ArrayList<>();
252         final ConnectivityManager cm = context.getSystemService(ConnectivityManager.class);
253         setupNetworksDetailTracker();
254         final WifiEntry wifiEntry = mNetworkDetailsTracker.getWifiEntry();
255 
256         if (mIsInstantHotspotFeatureEnabled) {
257             getWifiNetworkDetailsViewModel().setWifiEntry(wifiEntry);
258         }
259 
260         final WifiSecondSummaryController2 wifiSecondSummaryController2 =
261                 new WifiSecondSummaryController2(context);
262         wifiSecondSummaryController2.setWifiEntry(wifiEntry);
263         mControllers.add(wifiSecondSummaryController2);
264 
265         mWifiDetailPreferenceController2 = WifiDetailPreferenceController2.newInstance(
266                 wifiEntry,
267                 cm,
268                 context,
269                 this,
270                 new Handler(Looper.getMainLooper()),  // UI thread.
271                 getSettingsLifecycle(),
272                 context.getSystemService(WifiManager.class),
273                 mMetricsFeatureProvider);
274         mControllers.add(mWifiDetailPreferenceController2);
275 
276         final WifiAutoConnectPreferenceController2 wifiAutoConnectPreferenceController2 =
277                 new WifiAutoConnectPreferenceController2(context);
278         wifiAutoConnectPreferenceController2.setWifiEntry(wifiEntry);
279         mControllers.add(wifiAutoConnectPreferenceController2);
280 
281         final AddDevicePreferenceController2 addDevicePreferenceController2 =
282                 new AddDevicePreferenceController2(context);
283         addDevicePreferenceController2.setWifiEntry(wifiEntry);
284         mControllers.add(addDevicePreferenceController2);
285 
286         final WifiMeteredPreferenceController2 meteredPreferenceController2 =
287                 new WifiMeteredPreferenceController2(context, wifiEntry);
288         mControllers.add(meteredPreferenceController2);
289 
290         final WifiPrivacyPreferenceController2 privacyController2 =
291                 new WifiPrivacyPreferenceController2(context);
292         privacyController2.setWifiEntry(wifiEntry);
293         mControllers.add(privacyController2);
294 
295         final WifiSubscriptionDetailPreferenceController2
296                 wifiSubscriptionDetailPreferenceController2 =
297                 new WifiSubscriptionDetailPreferenceController2(context);
298         wifiSubscriptionDetailPreferenceController2.setWifiEntry(wifiEntry);
299         mControllers.add(wifiSubscriptionDetailPreferenceController2);
300 
301         // Sets callback listener for wifi dialog.
302         mWifiDialogListeners.add(mWifiDetailPreferenceController2);
303 
304         return mControllers;
305     }
306 
307     @Override
onSubmit(@onNull WifiDialog2 dialog)308     public void onSubmit(@NonNull WifiDialog2 dialog) {
309         for (WifiDialog2.WifiDialog2Listener listener : mWifiDialogListeners) {
310             listener.onSubmit(dialog);
311         }
312     }
313 
setupNetworksDetailTracker()314     private void setupNetworksDetailTracker() {
315         if (mNetworkDetailsTracker != null) {
316             return;
317         }
318 
319         final Context context = getContext();
320         mWorkerThread = new HandlerThread(TAG
321                 + "{" + Integer.toHexString(System.identityHashCode(this)) + "}",
322                 Process.THREAD_PRIORITY_BACKGROUND);
323         mWorkerThread.start();
324         final Clock elapsedRealtimeClock = new SimpleClock(ZoneOffset.UTC) {
325             @Override
326             public long millis() {
327                 return SystemClock.elapsedRealtime();
328             }
329         };
330 
331         mNetworkDetailsTracker = FeatureFactory.getFeatureFactory()
332                 .getWifiTrackerLibProvider()
333                 .createNetworkDetailsTracker(
334                         getSettingsLifecycle(),
335                         context,
336                         new Handler(Looper.getMainLooper()),
337                         mWorkerThread.getThreadHandler(),
338                         elapsedRealtimeClock,
339                         MAX_SCAN_AGE_MILLIS,
340                         SCAN_INTERVAL_MILLIS,
341                         getArguments().getString(KEY_CHOSEN_WIFIENTRY_KEY));
342     }
343 
clearWifiEntryCallback()344     private void clearWifiEntryCallback() {
345         if (mNetworkDetailsTracker == null) {
346             return;
347         }
348         final WifiEntry wifiEntry = mNetworkDetailsTracker.getWifiEntry();
349         if (wifiEntry == null) {
350             return;
351         }
352         wifiEntry.setListener(null);
353     }
354 
isEditable()355     private boolean isEditable() {
356         if (mNetworkDetailsTracker == null) {
357             return false;
358         }
359         final WifiEntry wifiEntry = mNetworkDetailsTracker.getWifiEntry();
360         if (wifiEntry == null) {
361             return false;
362         }
363         return wifiEntry.isSaved();
364     }
365 
366     /**
367      * API call for refreshing the preferences in this fragment.
368      */
refreshPreferences()369     public void refreshPreferences() {
370         updatePreferenceStates();
371         displayPreferenceControllers();
372     }
373 
displayPreferenceControllers()374     protected void displayPreferenceControllers() {
375         final PreferenceScreen screen = getPreferenceScreen();
376         for (AbstractPreferenceController controller : mControllers) {
377             // WifiDetailPreferenceController2 gets the callback WifiEntryCallback#onUpdated,
378             // it can control the visibility change by itself.
379             // And WifiDetailPreferenceController2#updatePreference renew mEntityHeaderController
380             // instance which will cause icon reset.
381             if (controller instanceof WifiDetailPreferenceController2) {
382                 continue;
383             }
384             controller.displayPreference(screen);
385         }
386         if (mIsInstantHotspotFeatureEnabled) {
387             getWifiNetworkDetailsViewModel().setWifiEntry(mNetworkDetailsTracker.getWifiEntry());
388         }
389     }
390 
getWifiNetworkDetailsViewModel()391     private WifiNetworkDetailsViewModel getWifiNetworkDetailsViewModel() {
392         if (mWifiNetworkDetailsViewModel == null) {
393             mWifiNetworkDetailsViewModel = FeatureFactory.getFeatureFactory()
394                     .getWifiFeatureProvider().getWifiNetworkDetailsViewModel(this);
395             mWifiNetworkDetailsViewModel.getHotspotNetworkData()
396                     .observe(this, this::onHotspotNetworkChanged);
397         }
398         return mWifiNetworkDetailsViewModel;
399     }
400 
401     @VisibleForTesting
onHotspotNetworkChanged(WifiNetworkDetailsViewModel.HotspotNetworkData data)402     void onHotspotNetworkChanged(WifiNetworkDetailsViewModel.HotspotNetworkData data) {
403         if (mIsUiRestricted) {
404             return;
405         }
406         PreferenceScreen screen = getPreferenceScreen();
407         if (screen == null) {
408             return;
409         }
410         if (data == null) {
411             screen.findPreference(KEY_HOTSPOT_DEVICE_CATEGORY).setVisible(false);
412             screen.findPreference(KEY_HOTSPOT_CONNECTION_CATEGORY).setVisible(false);
413             if (mWifiDetailPreferenceController2 != null) {
414                 mWifiDetailPreferenceController2.setSignalStrengthTitle(R.string.wifi_signal);
415             }
416             return;
417         }
418         screen.findPreference(KEY_HOTSPOT_DEVICE_CATEGORY).setVisible(true);
419         updateInternetSource(data.getNetworkType(), data.getUpstreamConnectionStrength());
420         updateBattery(data.isBatteryCharging(), data.getBatteryPercentage());
421 
422         screen.findPreference(KEY_HOTSPOT_CONNECTION_CATEGORY).setVisible(true);
423         if (mWifiDetailPreferenceController2 != null) {
424             mWifiDetailPreferenceController2
425                     .setSignalStrengthTitle(R.string.hotspot_connection_strength);
426         }
427     }
428 
429     @VisibleForTesting
updateInternetSource(int networkType, int upstreamConnectionStrength)430     void updateInternetSource(int networkType, int upstreamConnectionStrength) {
431         Preference internetSource = getPreferenceScreen()
432                 .findPreference(KEY_HOTSPOT_DEVICE_INTERNET_SOURCE);
433         Drawable drawable;
434         if (networkType == HotspotNetwork.NETWORK_TYPE_WIFI) {
435             internetSource.setSummary(R.string.internet_source_wifi);
436             drawable = getContext().getDrawable(
437                     WifiUtils.getInternetIconResource(upstreamConnectionStrength, false));
438         } else if (networkType == HotspotNetwork.NETWORK_TYPE_CELLULAR) {
439             internetSource.setSummary(R.string.internet_source_mobile_data);
440             drawable = getMobileDataIcon(upstreamConnectionStrength);
441         } else if (networkType == HotspotNetwork.NETWORK_TYPE_ETHERNET) {
442             internetSource.setSummary(R.string.internet_source_ethernet);
443             drawable = getContext().getDrawable(R.drawable.ic_settings_ethernet);
444         } else {
445             internetSource.setSummary(R.string.summary_placeholder);
446             drawable = null;
447         }
448         if (drawable != null) {
449             drawable.setTintList(
450                     Utils.getColorAttr(getContext(), android.R.attr.colorControlNormal));
451         }
452         internetSource.setIcon(drawable);
453     }
454 
455     @VisibleForTesting
getMobileDataIcon(int level)456     Drawable getMobileDataIcon(int level) {
457         return MobileNetworkUtils.getSignalStrengthIcon(getContext(), level,
458                 SignalStrength.NUM_SIGNAL_STRENGTH_BINS, NO_CELL_DATA_TYPE_ICON, false, false);
459     }
460 
461     @VisibleForTesting
updateBattery(boolean isChanging, int percentage)462     void updateBattery(boolean isChanging, int percentage) {
463         Preference battery = getPreferenceScreen().findPreference(KEY_HOTSPOT_DEVICE_BATTERY);
464         battery.setSummary((isChanging)
465                 ? getString(R.string.hotspot_battery_charging_summary, formatPercentage(percentage))
466                 : formatPercentage(percentage));
467     }
468 }
469