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