1 /* 2 * Copyright (C) 2018 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.settings.network.telephony; 18 19 import android.app.Activity; 20 import android.app.settings.SettingsEnums; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.os.Bundle; 24 import android.os.Handler; 25 import android.os.Message; 26 import android.os.PersistableBundle; 27 import android.provider.Settings; 28 import android.telephony.AccessNetworkConstants; 29 import android.telephony.CarrierConfigManager; 30 import android.telephony.CellIdentity; 31 import android.telephony.CellInfo; 32 import android.telephony.NetworkRegistrationInfo; 33 import android.telephony.ServiceState; 34 import android.telephony.SignalStrength; 35 import android.telephony.SubscriptionManager; 36 import android.telephony.TelephonyManager; 37 import android.util.Log; 38 import android.view.View; 39 40 import androidx.annotation.Keep; 41 import androidx.annotation.VisibleForTesting; 42 import androidx.preference.Preference; 43 import androidx.preference.PreferenceCategory; 44 45 import com.android.internal.telephony.OperatorInfo; 46 import com.android.settings.R; 47 import com.android.settings.dashboard.DashboardFragment; 48 import com.android.settings.overlay.FeatureFactory; 49 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider; 50 import com.android.settingslib.utils.ThreadUtils; 51 52 import java.util.ArrayList; 53 import java.util.Arrays; 54 import java.util.List; 55 import java.util.Optional; 56 import java.util.concurrent.ExecutorService; 57 import java.util.concurrent.Executors; 58 59 /** 60 * "Choose network" settings UI for the Settings app. 61 */ 62 @Keep 63 public class NetworkSelectSettings extends DashboardFragment { 64 65 private static final String TAG = "NetworkSelectSettings"; 66 67 private static final int EVENT_SET_NETWORK_SELECTION_MANUALLY_DONE = 1; 68 private static final int EVENT_NETWORK_SCAN_RESULTS = 2; 69 private static final int EVENT_NETWORK_SCAN_ERROR = 3; 70 private static final int EVENT_NETWORK_SCAN_COMPLETED = 4; 71 72 private static final String PREF_KEY_NETWORK_OPERATORS = "network_operators_preference"; 73 private static final int MIN_NUMBER_OF_SCAN_REQUIRED = 2; 74 75 private PreferenceCategory mPreferenceCategory; 76 @VisibleForTesting 77 NetworkOperatorPreference mSelectedPreference; 78 private View mProgressHeader; 79 private Preference mStatusMessagePreference; 80 @VisibleForTesting 81 List<CellInfo> mCellInfoList; 82 private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; 83 private TelephonyManager mTelephonyManager; 84 private List<String> mForbiddenPlmns; 85 private boolean mShow4GForLTE = false; 86 private NetworkScanHelper mNetworkScanHelper; 87 private final ExecutorService mNetworkScanExecutor = Executors.newFixedThreadPool(1); 88 private MetricsFeatureProvider mMetricsFeatureProvider; 89 private boolean mUseNewApi; 90 private long mRequestIdManualNetworkSelect; 91 private long mRequestIdManualNetworkScan; 92 private long mWaitingForNumberOfScanResults; 93 @VisibleForTesting 94 boolean mIsAggregationEnabled = false; 95 96 @Override onCreate(Bundle icicle)97 public void onCreate(Bundle icicle) { 98 super.onCreate(icicle); 99 onCreateInitialization(); 100 } 101 102 @Keep 103 @VisibleForTesting onCreateInitialization()104 protected void onCreateInitialization() { 105 mUseNewApi = enableNewAutoSelectNetworkUI(getContext()); 106 mSubId = getSubId(); 107 108 mPreferenceCategory = getPreferenceCategory(PREF_KEY_NETWORK_OPERATORS); 109 mStatusMessagePreference = new Preference(getContext()); 110 mStatusMessagePreference.setSelectable(false); 111 mSelectedPreference = null; 112 mTelephonyManager = getTelephonyManager(getContext(), mSubId); 113 mNetworkScanHelper = new NetworkScanHelper( 114 mTelephonyManager, mCallback, mNetworkScanExecutor); 115 PersistableBundle bundle = getCarrierConfigManager(getContext()) 116 .getConfigForSubId(mSubId); 117 if (bundle != null) { 118 mShow4GForLTE = bundle.getBoolean( 119 CarrierConfigManager.KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL); 120 } 121 122 mMetricsFeatureProvider = getMetricsFeatureProvider(getContext()); 123 mIsAggregationEnabled = enableAggregation(getContext()); 124 Log.d(TAG, "init: mUseNewApi:" + mUseNewApi 125 + " ,mIsAggregationEnabled:" + mIsAggregationEnabled + " ,mSubId:" + mSubId); 126 } 127 128 @Keep 129 @VisibleForTesting enableNewAutoSelectNetworkUI(Context context)130 protected boolean enableNewAutoSelectNetworkUI(Context context) { 131 return context.getResources().getBoolean( 132 com.android.internal.R.bool.config_enableNewAutoSelectNetworkUI); 133 } 134 135 @Keep 136 @VisibleForTesting enableAggregation(Context context)137 protected boolean enableAggregation(Context context) { 138 return context.getResources().getBoolean( 139 R.bool.config_network_selection_list_aggregation_enabled); 140 } 141 142 @Keep 143 @VisibleForTesting getPreferenceCategory(String preferenceKey)144 protected PreferenceCategory getPreferenceCategory(String preferenceKey) { 145 return findPreference(preferenceKey); 146 } 147 148 @Keep 149 @VisibleForTesting getTelephonyManager(Context context, int subscriptionId)150 protected TelephonyManager getTelephonyManager(Context context, int subscriptionId) { 151 return context.getSystemService(TelephonyManager.class) 152 .createForSubscriptionId(subscriptionId); 153 } 154 155 @Keep 156 @VisibleForTesting getCarrierConfigManager(Context context)157 protected CarrierConfigManager getCarrierConfigManager(Context context) { 158 return context.getSystemService(CarrierConfigManager.class); 159 } 160 161 @Keep 162 @VisibleForTesting getMetricsFeatureProvider(Context context)163 protected MetricsFeatureProvider getMetricsFeatureProvider(Context context) { 164 return FeatureFactory.getFactory(context).getMetricsFeatureProvider(); 165 } 166 167 @Keep 168 @VisibleForTesting isPreferenceScreenEnabled()169 protected boolean isPreferenceScreenEnabled() { 170 return getPreferenceScreen().isEnabled(); 171 } 172 173 @Keep 174 @VisibleForTesting enablePreferenceScreen(boolean enable)175 protected void enablePreferenceScreen(boolean enable) { 176 getPreferenceScreen().setEnabled(enable); 177 } 178 179 @Keep 180 @VisibleForTesting getSubId()181 protected int getSubId() { 182 int subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; 183 Intent intent = getActivity().getIntent(); 184 if (intent != null) { 185 subId = intent.getIntExtra(Settings.EXTRA_SUB_ID, 186 SubscriptionManager.INVALID_SUBSCRIPTION_ID); 187 } 188 return subId; 189 } 190 191 @Override onViewCreated(View view, Bundle savedInstanceState)192 public void onViewCreated(View view, Bundle savedInstanceState) { 193 super.onViewCreated(view, savedInstanceState); 194 195 final Activity activity = getActivity(); 196 if (activity != null) { 197 mProgressHeader = setPinnedHeaderView(R.layout.progress_header) 198 .findViewById(R.id.progress_bar_animation); 199 setProgressBarVisible(false); 200 } 201 forceUpdateConnectedPreferenceCategory(); 202 } 203 204 @Override onStart()205 public void onStart() { 206 super.onStart(); 207 208 updateForbiddenPlmns(); 209 if (isProgressBarVisible()) { 210 return; 211 } 212 if (mWaitingForNumberOfScanResults <= 0) { 213 startNetworkQuery(); 214 } 215 } 216 217 /** 218 * Update forbidden PLMNs from the USIM App 219 */ 220 @Keep 221 @VisibleForTesting updateForbiddenPlmns()222 protected void updateForbiddenPlmns() { 223 final String[] forbiddenPlmns = mTelephonyManager.getForbiddenPlmns(); 224 mForbiddenPlmns = forbiddenPlmns != null 225 ? Arrays.asList(forbiddenPlmns) 226 : new ArrayList<>(); 227 } 228 229 @Override onStop()230 public void onStop() { 231 if (mWaitingForNumberOfScanResults <= 0) { 232 stopNetworkQuery(); 233 } 234 super.onStop(); 235 } 236 237 @Override onPreferenceTreeClick(Preference preference)238 public boolean onPreferenceTreeClick(Preference preference) { 239 if (preference == mSelectedPreference) { 240 Log.d(TAG, "onPreferenceTreeClick: preference is mSelectedPreference. Do nothing."); 241 return true; 242 } 243 if (!(preference instanceof NetworkOperatorPreference)) { 244 Log.d(TAG, "onPreferenceTreeClick: preference is not the NetworkOperatorPreference."); 245 return false; 246 } 247 248 stopNetworkQuery(); 249 250 // Refresh the last selected item in case users reselect network. 251 clearPreferenceSummary(); 252 if (mSelectedPreference != null) { 253 // Set summary as "Disconnected" to the previously connected network 254 mSelectedPreference.setSummary(R.string.network_disconnected); 255 } 256 257 mSelectedPreference = (NetworkOperatorPreference) preference; 258 mSelectedPreference.setSummary(R.string.network_connecting); 259 260 mMetricsFeatureProvider.action(getContext(), 261 SettingsEnums.ACTION_MOBILE_NETWORK_MANUAL_SELECT_NETWORK); 262 263 setProgressBarVisible(true); 264 // Disable the screen until network is manually set 265 enablePreferenceScreen(false); 266 267 mRequestIdManualNetworkSelect = getNewRequestId(); 268 mWaitingForNumberOfScanResults = MIN_NUMBER_OF_SCAN_REQUIRED; 269 final OperatorInfo operator = mSelectedPreference.getOperatorInfo(); 270 ThreadUtils.postOnBackgroundThread(() -> { 271 final Message msg = mHandler.obtainMessage( 272 EVENT_SET_NETWORK_SELECTION_MANUALLY_DONE); 273 msg.obj = mTelephonyManager.setNetworkSelectionModeManual( 274 operator, true /* persistSelection */); 275 msg.sendToTarget(); 276 }); 277 278 return true; 279 } 280 281 @Override getPreferenceScreenResId()282 protected int getPreferenceScreenResId() { 283 return R.xml.choose_network; 284 } 285 286 @Override getLogTag()287 protected String getLogTag() { 288 return TAG; 289 } 290 291 @Override getMetricsCategory()292 public int getMetricsCategory() { 293 return SettingsEnums.MOBILE_NETWORK_SELECT; 294 } 295 296 private final Handler mHandler = new Handler() { 297 @Override 298 public void handleMessage(Message msg) { 299 switch (msg.what) { 300 case EVENT_SET_NETWORK_SELECTION_MANUALLY_DONE: 301 final boolean isSucceed = (boolean) msg.obj; 302 stopNetworkQuery(); 303 setProgressBarVisible(false); 304 enablePreferenceScreen(true); 305 306 if (mSelectedPreference != null) { 307 mSelectedPreference.setSummary(isSucceed 308 ? R.string.network_connected 309 : R.string.network_could_not_connect); 310 } else { 311 Log.e(TAG, "No preference to update!"); 312 } 313 break; 314 case EVENT_NETWORK_SCAN_RESULTS: 315 scanResultHandler((List<CellInfo>) msg.obj); 316 break; 317 318 case EVENT_NETWORK_SCAN_ERROR: 319 stopNetworkQuery(); 320 Log.i(TAG, "Network scan failure " + msg.arg1 + ":" 321 + " scan request 0x" + Long.toHexString(mRequestIdManualNetworkScan) 322 + ", waiting for scan results = " + mWaitingForNumberOfScanResults 323 + ", select request 0x" 324 + Long.toHexString(mRequestIdManualNetworkSelect)); 325 if (mRequestIdManualNetworkScan < mRequestIdManualNetworkSelect) { 326 break; 327 } 328 if (!isPreferenceScreenEnabled()) { 329 clearPreferenceSummary(); 330 enablePreferenceScreen(true); 331 } else { 332 addMessagePreference(R.string.network_query_error); 333 } 334 break; 335 336 case EVENT_NETWORK_SCAN_COMPLETED: 337 stopNetworkQuery(); 338 Log.d(TAG, "Network scan complete:" 339 + " scan request 0x" + Long.toHexString(mRequestIdManualNetworkScan) 340 + ", waiting for scan results = " + mWaitingForNumberOfScanResults 341 + ", select request 0x" 342 + Long.toHexString(mRequestIdManualNetworkSelect)); 343 if (mRequestIdManualNetworkScan < mRequestIdManualNetworkSelect) { 344 break; 345 } 346 if (!isPreferenceScreenEnabled()) { 347 clearPreferenceSummary(); 348 enablePreferenceScreen(true); 349 } else if (mCellInfoList == null) { 350 // In case the scan timeout before getting any results 351 addMessagePreference(R.string.empty_networks_list); 352 } 353 break; 354 } 355 return; 356 } 357 }; 358 359 @VisibleForTesting doAggregation(List<CellInfo> cellInfoListInput)360 List<CellInfo> doAggregation(List<CellInfo> cellInfoListInput) { 361 if (!mIsAggregationEnabled) { 362 Log.d(TAG, "no aggregation"); 363 return new ArrayList<>(cellInfoListInput); 364 } 365 ArrayList<CellInfo> aggregatedList = new ArrayList<>(); 366 for (CellInfo cellInfo : cellInfoListInput) { 367 String plmn = CellInfoUtil.getNetworkTitle(cellInfo.getCellIdentity(), 368 CellInfoUtil.getCellIdentityMccMnc(cellInfo.getCellIdentity())); 369 Class className = cellInfo.getClass(); 370 371 Optional<CellInfo> itemInTheList = aggregatedList.stream().filter( 372 item -> { 373 String itemPlmn = CellInfoUtil.getNetworkTitle(item.getCellIdentity(), 374 CellInfoUtil.getCellIdentityMccMnc(item.getCellIdentity())); 375 return itemPlmn.equals(plmn) && item.getClass().equals(className); 376 }) 377 .findFirst(); 378 if (itemInTheList.isPresent()) { 379 if (cellInfo.isRegistered() && !itemInTheList.get().isRegistered()) { 380 // Adding the registered cellinfo item into list. If there are two registered 381 // cellinfo items, then select first one from source list. 382 aggregatedList.set(aggregatedList.indexOf(itemInTheList.get()), cellInfo); 383 } 384 continue; 385 } 386 aggregatedList.add(cellInfo); 387 } 388 return aggregatedList; 389 } 390 391 private final NetworkScanHelper.NetworkScanCallback mCallback = 392 new NetworkScanHelper.NetworkScanCallback() { 393 public void onResults(List<CellInfo> results) { 394 final Message msg = mHandler.obtainMessage(EVENT_NETWORK_SCAN_RESULTS, results); 395 msg.sendToTarget(); 396 } 397 398 public void onComplete() { 399 final Message msg = mHandler.obtainMessage(EVENT_NETWORK_SCAN_COMPLETED); 400 msg.sendToTarget(); 401 } 402 403 public void onError(int error) { 404 final Message msg = mHandler.obtainMessage(EVENT_NETWORK_SCAN_ERROR, error, 405 0 /* arg2 */); 406 msg.sendToTarget(); 407 } 408 }; 409 410 @Keep 411 @VisibleForTesting scanResultHandler(List<CellInfo> results)412 protected void scanResultHandler(List<CellInfo> results) { 413 if (mRequestIdManualNetworkScan < mRequestIdManualNetworkSelect) { 414 Log.d(TAG, "CellInfoList (drop): " 415 + CellInfoUtil.cellInfoListToString(new ArrayList<>(results))); 416 return; 417 } 418 mWaitingForNumberOfScanResults--; 419 if ((mWaitingForNumberOfScanResults <= 0) && (!isResumed())) { 420 stopNetworkQuery(); 421 } 422 423 mCellInfoList = doAggregation(results); 424 Log.d(TAG, "CellInfoList: " + CellInfoUtil.cellInfoListToString(mCellInfoList)); 425 if (mCellInfoList != null && mCellInfoList.size() != 0) { 426 final NetworkOperatorPreference connectedPref = 427 updateAllPreferenceCategory(); 428 if (connectedPref != null) { 429 // update selected preference instance into connected preference 430 if (mSelectedPreference != null) { 431 mSelectedPreference = connectedPref; 432 } 433 } else if (!isPreferenceScreenEnabled()) { 434 if (connectedPref == null) { 435 mSelectedPreference.setSummary(R.string.network_connecting); 436 } 437 } 438 enablePreferenceScreen(true); 439 } else if (isPreferenceScreenEnabled()) { 440 addMessagePreference(R.string.empty_networks_list); 441 // keep showing progress bar, it will be stopped when error or completed 442 setProgressBarVisible(true); 443 } 444 } 445 446 @Keep 447 @VisibleForTesting createNetworkOperatorPreference(CellInfo cellInfo)448 protected NetworkOperatorPreference createNetworkOperatorPreference(CellInfo cellInfo) { 449 return new NetworkOperatorPreference(getPrefContext(), 450 cellInfo, mForbiddenPlmns, mShow4GForLTE); 451 } 452 453 /** 454 * Update the content of network operators list. 455 * 456 * @return preference which shows connected 457 */ updateAllPreferenceCategory()458 private NetworkOperatorPreference updateAllPreferenceCategory() { 459 int numberOfPreferences = mPreferenceCategory.getPreferenceCount(); 460 461 // remove unused preferences 462 while (numberOfPreferences > mCellInfoList.size()) { 463 numberOfPreferences--; 464 mPreferenceCategory.removePreference( 465 mPreferenceCategory.getPreference(numberOfPreferences)); 466 } 467 468 // update the content of preference 469 NetworkOperatorPreference connectedPref = null; 470 for (int index = 0; index < mCellInfoList.size(); index++) { 471 final CellInfo cellInfo = mCellInfoList.get(index); 472 473 NetworkOperatorPreference pref = null; 474 if (index < numberOfPreferences) { 475 final Preference rawPref = mPreferenceCategory.getPreference(index); 476 if (rawPref instanceof NetworkOperatorPreference) { 477 // replace existing preference 478 pref = (NetworkOperatorPreference) rawPref; 479 pref.updateCell(cellInfo); 480 } else { 481 mPreferenceCategory.removePreference(rawPref); 482 } 483 } 484 if (pref == null) { 485 // add new preference 486 pref = createNetworkOperatorPreference(cellInfo); 487 pref.setOrder(index); 488 mPreferenceCategory.addPreference(pref); 489 } 490 pref.setKey(pref.getOperatorName()); 491 492 if (mCellInfoList.get(index).isRegistered()) { 493 pref.setSummary(R.string.network_connected); 494 connectedPref = pref; 495 } else { 496 pref.setSummary(null); 497 } 498 } 499 500 // update selected preference instance by index 501 for (int index = 0; index < mCellInfoList.size(); index++) { 502 final CellInfo cellInfo = mCellInfoList.get(index); 503 504 if ((mSelectedPreference != null) && mSelectedPreference.isSameCell(cellInfo)) { 505 mSelectedPreference = (NetworkOperatorPreference) 506 (mPreferenceCategory.getPreference(index)); 507 } 508 } 509 510 return connectedPref; 511 } 512 513 /** 514 * Config the network operator list when the page was created. When user get 515 * into this page, the device might or might not have data connection. 516 * - If the device has data: 517 * 1. use {@code ServiceState#getNetworkRegistrationInfoList()} to get the currently 518 * registered cellIdentity, wrap it into a CellInfo; 519 * 2. set the signal strength level as strong; 520 * 3. get the title of the previously connected network operator, since the CellIdentity 521 * got from step 1 only has PLMN. 522 * - If the device has no data, we will remove the connected network operators list from the 523 * screen. 524 */ forceUpdateConnectedPreferenceCategory()525 private void forceUpdateConnectedPreferenceCategory() { 526 if (mTelephonyManager.getDataState() == mTelephonyManager.DATA_CONNECTED) { 527 // Try to get the network registration states 528 final ServiceState ss = mTelephonyManager.getServiceState(); 529 if (ss == null) { 530 return; 531 } 532 final List<NetworkRegistrationInfo> networkList = 533 ss.getNetworkRegistrationInfoListForTransportType( 534 AccessNetworkConstants.TRANSPORT_TYPE_WWAN); 535 if (networkList == null || networkList.size() == 0) { 536 return; 537 } 538 // Due to the aggregation of cell between carriers, it's possible to get CellIdentity 539 // containing forbidden PLMN. 540 // Getting current network from ServiceState is no longer a good idea. 541 // Add an additional rule to avoid from showing forbidden PLMN to the user. 542 if (mForbiddenPlmns == null) { 543 updateForbiddenPlmns(); 544 } 545 for (NetworkRegistrationInfo regInfo : networkList) { 546 final CellIdentity cellIdentity = regInfo.getCellIdentity(); 547 if (cellIdentity == null) { 548 continue; 549 } 550 final NetworkOperatorPreference pref = new NetworkOperatorPreference( 551 getPrefContext(), cellIdentity, mForbiddenPlmns, mShow4GForLTE); 552 if (pref.isForbiddenNetwork()) { 553 continue; 554 } 555 pref.setSummary(R.string.network_connected); 556 // Update the signal strength icon, since the default signalStrength value 557 // would be zero 558 // (it would be quite confusing why the connected network has no signal) 559 pref.setIcon(SignalStrength.NUM_SIGNAL_STRENGTH_BINS - 1); 560 mPreferenceCategory.addPreference(pref); 561 break; 562 } 563 } 564 } 565 566 /** 567 * Clear all of the preference summary 568 */ clearPreferenceSummary()569 private void clearPreferenceSummary() { 570 int idxPreference = mPreferenceCategory.getPreferenceCount(); 571 while (idxPreference > 0) { 572 idxPreference--; 573 final Preference networkOperator = mPreferenceCategory.getPreference(idxPreference); 574 networkOperator.setSummary(null); 575 } 576 } 577 getNewRequestId()578 private long getNewRequestId() { 579 return Math.max(mRequestIdManualNetworkSelect, 580 mRequestIdManualNetworkScan) + 1; 581 } 582 isProgressBarVisible()583 private boolean isProgressBarVisible() { 584 if (mProgressHeader == null) { 585 return false; 586 } 587 return (mProgressHeader.getVisibility() == View.VISIBLE); 588 } 589 setProgressBarVisible(boolean visible)590 protected void setProgressBarVisible(boolean visible) { 591 if (mProgressHeader != null) { 592 mProgressHeader.setVisibility(visible ? View.VISIBLE : View.GONE); 593 } 594 } 595 addMessagePreference(int messageId)596 private void addMessagePreference(int messageId) { 597 setProgressBarVisible(false); 598 mStatusMessagePreference.setTitle(messageId); 599 mPreferenceCategory.removeAll(); 600 mPreferenceCategory.addPreference(mStatusMessagePreference); 601 } 602 startNetworkQuery()603 private void startNetworkQuery() { 604 setProgressBarVisible(true); 605 if (mNetworkScanHelper != null) { 606 mRequestIdManualNetworkScan = getNewRequestId(); 607 mWaitingForNumberOfScanResults = MIN_NUMBER_OF_SCAN_REQUIRED; 608 mNetworkScanHelper.startNetworkScan( 609 mUseNewApi 610 ? NetworkScanHelper.NETWORK_SCAN_TYPE_INCREMENTAL_RESULTS 611 : NetworkScanHelper.NETWORK_SCAN_TYPE_WAIT_FOR_ALL_RESULTS); 612 } 613 } 614 stopNetworkQuery()615 private void stopNetworkQuery() { 616 setProgressBarVisible(false); 617 if (mNetworkScanHelper != null) { 618 mWaitingForNumberOfScanResults = 0; 619 mNetworkScanHelper.stopNetworkQuery(); 620 } 621 } 622 623 @Override onDestroy()624 public void onDestroy() { 625 stopNetworkQuery(); 626 mNetworkScanExecutor.shutdown(); 627 super.onDestroy(); 628 } 629 } 630