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.wifi.WifiSettings.WIFI_DIALOG_ID;
19 
20 import android.app.Dialog;
21 import android.app.admin.DevicePolicyManager;
22 import android.app.settings.SettingsEnums;
23 import android.content.Context;
24 import android.net.ConnectivityManager;
25 import android.net.wifi.WifiManager;
26 import android.os.Bundle;
27 import android.os.Handler;
28 import android.os.HandlerThread;
29 import android.os.Looper;
30 import android.os.Process;
31 import android.os.SimpleClock;
32 import android.os.SystemClock;
33 import android.os.UserHandle;
34 import android.os.UserManager;
35 import android.view.Menu;
36 import android.view.MenuInflater;
37 import android.view.MenuItem;
38 
39 import androidx.annotation.VisibleForTesting;
40 import androidx.preference.PreferenceScreen;
41 
42 import com.android.settings.R;
43 import com.android.settings.Utils;
44 import com.android.settings.dashboard.RestrictedDashboardFragment;
45 import com.android.settings.overlay.FeatureFactory;
46 import com.android.settings.wifi.WifiConfigUiBase2;
47 import com.android.settings.wifi.WifiDialog2;
48 import com.android.settings.wifi.details2.AddDevicePreferenceController2;
49 import com.android.settings.wifi.details2.WifiAutoConnectPreferenceController2;
50 import com.android.settings.wifi.details2.WifiDetailPreferenceController2;
51 import com.android.settings.wifi.details2.WifiMeteredPreferenceController2;
52 import com.android.settings.wifi.details2.WifiPrivacyPreferenceController2;
53 import com.android.settings.wifi.details2.WifiSecondSummaryController2;
54 import com.android.settings.wifi.details2.WifiSubscriptionDetailPreferenceController2;
55 import com.android.settingslib.RestrictedLockUtils;
56 import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
57 import com.android.settingslib.RestrictedLockUtilsInternal;
58 import com.android.settingslib.core.AbstractPreferenceController;
59 import com.android.wifitrackerlib.NetworkDetailsTracker;
60 import com.android.wifitrackerlib.WifiEntry;
61 
62 import java.time.Clock;
63 import java.time.ZoneOffset;
64 import java.util.ArrayList;
65 import java.util.List;
66 
67 /**
68  * Detail page for the currently connected wifi network.
69  *
70  * <p>The key of {@link WifiEntry} should be saved to the intent Extras when launching this class
71  * in order to properly render this page.
72  */
73 public class WifiNetworkDetailsFragment extends RestrictedDashboardFragment implements
74         WifiDialog2.WifiDialog2Listener {
75 
76     private static final String TAG = "WifiNetworkDetailsFrg";
77 
78     // Key of a Bundle to save/restore the selected WifiEntry
79     public static final String KEY_CHOSEN_WIFIENTRY_KEY = "key_chosen_wifientry_key";
80 
81     // Max age of tracked WifiEntries
82     private static final long MAX_SCAN_AGE_MILLIS = 15_000;
83     // Interval between initiating SavedNetworkTracker scans
84     private static final long SCAN_INTERVAL_MILLIS = 10_000;
85 
86     @VisibleForTesting
87     boolean mIsUiRestricted;
88     @VisibleForTesting
89     NetworkDetailsTracker mNetworkDetailsTracker;
90     private HandlerThread mWorkerThread;
91     private WifiDetailPreferenceController2 mWifiDetailPreferenceController2;
92     private List<WifiDialog2.WifiDialog2Listener> mWifiDialogListeners = new ArrayList<>();
93     @VisibleForTesting
94     List<AbstractPreferenceController> mControllers;
95 
WifiNetworkDetailsFragment()96     public WifiNetworkDetailsFragment() {
97         super(UserManager.DISALLOW_CONFIG_WIFI);
98     }
99 
100     @Override
onCreate(Bundle icicle)101     public void onCreate(Bundle icicle) {
102         super.onCreate(icicle);
103         setIfOnlyAvailableForAdmins(true);
104         mIsUiRestricted = isUiRestricted();
105     }
106 
107     @Override
onStart()108     public void onStart() {
109         super.onStart();
110         if (mIsUiRestricted) {
111             restrictUi();
112         }
113     }
114 
115     @VisibleForTesting
restrictUi()116     void restrictUi() {
117         clearWifiEntryCallback();
118         if (!isUiRestrictedByOnlyAdmin()) {
119             getEmptyTextView().setText(R.string.wifi_empty_list_user_restricted);
120         }
121         getPreferenceScreen().removeAll();
122     }
123 
124     @Override
onDestroy()125     public void onDestroy() {
126         mWorkerThread.quit();
127 
128         super.onDestroy();
129     }
130 
131     @Override
getMetricsCategory()132     public int getMetricsCategory() {
133         return SettingsEnums.WIFI_NETWORK_DETAILS;
134     }
135 
136     @Override
getLogTag()137     protected String getLogTag() {
138         return TAG;
139     }
140 
141     @Override
getPreferenceScreenResId()142     protected int getPreferenceScreenResId() {
143         return R.xml.wifi_network_details_fragment2;
144     }
145 
146     @Override
getDialogMetricsCategory(int dialogId)147     public int getDialogMetricsCategory(int dialogId) {
148         if (dialogId == WIFI_DIALOG_ID) {
149             return SettingsEnums.DIALOG_WIFI_AP_EDIT;
150         }
151         return 0;
152     }
153 
154     @Override
onCreateDialog(int dialogId)155     public Dialog onCreateDialog(int dialogId) {
156         if (getActivity() == null || mWifiDetailPreferenceController2 == null) {
157             return null;
158         }
159 
160         final WifiEntry wifiEntry = mNetworkDetailsTracker.getWifiEntry();
161         return WifiDialog2.createModal(getActivity(), this, wifiEntry,
162                 WifiConfigUiBase2.MODE_MODIFY);
163     }
164 
165     @Override
onCreateOptionsMenu(Menu menu, MenuInflater inflater)166     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
167         if (!mIsUiRestricted && isEditable()) {
168             MenuItem item = menu.add(0, Menu.FIRST, 0, R.string.wifi_modify);
169             item.setIcon(com.android.internal.R.drawable.ic_mode_edit);
170             item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
171         }
172         super.onCreateOptionsMenu(menu, inflater);
173     }
174 
175     @Override
onOptionsItemSelected(MenuItem menuItem)176     public boolean onOptionsItemSelected(MenuItem menuItem) {
177         switch (menuItem.getItemId()) {
178             case Menu.FIRST:
179                 if (!mWifiDetailPreferenceController2.canModifyNetwork()) {
180                     EnforcedAdmin admin = RestrictedLockUtilsInternal.getDeviceOwner(getContext());
181                     if (admin == null) {
182                         final DevicePolicyManager dpm = (DevicePolicyManager)
183                                 getContext().getSystemService(Context.DEVICE_POLICY_SERVICE);
184                         final UserManager um = (UserManager)
185                                 getContext().getSystemService(Context.USER_SERVICE);
186                         final int profileOwnerUserId = Utils.getManagedProfileId(
187                                 um, UserHandle.myUserId());
188                         if (profileOwnerUserId != UserHandle.USER_NULL) {
189                             admin = new EnforcedAdmin(dpm.getProfileOwnerAsUser(profileOwnerUserId),
190                                     null, UserHandle.of(profileOwnerUserId));
191                         }
192                     }
193                     RestrictedLockUtils.sendShowAdminSupportDetailsIntent(getContext(), admin);
194                 } else {
195                     showDialog(WIFI_DIALOG_ID);
196                 }
197                 return true;
198             default:
199                 return super.onOptionsItemSelected(menuItem);
200         }
201     }
202 
203     @Override
createPreferenceControllers(Context context)204     protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
205         mControllers = new ArrayList<>();
206         final ConnectivityManager cm = context.getSystemService(ConnectivityManager.class);
207         setupNetworksDetailTracker();
208         final WifiEntry wifiEntry = mNetworkDetailsTracker.getWifiEntry();
209 
210         final WifiSecondSummaryController2 wifiSecondSummaryController2 =
211                 new WifiSecondSummaryController2(context);
212         wifiSecondSummaryController2.setWifiEntry(wifiEntry);
213         mControllers.add(wifiSecondSummaryController2);
214 
215         mWifiDetailPreferenceController2 = WifiDetailPreferenceController2.newInstance(
216                 wifiEntry,
217                 cm,
218                 context,
219                 this,
220                 new Handler(Looper.getMainLooper()),  // UI thread.
221                 getSettingsLifecycle(),
222                 context.getSystemService(WifiManager.class),
223                 mMetricsFeatureProvider);
224         mControllers.add(mWifiDetailPreferenceController2);
225 
226         final WifiAutoConnectPreferenceController2 wifiAutoConnectPreferenceController2 =
227                 new WifiAutoConnectPreferenceController2(context);
228         wifiAutoConnectPreferenceController2.setWifiEntry(wifiEntry);
229         mControllers.add(wifiAutoConnectPreferenceController2);
230 
231         final AddDevicePreferenceController2 addDevicePreferenceController2 =
232                 new AddDevicePreferenceController2(context);
233         addDevicePreferenceController2.setWifiEntry(wifiEntry);
234         mControllers.add(addDevicePreferenceController2);
235 
236         final WifiMeteredPreferenceController2 meteredPreferenceController2 =
237                 new WifiMeteredPreferenceController2(context, wifiEntry);
238         mControllers.add(meteredPreferenceController2);
239 
240         final WifiPrivacyPreferenceController2 privacyController2 =
241                 new WifiPrivacyPreferenceController2(context);
242         privacyController2.setWifiEntry(wifiEntry);
243         mControllers.add(privacyController2);
244 
245         final WifiSubscriptionDetailPreferenceController2
246                 wifiSubscriptionDetailPreferenceController2 =
247                 new WifiSubscriptionDetailPreferenceController2(context);
248         wifiSubscriptionDetailPreferenceController2.setWifiEntry(wifiEntry);
249         mControllers.add(wifiSubscriptionDetailPreferenceController2);
250 
251         // Sets callback listener for wifi dialog.
252         mWifiDialogListeners.add(mWifiDetailPreferenceController2);
253         mWifiDialogListeners.add(privacyController2);
254         mWifiDialogListeners.add(meteredPreferenceController2);
255 
256         return mControllers;
257     }
258 
259     @Override
onSubmit(WifiDialog2 dialog)260     public void onSubmit(WifiDialog2 dialog) {
261         for (WifiDialog2.WifiDialog2Listener listener : mWifiDialogListeners) {
262             listener.onSubmit(dialog);
263         }
264     }
265 
setupNetworksDetailTracker()266     private void setupNetworksDetailTracker() {
267         if (mNetworkDetailsTracker != null) {
268             return;
269         }
270 
271         final Context context = getContext();
272         mWorkerThread = new HandlerThread(TAG
273                 + "{" + Integer.toHexString(System.identityHashCode(this)) + "}",
274                 Process.THREAD_PRIORITY_BACKGROUND);
275         mWorkerThread.start();
276         final Clock elapsedRealtimeClock = new SimpleClock(ZoneOffset.UTC) {
277             @Override
278             public long millis() {
279                 return SystemClock.elapsedRealtime();
280             }
281         };
282 
283         mNetworkDetailsTracker = FeatureFactory.getFactory(context)
284                 .getWifiTrackerLibProvider()
285                 .createNetworkDetailsTracker(
286                         getSettingsLifecycle(),
287                         context,
288                         new Handler(Looper.getMainLooper()),
289                         mWorkerThread.getThreadHandler(),
290                         elapsedRealtimeClock,
291                         MAX_SCAN_AGE_MILLIS,
292                         SCAN_INTERVAL_MILLIS,
293                         getArguments().getString(KEY_CHOSEN_WIFIENTRY_KEY));
294     }
295 
clearWifiEntryCallback()296     private void clearWifiEntryCallback() {
297         if (mNetworkDetailsTracker == null) {
298             return;
299         }
300         final WifiEntry wifiEntry = mNetworkDetailsTracker.getWifiEntry();
301         if (wifiEntry == null) {
302             return;
303         }
304         wifiEntry.setListener(null);
305     }
306 
isEditable()307     private boolean isEditable() {
308         if (mNetworkDetailsTracker == null) {
309             return false;
310         }
311         final WifiEntry wifiEntry = mNetworkDetailsTracker.getWifiEntry();
312         if (wifiEntry == null) {
313             return false;
314         }
315         return wifiEntry.isSaved();
316     }
317 
318     /**
319      * API call for refreshing the preferences in this fragment.
320      */
refreshPreferences()321     public void refreshPreferences() {
322         updatePreferenceStates();
323         displayPreferenceControllers();
324     }
325 
displayPreferenceControllers()326     protected void displayPreferenceControllers() {
327         final PreferenceScreen screen = getPreferenceScreen();
328         for (AbstractPreferenceController controller : mControllers) {
329             // WifiDetailPreferenceController2 gets the callback WifiEntryCallback#onUpdated,
330             // it can control the visibility change by itself.
331             // And WifiDetailPreferenceController2#updatePreference renew mEntityHeaderController
332             // instance which will cause icon reset.
333             if (controller instanceof WifiDetailPreferenceController2) {
334                 continue;
335             }
336             controller.displayPreference(screen);
337         }
338     }
339 }
340