Use the new scan API for manual network search.
This implementation takes three situations into consideration:
1. Using old scan API via Phone (support pre-Pixel2 devices). The app
will first send network scan request via new API and then receive an
ERROR_UNSUPPORTED error since modem won't support it. It will then
fall back to the old API to start a network query again via Phone. Logic
flow as below:
https://docs.google.com/drawings/d/1_LCNe6XKsqk9rp55Ik8Kw_GaKl3OsdlkerPNb6oKEiI/edit?usp=sharing
2. Using new scan API via TelephonyManager before HAL1_2 ready. The app
will first send network scan via new API and then receive a list of cell
info. The results is invalid since they do not have operator info. So it
will then fall back to the old API to start a network query again via
Phone. Logic flow as below:
https://docs.google.com/drawings/d/1fhG6INoNW4tXUCuOrkMRmsPdBQ1wvH8-dfCmMz2-QbY/edit?usp=sharing
3. Using new scan API via TelephonyManager after HAL1_2 ready. The app
will first send network scan via new API and then receive a list of cell
info, which contains both signal strength and operator info. Logic flow as below:
https://docs.google.com/drawings/d/1D7Zj0iNs0g0Ur4cy3lbYxufH6Fu5nntPiRZDM3sYHg0/edit?usp=sharing
Bug: 63984327
Test: Basic telephony sanity
Change-Id: Ifebbac9965a40acaff0d50d32ca8603c72a6a77f
Merged-In: Ifebbac9965a40acaff0d50d32ca8603c72a6a77f
diff --git a/src/com/android/phone/INetworkQueryService.aidl b/src/com/android/phone/INetworkQueryService.aidl
index b0fe992..f65c971 100644
--- a/src/com/android/phone/INetworkQueryService.aidl
+++ b/src/com/android/phone/INetworkQueryService.aidl
@@ -31,8 +31,14 @@
* object on query completion. If there is an existing request,
* then just add the callback to the list of notifications
* that will be sent upon query completion.
+ *
+ * It will send the network query with the use of
+ * <code>TelephonyManager.requestNetworkScan()</code> if the
+ * isIncrementalResult is true. And if the isIncrementalResult
+ * is set as false, it will try to send network query through
+ * <code>Phone.getAvailableNetworks()</code>.
*/
- void startNetworkQuery(in INetworkQueryServiceCallback cb, in int phoneId);
+ void startNetworkQuery(in INetworkQueryServiceCallback cb, in int phoneId, boolean isIncrementalResult);
/**
* Tells the service that the requested query is to be ignored.
@@ -40,7 +46,7 @@
* underlying RIL, but it ensures that the callback is removed
* from the list of notifications.
*/
- void stopNetworkQuery(in INetworkQueryServiceCallback cb);
+ void stopNetworkQuery();
/**
* Tells the service to unregister the network query callback.
diff --git a/src/com/android/phone/INetworkQueryServiceCallback.aidl b/src/com/android/phone/INetworkQueryServiceCallback.aidl
index 4c32883..2299f5e 100644
--- a/src/com/android/phone/INetworkQueryServiceCallback.aidl
+++ b/src/com/android/phone/INetworkQueryServiceCallback.aidl
@@ -16,6 +16,7 @@
package com.android.phone;
+import android.telephony.CellInfo;
import com.android.internal.telephony.OperatorInfo;
/**
@@ -26,14 +27,25 @@
oneway interface INetworkQueryServiceCallback {
/**
- * Called upon query completion, handing a status value and an
- * array of OperatorInfo objects.
- *
- * @param networkInfoArray is the list of OperatorInfo. Can be
- * null, indicating no results were found, or an error.
- * @param status the status indicating if there were any
- * problems with the request.
+ * Returns the scan results to the user, this callback will be
+ * called at least one time.
*/
- void onQueryComplete(in List<OperatorInfo> networkInfoArray, int status);
+ void onResults(in List<CellInfo> results);
+
+ /**
+ * Informs the user that the scan has stopped.
+ *
+ * This callback will be called when the scan is finished or cancelled by the user.
+ * The related NetworkScanRequest will be deleted after this callback.
+ */
+ void onComplete();
+
+ /**
+ * Informs the user that there is some error about the scan.
+ *
+ * This callback will be called whenever there is any error about the scan,
+ * and the scan will be terminated. onComplete() will NOT be called.
+ */
+ void onError(int error);
}
diff --git a/src/com/android/phone/NetworkQueryService.java b/src/com/android/phone/NetworkQueryService.java
index 84fde87..86f4b11 100644
--- a/src/com/android/phone/NetworkQueryService.java
+++ b/src/com/android/phone/NetworkQueryService.java
@@ -19,7 +19,6 @@
import android.app.Service;
import android.content.Context;
import android.content.Intent;
-import com.android.internal.telephony.OperatorInfo;
import android.os.AsyncResult;
import android.os.Binder;
import android.os.Handler;
@@ -27,12 +26,23 @@
import android.os.Message;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
-import android.telephony.SubscriptionManager;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneFactory;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.CellIdentityGsm;
+import android.telephony.CellInfo;
+import android.telephony.CellInfoGsm;
+import android.telephony.NetworkScan;
+import android.telephony.NetworkScanRequest;
+import android.telephony.RadioAccessSpecifier;
+import android.telephony.TelephonyManager;
+import android.telephony.TelephonyScanManager;
import android.util.Log;
+import com.android.internal.telephony.OperatorInfo;
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneFactory;
+
import java.util.ArrayList;
+import java.util.List;
/**
* Service code used to assist in querying the network for service
@@ -44,7 +54,10 @@
private static final boolean DBG = true;
// static events
- private static final int EVENT_NETWORK_SCAN_COMPLETED = 100;
+ private static final int EVENT_NETWORK_SCAN_VIA_PHONE_COMPLETED = 100;
+ private static final int EVENT_NETWORK_SCAN_RESULTS = 200;
+ private static final int EVENT_NETWORK_SCAN_ERROR = 300;
+ private static final int EVENT_NETWORK_SCAN_COMPLETED = 400;
// static states indicating the query status of the service
private static final int QUERY_READY = -1;
@@ -55,10 +68,20 @@
public static final int QUERY_EXCEPTION = 1;
static final String ACTION_LOCAL_BINDER = "com.android.phone.intent.action.LOCAL_BINDER";
-
+
/** state of the query service */
private int mState;
-
+
+ private NetworkScan mNetworkScan;
+
+ // NetworkScanRequest parameters
+ private static final int SCAN_TYPE = NetworkScanRequest.SCAN_TYPE_ONE_SHOT;
+ private static final boolean INCREMENTAL_RESULTS = true;
+ // The parameters below are in seconds
+ private static final int SEARCH_PERIODICITY_SEC = 5;
+ private static final int MAX_SEARCH_TIME_SEC = 60;
+ private static final int INCREMENTAL_RESULTS_PERIODICITY_SEC = 3;
+
/**
* Class for clients to access. Because we know this service always
* runs in the same process as its clients, we don't need to deal with
@@ -81,57 +104,150 @@
switch (msg.what) {
// if the scan is complete, broadcast the results.
// to all registerd callbacks.
+ case EVENT_NETWORK_SCAN_VIA_PHONE_COMPLETED:
+ if (DBG) log("scan via Phone completed, broadcasting results");
+ broadcastQueryResults(msg);
+ break;
+
+ case EVENT_NETWORK_SCAN_RESULTS:
+ if (DBG) log("get scan results, broadcasting results");
+ broadcastQueryResults(msg);
+ break;
+
+ case EVENT_NETWORK_SCAN_ERROR:
+ if (DBG) log("get scan error, broadcasting error code");
+ broadcastQueryResults(msg);
+ break;
+
case EVENT_NETWORK_SCAN_COMPLETED:
- if (DBG) log("scan completed, broadcasting results");
- broadcastQueryResults((AsyncResult) msg.obj);
+ if (DBG) log("network scan or stop network query completed");
+ broadcastQueryResults(msg);
break;
}
}
};
-
- /**
+
+ /**
* List of callback objects, also used to synchronize access to
* itself and to changes in state.
*/
final RemoteCallbackList<INetworkQueryServiceCallback> mCallbacks =
- new RemoteCallbackList<INetworkQueryServiceCallback> ();
-
+ new RemoteCallbackList<INetworkQueryServiceCallback>();
+
+ /**
+ * This implementation of NetworkScanCallbackImpl is used to receive callback notifications from
+ * the Telephony Manager.
+ */
+ public class NetworkScanCallbackImpl extends TelephonyScanManager.NetworkScanCallback {
+
+ /** Returns the scan results to the user, this callback will be called at least one time. */
+ public void onResults(List<CellInfo> results) {
+ if (DBG) log("got network scan results: " + results.size());
+ Message msg = mHandler.obtainMessage(EVENT_NETWORK_SCAN_RESULTS, results);
+ msg.sendToTarget();
+ }
+
+ /**
+ * Informs the user that the scan has stopped.
+ *
+ * This callback will be called when the scan is finished or cancelled by the user.
+ * The related NetworkScanRequest will be deleted after this callback.
+ */
+ public void onComplete() {
+ if (DBG) log("network scan completed");
+ Message msg = mHandler.obtainMessage(EVENT_NETWORK_SCAN_COMPLETED);
+ msg.sendToTarget();
+ }
+
+ /**
+ * Informs the user that there is some error about the scan.
+ *
+ * This callback will be called whenever there is any error about the scan, and the scan
+ * will be terminated. onComplete() will NOT be called.
+ */
+ public void onError(int error) {
+ if (DBG) log("network scan got error: " + error);
+ Message msg = mHandler.obtainMessage(EVENT_NETWORK_SCAN_ERROR, error, 0 /* arg2 */);
+ msg.sendToTarget();
+ }
+ }
+
/**
* Implementation of the INetworkQueryService interface.
*/
private final INetworkQueryService.Stub mBinder = new INetworkQueryService.Stub() {
-
+
/**
* Starts a query with a INetworkQueryServiceCallback object if
* one has not been started yet. Ignore the new query request
* if the query has been started already. Either way, place the
- * callback object in the queue to be notified upon request
+ * callback object in the queue to be notified upon request
* completion.
*/
- public void startNetworkQuery(INetworkQueryServiceCallback cb, int phoneId) {
+ public void startNetworkQuery(
+ INetworkQueryServiceCallback cb, int phoneId, boolean isIncrementalResult) {
if (cb != null) {
// register the callback to the list of callbacks.
synchronized (mCallbacks) {
mCallbacks.register(cb);
if (DBG) log("registering callback " + cb.getClass().toString());
-
+
switch (mState) {
case QUERY_READY:
- // TODO: we may want to install a timeout here in case we
- // do not get a timely response from the RIL.
- Phone phone = PhoneFactory.getPhone(phoneId);
- if (phone != null) {
- phone.getAvailableNetworks(
- mHandler.obtainMessage(EVENT_NETWORK_SCAN_COMPLETED));
+
+ if (isIncrementalResult) {
+ if (DBG) log("start network scan via TelephonManager");
+ TelephonyManager tm = (TelephonyManager) getSystemService(
+ Context.TELEPHONY_SERVICE);
+ // The Radio Access Specifiers below are meant to scan
+ // all the bands for all the supported technologies.
+ RadioAccessSpecifier gsm = new RadioAccessSpecifier(
+ AccessNetworkConstants.AccessNetworkType.GERAN,
+ null /* bands */,
+ null /* channels */);
+ RadioAccessSpecifier lte = new RadioAccessSpecifier(
+ AccessNetworkConstants.AccessNetworkType.EUTRAN,
+ null /* bands */,
+ null /* channels */);
+ RadioAccessSpecifier wcdma = new RadioAccessSpecifier(
+ AccessNetworkConstants.AccessNetworkType.UTRAN,
+ null /* bands */,
+ null /* channels */);
+ RadioAccessSpecifier[] radioAccessSpecifier = {gsm, lte, wcdma};
+ NetworkScanRequest networkScanRequest = new NetworkScanRequest(
+ SCAN_TYPE,
+ radioAccessSpecifier,
+ SEARCH_PERIODICITY_SEC,
+ MAX_SEARCH_TIME_SEC,
+ INCREMENTAL_RESULTS,
+ INCREMENTAL_RESULTS_PERIODICITY_SEC,
+ null /* List of PLMN ids (MCC-MNC) */);
+
+ // Construct a NetworkScanCallback
+ NetworkQueryService.NetworkScanCallbackImpl
+ networkScanCallback =
+ new NetworkQueryService.NetworkScanCallbackImpl();
+
+ // Request network scan
+ mNetworkScan = tm.requestNetworkScan(networkScanRequest,
+ networkScanCallback);
mState = QUERY_IS_RUNNING;
- if (DBG) log("starting new query");
} else {
- if (DBG) {
- log("phone is null");
+ Phone phone = PhoneFactory.getPhone(phoneId);
+ if (phone != null) {
+ phone.getAvailableNetworks(
+ mHandler.obtainMessage(
+ EVENT_NETWORK_SCAN_VIA_PHONE_COMPLETED));
+ mState = QUERY_IS_RUNNING;
+ if (DBG) log("start network scan via Phone");
+ } else {
+ if (DBG) {
+ log("phone is null");
+ }
}
}
+
break;
-
// do nothing if we're currently busy.
case QUERY_IS_RUNNING:
if (DBG) log("query already in progress");
@@ -141,19 +257,24 @@
}
}
}
-
+
/**
* Stops a query with a INetworkQueryServiceCallback object as
* a token.
*/
- public void stopNetworkQuery(INetworkQueryServiceCallback cb) {
- // currently we just unregister the callback, since there is
- // no way to tell the RIL to terminate the query request.
- // This means that the RIL may still be busy after the stop
- // request was made, but the state tracking logic ensures
- // that the delay will only last for 1 request even with
- // repeated button presses in the NetworkSetting activity.
- unregisterCallback(cb);
+ public void stopNetworkQuery() {
+ if (DBG) log("stop network query");
+ // Tells the RIL to terminate the query request.
+ if (mNetworkScan != null) {
+ try {
+ mNetworkScan.stop();
+ mState = QUERY_READY;
+ } catch (RemoteException e) {
+ if (DBG) log("stop mNetworkScan failed");
+ } catch (IllegalArgumentException e) {
+ // Do nothing, scan has already completed.
+ }
+ }
}
/**
@@ -171,7 +292,7 @@
@Override
public void onCreate() {
- mState = QUERY_READY;
+ mState = QUERY_READY;
}
/**
@@ -180,7 +301,7 @@
@Override
public void onStart(Intent intent, int startId) {
}
-
+
/**
* Handle the bind request.
*/
@@ -198,38 +319,85 @@
* Broadcast the results from the query to all registered callback
* objects.
*/
- private void broadcastQueryResults (AsyncResult ar) {
+ private void broadcastQueryResults(Message msg) {
// reset the state.
synchronized (mCallbacks) {
mState = QUERY_READY;
-
- // see if we need to do any work.
- if (ar == null) {
- if (DBG) log("AsyncResult is null.");
- return;
- }
-
- // TODO: we may need greater accuracy here, but for now, just a
- // simple status integer will suffice.
- int exception = (ar.exception == null) ? QUERY_OK : QUERY_EXCEPTION;
- if (DBG) log("AsyncResult has exception " + exception);
-
+
// Make the calls to all the registered callbacks.
for (int i = (mCallbacks.beginBroadcast() - 1); i >= 0; i--) {
- INetworkQueryServiceCallback cb = mCallbacks.getBroadcastItem(i);
+ INetworkQueryServiceCallback cb = mCallbacks.getBroadcastItem(i);
if (DBG) log("broadcasting results to " + cb.getClass().toString());
try {
- cb.onQueryComplete((ArrayList<OperatorInfo>) ar.result, exception);
+ switch (msg.what) {
+ case EVENT_NETWORK_SCAN_VIA_PHONE_COMPLETED:
+ AsyncResult ar = (AsyncResult) msg.obj;
+ if (ar != null) {
+ cb.onResults(getCellInfoList((List<OperatorInfo>) ar.result));
+ } else {
+ if (DBG) log("AsyncResult is null.");
+ }
+ // Send the onComplete() callback to indicate the one-time network
+ // scan has completed.
+ cb.onComplete();
+ break;
+
+ case EVENT_NETWORK_SCAN_RESULTS:
+ cb.onResults((List<CellInfo>) msg.obj);
+ break;
+
+ case EVENT_NETWORK_SCAN_COMPLETED:
+ cb.onComplete();
+ break;
+
+ case EVENT_NETWORK_SCAN_ERROR:
+ cb.onError(msg.arg1);
+ break;
+ }
} catch (RemoteException e) {
}
}
-
+
// finish up.
mCallbacks.finishBroadcast();
}
}
-
+
+ /**
+ * Wraps up a list of OperatorInfo object to a list of CellInfo object. GsmCellInfo is used here
+ * only because operatorInfo does not contain technology type while CellInfo is an abstract
+ * object that requires to specify technology type. It doesn't matter which CellInfo type to
+ * use here, since we only want to wrap the operator info and PLMN to a CellInfo object.
+ */
+ private List<CellInfo> getCellInfoList(List<OperatorInfo> operatorInfoList) {
+ List<CellInfo> cellInfoList = new ArrayList<>();
+ for (OperatorInfo oi: operatorInfoList) {
+ String operatorNumeric = oi.getOperatorNumeric();
+ String mcc = null;
+ String mnc = null;
+ log("operatorNumeric: " + operatorNumeric);
+ if (operatorNumeric != null && operatorNumeric.matches("^[0-9]{5,6}$")) {
+ mcc = operatorNumeric.substring(0, 3);
+ mnc = operatorNumeric.substring(3);
+ }
+ CellIdentityGsm cig = new CellIdentityGsm(
+ Integer.MAX_VALUE /* lac */,
+ Integer.MAX_VALUE /* cid */,
+ Integer.MAX_VALUE /* arfcn */,
+ Integer.MAX_VALUE /* bsic */,
+ mcc,
+ mnc,
+ oi.getOperatorAlphaLong(),
+ oi.getOperatorAlphaShort());
+
+ CellInfoGsm ci = new CellInfoGsm();
+ ci.setCellIdentity(cig);
+ cellInfoList.add(ci);
+ }
+ return cellInfoList;
+ }
+
private static void log(String msg) {
Log.d(LOG_TAG, msg);
- }
-}
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/phone/NetworkSelectListPreference.java b/src/com/android/phone/NetworkSelectListPreference.java
index 1af31b2..cc54b09 100644
--- a/src/com/android/phone/NetworkSelectListPreference.java
+++ b/src/com/android/phone/NetworkSelectListPreference.java
@@ -28,6 +28,16 @@
import android.os.RemoteException;
import android.preference.ListPreference;
import android.preference.Preference;
+import android.telephony.CellInfo;
+import android.telephony.CellInfoCdma;
+import android.telephony.CellInfoGsm;
+import android.telephony.CellInfoLte;
+import android.telephony.CellInfoWcdma;
+import android.telephony.CellSignalStrengthCdma;
+import android.telephony.CellSignalStrengthGsm;
+import android.telephony.CellSignalStrengthLte;
+import android.telephony.CellSignalStrengthWcdma;
+import android.telephony.NetworkScan;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.text.BidiFormatter;
@@ -42,6 +52,7 @@
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneFactory;
+import java.util.ArrayList;
import java.util.List;
@@ -57,19 +68,22 @@
private static final String LOG_TAG = "networkSelect";
private static final boolean DBG = true;
- private static final int EVENT_NETWORK_SCAN_COMPLETED = 100;
- private static final int EVENT_NETWORK_SELECTION_DONE = 200;
+ private static final int EVENT_NETWORK_SELECTION_DONE = 1;
+ private static final int EVENT_NETWORK_SCAN_RESULTS = 2;
+ private static final int EVENT_NETWORK_SCAN_ERROR = 3;
+ private static final int EVENT_NETWORK_SCAN_COMPLETED = 4;
//dialog ids
private static final int DIALOG_NETWORK_SELECTION = 100;
private static final int DIALOG_NETWORK_LIST_LOAD = 200;
private int mPhoneId = SubscriptionManager.INVALID_PHONE_INDEX;
- private List<OperatorInfo> mOperatorInfoList;
- private OperatorInfo mOperatorInfo;
+ private List<CellInfo> mCellInfoList;
+ private CellInfo mCellInfo;
private int mSubId;
private NetworkOperators mNetworkOperators;
+ private boolean mNeedScanAgain;
private ProgressDialog mProgressDialog;
public NetworkSelectListPreference(Context context, AttributeSet attrs) {
@@ -77,14 +91,15 @@
}
public NetworkSelectListPreference(Context context, AttributeSet attrs, int defStyleAttr,
- int defStyleRes) {
+ int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
protected void onClick() {
sendMetricsEvent(null);
- loadNetworksList();
+ // Scan the network with setting the isIncrementalResult as true via TelephonyManager first.
+ loadNetworksList(true);
}
private final Handler mHandler = new Handler() {
@@ -92,10 +107,6 @@
public void handleMessage(Message msg) {
AsyncResult ar;
switch (msg.what) {
- case EVENT_NETWORK_SCAN_COMPLETED:
- networksListLoaded((List<OperatorInfo>) msg.obj, msg.arg1);
- break;
-
case EVENT_NETWORK_SELECTION_DONE:
if (DBG) logd("hideProgressPanel");
try {
@@ -110,15 +121,98 @@
mNetworkOperators.displayNetworkSelectionFailed(ar.exception);
} else {
if (DBG) {
- logd("manual network selection: succeeded!"
- + getNetworkTitle(mOperatorInfo));
+ logd("manual network selection: succeeded! "
+ + getNetworkTitle(mCellInfo));
}
mNetworkOperators.displayNetworkSelectionSucceeded();
}
mNetworkOperators.getNetworkSelectionMode();
break;
- }
+ case EVENT_NETWORK_SCAN_RESULTS:
+ List<CellInfo> results = (List<CellInfo>) msg.obj;
+ results.removeIf(cellInfo -> cellInfo == null);
+ if (results.size() > 0) {
+ boolean isInvalidCellInfoList = true;
+ // Regard the list as invalid only if all the elements in the list are
+ // invalid.
+ for (CellInfo cellInfo : results) {
+ if (!isInvalidCellInfo(cellInfo)) {
+ isInvalidCellInfoList = false;
+ break;
+ }
+ }
+ if (isInvalidCellInfoList) {
+ mNeedScanAgain = true;
+ if (DBG) {
+ logd("Invalid cell info. Stop current network scan "
+ + "and start a new one via old API");
+ }
+ // Stop current network scan flow. This behavior will result in a
+ // onComplete() callback, after which we will start a new network query
+ // via Phone.getAvailableNetworks(). This behavior might also result in
+ // a onError() callback if the modem did not stop network query
+ // successfully. In this case we will display network query failed
+ // instead of resending a new request.
+ try {
+ if (mNetworkQueryService != null) {
+ mNetworkQueryService.stopNetworkQuery();
+ }
+ } catch (RemoteException e) {
+ loge("exception from stopNetworkQuery " + e);
+ }
+ } else {
+ // TODO(b/70530820): Display the scan results incrementally after
+ // finalizing the UI desing on Mobile Network Setting page. For now,
+ // just update the CellInfo list when received the onResult callback,
+ // and display the scan result when received the onComplete callback
+ // in the end.
+ mCellInfoList = new ArrayList<>(results);
+ if (DBG) logd("CALLBACK_SCAN_RESULTS" + mCellInfoList.toString());
+ }
+ }
+
+ break;
+
+ case EVENT_NETWORK_SCAN_ERROR:
+ int error = msg.arg1;
+ if (DBG) logd("error while querying available networks " + error);
+ if (error == NetworkScan.ERROR_UNSUPPORTED) {
+ if (DBG) {
+ logd("Modem does not support: try to scan network again via Phone");
+ }
+ loadNetworksList(false);
+ } else {
+ try {
+ if (mNetworkQueryService != null) {
+ mNetworkQueryService.unregisterCallback(mCallback);
+ }
+ } catch (RemoteException e) {
+ loge("onError: exception from unregisterCallback " + e);
+ }
+ displayNetworkQueryFailed(error);
+ }
+ break;
+
+ case EVENT_NETWORK_SCAN_COMPLETED:
+ if (mNeedScanAgain) {
+ logd("CellInfo is invalid to display. Start a new scan via Phone. ");
+ loadNetworksList(false);
+ mNeedScanAgain = false;
+ } else {
+ try {
+ if (mNetworkQueryService != null) {
+ mNetworkQueryService.unregisterCallback(mCallback);
+ }
+ } catch (RemoteException e) {
+ loge("onComplete: exception from unregisterCallback " + e);
+ }
+ if (DBG) logd("scan complete, load the cellInfosList");
+ // Modify UI to indicate users that the scan has completed.
+ networksListLoaded();
+ }
+ break;
+ }
return;
}
};
@@ -130,11 +224,34 @@
*/
private final INetworkQueryServiceCallback mCallback = new INetworkQueryServiceCallback.Stub() {
- /** place the message on the looper queue upon query completion. */
- public void onQueryComplete(List<OperatorInfo> networkInfoArray, int status) {
- if (DBG) logd("notifying message loop of query completion.");
- Message msg = mHandler.obtainMessage(EVENT_NETWORK_SCAN_COMPLETED,
- status, 0, networkInfoArray);
+ /** Returns the scan results to the user, this callback will be called at lease one time. */
+ public void onResults(List<CellInfo> results) {
+ if (DBG) logd("get scan results.");
+ Message msg = mHandler.obtainMessage(EVENT_NETWORK_SCAN_RESULTS, results);
+ msg.sendToTarget();
+ }
+
+ /**
+ * Informs the user that the scan has stopped.
+ *
+ * This callback will be called when the scan is finished or cancelled by the user.
+ * The related NetworkScanRequest will be deleted after this callback.
+ */
+ public void onComplete() {
+ if (DBG) logd("network scan completed.");
+ Message msg = mHandler.obtainMessage(EVENT_NETWORK_SCAN_COMPLETED);
+ msg.sendToTarget();
+ }
+
+ /**
+ * Informs the user that there is some error about the scan.
+ *
+ * This callback will be called whenever there is any error about the scan, and the scan
+ * will be terminated. onComplete() will NOT be called.
+ */
+ public void onError(int error) {
+ if (DBG) logd("get onError callback with error code: " + error);
+ Message msg = mHandler.obtainMessage(EVENT_NETWORK_SCAN_ERROR, error, 0 /* arg2 */);
msg.sendToTarget();
}
};
@@ -142,10 +259,12 @@
@Override
//implemented for DialogInterface.OnCancelListener
public void onCancel(DialogInterface dialog) {
+ if (DBG) logd("user manually close the dialog");
// request that the service stop the query with this callback object.
try {
if (mNetworkQueryService != null) {
- mNetworkQueryService.stopNetworkQuery(mCallback);
+ mNetworkQueryService.stopNetworkQuery();
+ mNetworkQueryService.unregisterCallback(mCallback);
}
// If cancelled, we query NetworkSelectMode and update states of AutoSelect button.
mNetworkOperators.getNetworkSelectionMode();
@@ -164,18 +283,6 @@
}
}
- /**
- * Return normalized carrier name given network info.
- *
- * @param ni is network information in OperatorInfo type.
- */
- public String getNormalizedCarrierName(OperatorInfo ni) {
- if (ni != null) {
- return ni.getOperatorAlphaLong() + " (" + ni.getOperatorNumeric() + ")";
- }
- return null;
- }
-
// This method is provided besides initialize() because bind to network query service
// may be binded after initialize(). In that case this method needs to be called explicitly
// to set mNetworkQueryService. Otherwise mNetworkQueryService will remain null.
@@ -185,12 +292,13 @@
// This initialize method needs to be called for this preference to work properly.
protected void initialize(int subId, INetworkQueryService queryService,
- NetworkOperators networkOperators, ProgressDialog progressDialog) {
+ NetworkOperators networkOperators, ProgressDialog progressDialog) {
mSubId = subId;
mNetworkQueryService = queryService;
mNetworkOperators = networkOperators;
// This preference should share the same progressDialog with networkOperators category.
mProgressDialog = progressDialog;
+ mNeedScanAgain = false;
if (SubscriptionManager.isValidSubscriptionId(mSubId)) {
mPhoneId = SubscriptionManager.getPhoneId(mSubId);
@@ -236,7 +344,7 @@
}
private void displayNetworkSelectionInProgress() {
- showProgressBar(DIALOG_NETWORK_SELECTION);
+ showProgressDialog(DIALOG_NETWORK_SELECTION);
}
private void displayNetworkQueryFailed(int error) {
@@ -253,15 +361,17 @@
NotificationMgr.NETWORK_SELECTION_NOTIFICATION, status);
}
- private void loadNetworksList() {
+ private void loadNetworksList(boolean isIncrementalResult) {
if (DBG) logd("load networks list...");
- showProgressBar(DIALOG_NETWORK_LIST_LOAD);
+ if (!mNeedScanAgain) {
+ // Avoid blinking while showing the dialog again.
+ showProgressDialog(DIALOG_NETWORK_LIST_LOAD);
+ }
- // delegate query request to the service.
try {
if (mNetworkQueryService != null) {
- mNetworkQueryService.startNetworkQuery(mCallback, mPhoneId);
+ mNetworkQueryService.startNetworkQuery(mCallback, mPhoneId, isIncrementalResult);
} else {
displayNetworkQueryFailed(NetworkQueryService.QUERY_EXCEPTION);
}
@@ -271,25 +381,9 @@
}
}
- /**
- * networksListLoaded has been rewritten to take an array of
- * OperatorInfo objects and a status field, instead of an
- * AsyncResult. Otherwise, the functionality which takes the
- * OperatorInfo array and creates a list of preferences from it,
- * remains unchanged.
- */
- private void networksListLoaded(List<OperatorInfo> result, int status) {
+ private void networksListLoaded() {
if (DBG) logd("networks list loaded");
- // used to un-register callback
- try {
- if (mNetworkQueryService != null) {
- mNetworkQueryService.unregisterCallback(mCallback);
- }
- } catch (RemoteException e) {
- loge("networksListLoaded: exception from unregisterCallback " + e);
- }
-
// update the state of the preferences.
if (DBG) logd("hideProgressPanel");
@@ -305,62 +399,28 @@
}
setEnabled(true);
- clearList();
- if (status != NetworkQueryService.QUERY_OK) {
- if (DBG) logd("error while querying available networks");
- displayNetworkQueryFailed(status);
- } else {
- if (result != null) {
- // create a preference for each item in the list.
- // just use the operator name instead of the mildly
- // confusing mcc/mnc.
- mOperatorInfoList = result;
- CharSequence[] networkEntries = new CharSequence[result.size()];
- CharSequence[] networkEntryValues = new CharSequence[result.size()];
- for (int i = 0; i < mOperatorInfoList.size(); i++) {
- if (mOperatorInfoList.get(i).getState() == OperatorInfo.State.FORBIDDEN) {
- networkEntries[i] = getNetworkTitle(mOperatorInfoList.get(i))
- + " "
- + getContext().getResources().getString(R.string.forbidden_network);
- } else {
- networkEntries[i] = getNetworkTitle(mOperatorInfoList.get(i));
- }
- networkEntryValues[i] = Integer.toString(i + 2);
+ if (mCellInfoList != null) {
+ // create a preference for each item in the list.
+ // just use the operator name instead of the mildly
+ // confusing mcc/mnc.
+ List<CharSequence> networkEntriesList = new ArrayList<>();
+ List<CharSequence> networkEntryValuesList = new ArrayList<>();
+ for (CellInfo cellInfo: mCellInfoList) {
+ // Display each operator name only once.
+ String networkTitle = getNetworkTitle(cellInfo);
+ if (!networkEntriesList.contains(networkTitle)) {
+ networkEntriesList.add(networkTitle);
+ networkEntryValuesList.add(Integer.toString(networkEntriesList.size() + 1));
}
-
- setEntries(networkEntries);
- setEntryValues(networkEntryValues);
-
- super.onClick();
- } else {
- displayEmptyNetworkList();
}
- }
- }
+ setEntries(networkEntriesList.toArray(new CharSequence[networkEntriesList.size()]));
+ setEntryValues(networkEntryValuesList.toArray(
+ new CharSequence[networkEntryValuesList.size()]));
- /**
- * Returns the title of the network obtained in the manual search.
- *
- * @param ni contains the information of the network.
- *
- * @return Long Name if not null/empty, otherwise Short Name if not null/empty,
- * else MCCMNC string.
- */
- private String getNetworkTitle(OperatorInfo ni) {
- if (!TextUtils.isEmpty(ni.getOperatorAlphaLong())) {
- return ni.getOperatorAlphaLong();
- } else if (!TextUtils.isEmpty(ni.getOperatorAlphaShort())) {
- return ni.getOperatorAlphaShort();
+ super.onClick();
} else {
- BidiFormatter bidiFormatter = BidiFormatter.getInstance();
- return bidiFormatter.unicodeWrap(ni.getOperatorNumeric(), TextDirectionHeuristics.LTR);
- }
- }
-
- private void clearList() {
- if (mOperatorInfoList != null) {
- mOperatorInfoList.clear();
+ displayEmptyNetworkList();
}
}
@@ -370,7 +430,7 @@
}
}
- private void showProgressBar(int id) {
+ private void showProgressDialog(int id) {
if (mProgressDialog == null) {
mProgressDialog = new ProgressDialog(getContext());
} else {
@@ -378,29 +438,27 @@
dismissProgressBar();
}
- if ((id == DIALOG_NETWORK_SELECTION) || (id == DIALOG_NETWORK_LIST_LOAD)) {
- switch (id) {
- case DIALOG_NETWORK_SELECTION:
- final String networkSelectMsg = getContext().getResources()
- .getString(R.string.register_on_network,
- getNetworkTitle(mOperatorInfo));
- mProgressDialog.setMessage(networkSelectMsg);
- mProgressDialog.setCanceledOnTouchOutside(false);
- mProgressDialog.setCancelable(false);
- mProgressDialog.setIndeterminate(true);
- break;
- case DIALOG_NETWORK_LIST_LOAD:
- mProgressDialog.setMessage(
- getContext().getResources().getString(R.string.load_networks_progress));
- mProgressDialog.setCanceledOnTouchOutside(false);
- mProgressDialog.setCancelable(true);
- mProgressDialog.setIndeterminate(false);
- mProgressDialog.setOnCancelListener(this);
- break;
- default:
- }
- mProgressDialog.show();
+ switch (id) {
+ case DIALOG_NETWORK_SELECTION:
+ final String networkSelectMsg = getContext().getResources()
+ .getString(R.string.register_on_network,
+ getNetworkTitle(mCellInfo));
+ mProgressDialog.setMessage(networkSelectMsg);
+ mProgressDialog.setCanceledOnTouchOutside(false);
+ mProgressDialog.setCancelable(false);
+ mProgressDialog.setIndeterminate(true);
+ break;
+ case DIALOG_NETWORK_LIST_LOAD:
+ mProgressDialog.setMessage(
+ getContext().getResources().getString(R.string.load_networks_progress));
+ mProgressDialog.setCanceledOnTouchOutside(false);
+ mProgressDialog.setCancelable(true);
+ mProgressDialog.setIndeterminate(false);
+ mProgressDialog.setOnCancelListener(this);
+ break;
+ default:
}
+ mProgressDialog.show();
}
/**
@@ -413,24 +471,119 @@
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
int operatorIndex = findIndexOfValue((String) newValue);
- mOperatorInfo = mOperatorInfoList.get(operatorIndex);
+ mCellInfo = mCellInfoList.get(operatorIndex);
+ if (DBG) logd("selected network: " + mCellInfo.toString());
- if (DBG) logd("selected network: " + getNetworkTitle(mOperatorInfo));
-
- sendMetricsEvent(getNetworkTitle(mOperatorInfo));
+ sendMetricsEvent(getNetworkTitle(mCellInfo));
Message msg = mHandler.obtainMessage(EVENT_NETWORK_SELECTION_DONE);
Phone phone = PhoneFactory.getPhone(mPhoneId);
if (phone != null) {
- phone.selectNetworkManually(mOperatorInfo, true, msg);
+ OperatorInfo operatorInfo = getOperatorInfoFromCellInfo(mCellInfo);
+ if (DBG) logd("manually selected network: " + operatorInfo.toString());
+ phone.selectNetworkManually(operatorInfo, true, msg);
displayNetworkSelectionInProgress();
} else {
loge("Error selecting network. phone is null.");
}
-
return true;
}
+ /**
+ * Returns the title of the network obtained in the manual search.
+ *
+ * @param cellInfo contains the information of the network.
+ * @return Long Name if not null/empty, otherwise Short Name if not null/empty,
+ * else MCCMNC string.
+ */
+ private String getNetworkTitle(CellInfo cellInfo) {
+ OperatorInfo ni = getOperatorInfoFromCellInfo(cellInfo);
+
+ if (!TextUtils.isEmpty(ni.getOperatorAlphaLong())) {
+ return ni.getOperatorAlphaLong();
+ } else if (!TextUtils.isEmpty(ni.getOperatorAlphaShort())) {
+ return ni.getOperatorAlphaShort();
+ } else {
+ BidiFormatter bidiFormatter = BidiFormatter.getInstance();
+ return bidiFormatter.unicodeWrap(ni.getOperatorNumeric(), TextDirectionHeuristics.LTR);
+ }
+ }
+
+ /**
+ * Wrap a cell info into an operator info.
+ */
+ private OperatorInfo getOperatorInfoFromCellInfo(CellInfo cellInfo) {
+ OperatorInfo oi;
+ if (cellInfo instanceof CellInfoLte) {
+ CellInfoLte lte = (CellInfoLte) cellInfo;
+ oi = new OperatorInfo(
+ (String) lte.getCellIdentity().getOperatorAlphaLong(),
+ (String) lte.getCellIdentity().getOperatorAlphaShort(),
+ lte.getCellIdentity().getMobileNetworkOperator());
+ } else if (cellInfo instanceof CellInfoWcdma) {
+ CellInfoWcdma wcdma = (CellInfoWcdma) cellInfo;
+ oi = new OperatorInfo(
+ (String) wcdma.getCellIdentity().getOperatorAlphaLong(),
+ (String) wcdma.getCellIdentity().getOperatorAlphaShort(),
+ wcdma.getCellIdentity().getMobileNetworkOperator());
+ } else if (cellInfo instanceof CellInfoGsm) {
+ CellInfoGsm gsm = (CellInfoGsm) cellInfo;
+ oi = new OperatorInfo(
+ (String) gsm.getCellIdentity().getOperatorAlphaLong(),
+ (String) gsm.getCellIdentity().getOperatorAlphaShort(),
+ gsm.getCellIdentity().getMobileNetworkOperator());
+ } else if (cellInfo instanceof CellInfoCdma) {
+ CellInfoCdma cdma = (CellInfoCdma) cellInfo;
+ oi = new OperatorInfo(
+ (String) cdma.getCellIdentity().getOperatorAlphaLong(),
+ (String) cdma.getCellIdentity().getOperatorAlphaShort(),
+ "" /* operator numeric */);
+ } else {
+ oi = new OperatorInfo("", "", "");
+ }
+ return oi;
+ }
+
+
+ /**
+ * Check if the CellInfo is valid to display. If a CellInfo has signal strength but does
+ * not have operator info, it is invalid to display.
+ */
+ private boolean isInvalidCellInfo(CellInfo cellInfo) {
+ if (DBG) logd("Check isInvalidCellInfo: " + cellInfo.toString());
+ CharSequence al = null;
+ CharSequence as = null;
+ boolean hasSignalStrength = false;
+ if (cellInfo instanceof CellInfoLte) {
+ CellInfoLte lte = (CellInfoLte) cellInfo;
+ al = lte.getCellIdentity().getOperatorAlphaLong();
+ as = lte.getCellIdentity().getOperatorAlphaShort();
+ hasSignalStrength = !lte.getCellSignalStrength().equals(new CellSignalStrengthLte());
+ } else if (cellInfo instanceof CellInfoWcdma) {
+ CellInfoWcdma wcdma = (CellInfoWcdma) cellInfo;
+ al = wcdma.getCellIdentity().getOperatorAlphaLong();
+ as = wcdma.getCellIdentity().getOperatorAlphaShort();
+ hasSignalStrength = !wcdma.getCellSignalStrength().equals(
+ new CellSignalStrengthWcdma());
+ } else if (cellInfo instanceof CellInfoGsm) {
+ CellInfoGsm gsm = (CellInfoGsm) cellInfo;
+ al = gsm.getCellIdentity().getOperatorAlphaLong();
+ as = gsm.getCellIdentity().getOperatorAlphaShort();
+ hasSignalStrength = !gsm.getCellSignalStrength().equals(new CellSignalStrengthGsm());
+ } else if (cellInfo instanceof CellInfoCdma) {
+ CellInfoCdma cdma = (CellInfoCdma) cellInfo;
+ al = cdma.getCellIdentity().getOperatorAlphaLong();
+ as = cdma.getCellIdentity().getOperatorAlphaShort();
+ hasSignalStrength = !cdma.getCellSignalStrength().equals(new CellSignalStrengthCdma());
+ } else {
+ return true;
+ }
+ if (TextUtils.isEmpty(al) && TextUtils.isEmpty(as) && hasSignalStrength) {
+ return true;
+ }
+ return false;
+ }
+
@Override
protected Parcelable onSaveInstanceState() {
final Parcelable superState = super.onSaveInstanceState();
@@ -442,7 +595,7 @@
final SavedState myState = new SavedState(superState);
myState.mDialogListEntries = getEntries();
myState.mDialogListEntryValues = getEntryValues();
- myState.mOperatorInfoList = mOperatorInfoList;
+ myState.mCellInfoList = mCellInfoList;
return myState;
}
@@ -462,8 +615,8 @@
if (getEntryValues() == null && myState.mDialogListEntryValues != null) {
setEntryValues(myState.mDialogListEntryValues);
}
- if (mOperatorInfoList == null && myState.mOperatorInfoList != null) {
- mOperatorInfoList = myState.mOperatorInfoList;
+ if (mCellInfoList == null && myState.mCellInfoList != null) {
+ mCellInfoList = myState.mCellInfoList;
}
super.onRestoreInstanceState(myState.getSuperState());
@@ -478,14 +631,14 @@
private static class SavedState extends BaseSavedState {
CharSequence[] mDialogListEntries;
CharSequence[] mDialogListEntryValues;
- List<OperatorInfo> mOperatorInfoList;
+ List<CellInfo> mCellInfoList;
SavedState(Parcel source) {
super(source);
final ClassLoader boot = Object.class.getClassLoader();
mDialogListEntries = source.readCharSequenceArray();
mDialogListEntryValues = source.readCharSequenceArray();
- mOperatorInfoList = source.readParcelableList(mOperatorInfoList, boot);
+ mCellInfoList = source.readParcelableList(mCellInfoList, boot);
}
@Override
@@ -493,7 +646,7 @@
super.writeToParcel(dest, flags);
dest.writeCharSequenceArray(mDialogListEntries);
dest.writeCharSequenceArray(mDialogListEntryValues);
- dest.writeParcelableList(mOperatorInfoList, flags);
+ dest.writeParcelableList(mCellInfoList, flags);
}
SavedState(Parcelable superState) {
@@ -515,7 +668,7 @@
private void sendMetricsEvent(String network) {
final LogMaker logMaker =
new LogMaker(MetricsEvent.ACTION_MOBILE_NETWORK_MANUAL_SELECT_NETWORK)
- .setType(MetricsEvent.TYPE_ACTION);
+ .setType(MetricsEvent.TYPE_ACTION);
if (network != null) {
// Since operator list is loaded dynamically from modem, we cannot know which network
@@ -534,4 +687,4 @@
private void loge(String msg) {
Log.e(LOG_TAG, "[NetworksList] " + msg);
}
-}
+}
\ No newline at end of file