Loading src/java/com/android/internal/telephony/CommandsInterface.java +2 −1 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import android.os.Message; import android.os.WorkSource; import android.service.carrier.CarrierIdentifier; import android.telephony.ClientRequestStats; import android.telephony.NetworkScanRequest; import com.android.internal.telephony.cdma.CdmaSmsBroadcastConfigInfo; import com.android.internal.telephony.dataconnection.DataProfile; Loading Loading @@ -1293,7 +1294,7 @@ public interface CommandsInterface { * * ((AsyncResult)response.obj).result is a NetworkScanResult object */ void startNetworkScan(Message response); void startNetworkScan(NetworkScanRequest nsr, Message response); /** * Stops the ongoing network scan Loading src/java/com/android/internal/telephony/GsmCdmaPhone.java +3 −2 Original line number Diff line number Diff line Loading @@ -56,6 +56,7 @@ import android.telecom.VideoProfile; import android.telephony.CarrierConfigManager; import android.telephony.CellLocation; import android.telephony.ImsiEncryptionInfo; import android.telephony.NetworkScanRequest; import android.telephony.PhoneNumberUtils; import android.telephony.Rlog; import android.telephony.ServiceState; Loading Loading @@ -1815,8 +1816,8 @@ public class GsmCdmaPhone extends Phone { } @Override public void startNetworkScan(Message response) { mCi.startNetworkScan(response); public void startNetworkScan(NetworkScanRequest nsr, Message response) { mCi.startNetworkScan(nsr, response); } @Override Loading src/java/com/android/internal/telephony/NetworkScanRequestTracker.java 0 → 100644 +483 −0 Original line number Diff line number Diff line /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.telephony; import static android.telephony.RadioNetworkConstants.RadioAccessNetworks.EUTRAN; import static android.telephony.RadioNetworkConstants.RadioAccessNetworks.GERAN; import static android.telephony.RadioNetworkConstants.RadioAccessNetworks.UTRAN; import android.hardware.radio.V1_0.RadioError; import android.os.AsyncResult; import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.Messenger; import android.os.Process; import android.os.RemoteException; import android.telephony.NetworkScan; import android.telephony.NetworkScanRequest; import android.telephony.RadioAccessSpecifier; import android.telephony.TelephonyScanManager; import android.util.Log; import java.util.concurrent.atomic.AtomicInteger; /** * Manages radio access network scan requests. * * Provides methods to start and stop network scan requests, and keeps track of all the live scans. * * {@hide} */ public final class NetworkScanRequestTracker { private static final String TAG = "ScanRequestTracker"; private static final int CMD_START_NETWORK_SCAN = 1; private static final int EVENT_START_NETWORK_SCAN_DONE = 2; private static final int EVENT_RECEIVE_NETWORK_SCAN_RESULT = 3; private static final int CMD_STOP_NETWORK_SCAN = 4; private static final int EVENT_STOP_NETWORK_SCAN_DONE = 5; private static final int CMD_INTERRUPT_NETWORK_SCAN = 6; private static final int EVENT_INTERRUPT_NETWORK_SCAN_DONE = 7; private final Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case CMD_START_NETWORK_SCAN: mScheduler.doStartScan((NetworkScanRequestInfo) msg.obj); break; case EVENT_START_NETWORK_SCAN_DONE: mScheduler.startScanDone((AsyncResult) msg.obj); break; case EVENT_RECEIVE_NETWORK_SCAN_RESULT: mScheduler.receiveResult((AsyncResult) msg.obj); break; case CMD_STOP_NETWORK_SCAN: mScheduler.doStopScan(msg.arg1); break; case EVENT_STOP_NETWORK_SCAN_DONE: mScheduler.stopScanDone((AsyncResult) msg.obj); break; case CMD_INTERRUPT_NETWORK_SCAN: mScheduler.doInterruptScan(msg.arg1); break; case EVENT_INTERRUPT_NETWORK_SCAN_DONE: mScheduler.interruptScanDone((AsyncResult) msg.obj); break; } } }; // The sequence number of NetworkScanRequests private final AtomicInteger mNextNetworkScanRequestId = new AtomicInteger(1); private final NetworkScanRequestScheduler mScheduler = new NetworkScanRequestScheduler(); private void logEmptyResultOrException(AsyncResult ar) { if (ar.result == null) { Log.e(TAG, "NetworkScanResult: Empty result"); } else { Log.e(TAG, "NetworkScanResult: Exception: " + ar.exception); } } private boolean isValidScan(NetworkScanRequestInfo nsri) { if (nsri.mRequest.specifiers.length > NetworkScanRequest.MAX_RADIO_ACCESS_NETWORKS) { return false; } for (RadioAccessSpecifier ras : nsri.mRequest.specifiers) { if (ras.radioAccessNetwork != GERAN && ras.radioAccessNetwork != UTRAN && ras.radioAccessNetwork != EUTRAN) { return false; } if (ras.bands.length > NetworkScanRequest.MAX_BANDS || ras.channels.length > NetworkScanRequest.MAX_CHANNELS) { return false; } } return true; } /** Sends a message back to the application via its callback. */ private void notifyMessenger(NetworkScanRequestInfo nsri, int what, int err, Object result) { Messenger messenger = nsri.mMessenger; Message message = Message.obtain(); message.what = what; message.arg1 = err; message.arg2 = nsri.mScanId; message.obj = result; try { messenger.send(message); } catch (RemoteException e) { Log.e(TAG, "Exception in notifyMessenger: " + e); } } /** * Tracks info about the radio network scan. * * Also used to notice when the calling process dies so we can self-expire. */ class NetworkScanRequestInfo implements IBinder.DeathRecipient { private final NetworkScanRequest mRequest; private final Messenger mMessenger; private final IBinder mBinder; private final Phone mPhone; private final int mScanId; private final int mUid; private final int mPid; private boolean mIsBinderDead; NetworkScanRequestInfo(NetworkScanRequest r, Messenger m, IBinder b, int id, Phone phone) { super(); mRequest = r; mMessenger = m; mBinder = b; mScanId = id; mPhone = phone; mUid = Binder.getCallingUid(); mPid = Binder.getCallingPid(); mIsBinderDead = false; try { mBinder.linkToDeath(this, 0); } catch (RemoteException e) { binderDied(); } } synchronized void setIsBinderDead(boolean val) { mIsBinderDead = val; } synchronized boolean getIsBinderDead() { return mIsBinderDead; } NetworkScanRequest getRequest() { return mRequest; } void unlinkDeathRecipient() { if (mBinder != null) { mBinder.unlinkToDeath(this, 0); } } @Override public void binderDied() { Log.e(TAG, "PhoneInterfaceManager NetworkScanRequestInfo binderDied(" + mRequest + ", " + mBinder + ")"); setIsBinderDead(true); interruptNetworkScan(mScanId); } } /** * Handles multiplexing and scheduling for multiple requests. */ private class NetworkScanRequestScheduler { private NetworkScanRequestInfo mLiveRequestInfo; private NetworkScanRequestInfo mPendingRequestInfo; private int rilErrorToScanError(int rilError) { switch (rilError) { case RadioError.NONE: return NetworkScan.SUCCESS; case RadioError.RADIO_NOT_AVAILABLE: Log.e(TAG, "rilErrorToScanError: RADIO_NOT_AVAILABLE"); return NetworkScan.ERROR_MODEM_ERROR; case RadioError.REQUEST_NOT_SUPPORTED: Log.e(TAG, "rilErrorToScanError: REQUEST_NOT_SUPPORTED"); return NetworkScan.ERROR_UNSUPPORTED; case RadioError.NO_MEMORY: Log.e(TAG, "rilErrorToScanError: NO_MEMORY"); return NetworkScan.ERROR_MODEM_ERROR; case RadioError.INTERNAL_ERR: Log.e(TAG, "rilErrorToScanError: INTERNAL_ERR"); return NetworkScan.ERROR_MODEM_ERROR; case RadioError.MODEM_ERR: Log.e(TAG, "rilErrorToScanError: MODEM_ERR"); return NetworkScan.ERROR_MODEM_ERROR; case RadioError.OPERATION_NOT_ALLOWED: Log.e(TAG, "rilErrorToScanError: OPERATION_NOT_ALLOWED"); return NetworkScan.ERROR_MODEM_ERROR; case RadioError.INVALID_ARGUMENTS: Log.e(TAG, "rilErrorToScanError: INVALID_ARGUMENTS"); return NetworkScan.ERROR_INVALID_SCAN; case RadioError.DEVICE_IN_USE: Log.e(TAG, "rilErrorToScanError: DEVICE_IN_USE"); return NetworkScan.ERROR_MODEM_BUSY; default: Log.e(TAG, "rilErrorToScanError: Unexpected RadioError " + rilError); return NetworkScan.ERROR_RIL_ERROR; } } private void doStartScan(NetworkScanRequestInfo nsri) { if (nsri == null) { Log.e(TAG, "CMD_START_NETWORK_SCAN: nsri is null"); return; } if (!isValidScan(nsri)) { notifyMessenger(nsri, TelephonyScanManager.CALLBACK_SCAN_ERROR, NetworkScan.ERROR_INVALID_SCAN, null); return; } if (nsri.getIsBinderDead()) { Log.e(TAG, "CMD_START_NETWORK_SCAN: Binder has died"); return; } if (!startNewScan(nsri)) { if (!interruptLiveScan(nsri)) { if (!cacheScan(nsri)) { notifyMessenger(nsri, TelephonyScanManager.CALLBACK_SCAN_ERROR, NetworkScan.ERROR_MODEM_BUSY, null); } } } } private synchronized void startScanDone(AsyncResult ar) { NetworkScanRequestInfo nsri = (NetworkScanRequestInfo) ar.userObj; if (nsri == null) { Log.e(TAG, "EVENT_START_NETWORK_SCAN_DONE: nsri is null"); return; } if (mLiveRequestInfo == null || nsri.mScanId != mLiveRequestInfo.mScanId) { Log.e(TAG, "EVENT_START_NETWORK_SCAN_DONE: nsri does not match mLiveRequestInfo"); return; } if (ar.exception == null && ar.result != null) { NetworkScanResult nsr = (NetworkScanResult) ar.result; if (nsr.scanError == NetworkScan.SUCCESS) { // Register for the scan results if the scan started successfully. nsri.mPhone.mCi.registerForNetworkScanResult(mHandler, EVENT_RECEIVE_NETWORK_SCAN_RESULT, nsri); } else { deleteScanAndMayNotify(nsri, rilErrorToScanError(nsr.scanError), true); } } else { logEmptyResultOrException(ar); deleteScanAndMayNotify(nsri, NetworkScan.ERROR_RIL_ERROR, true); } } private void receiveResult(AsyncResult ar) { NetworkScanRequestInfo nsri = (NetworkScanRequestInfo) ar.userObj; if (nsri == null) { Log.e(TAG, "EVENT_RECEIVE_NETWORK_SCAN_RESULT: nsri is null"); return; } if (ar.exception == null && ar.result != null) { NetworkScanResult nsr = (NetworkScanResult) ar.result; if (nsr.scanError == NetworkScan.SUCCESS) { notifyMessenger(nsri, TelephonyScanManager.CALLBACK_SCAN_RESULTS, rilErrorToScanError(nsr.scanError), nsr.networkInfos); if (nsr.scanStatus == NetworkScanResult.SCAN_STATUS_COMPLETE) { deleteScanAndMayNotify(nsri, NetworkScan.SUCCESS, true); nsri.mPhone.mCi.unregisterForNetworkScanResult(mHandler); } } else { if (nsr.networkInfos != null) { notifyMessenger(nsri, TelephonyScanManager.CALLBACK_SCAN_RESULTS, NetworkScan.SUCCESS, nsr.networkInfos); } deleteScanAndMayNotify(nsri, rilErrorToScanError(nsr.scanError), true); nsri.mPhone.mCi.unregisterForNetworkScanResult(mHandler); } } else { logEmptyResultOrException(ar); deleteScanAndMayNotify(nsri, NetworkScan.ERROR_RIL_ERROR, true); nsri.mPhone.mCi.unregisterForNetworkScanResult(mHandler); } } // Stops the scan if the scanId and uid match the mScanId and mUid. // If the scan to be stopped is the live scan, we only send the request to RIL, while the // mLiveRequestInfo will not be cleared and the user will not be notified either. // If the scan to be stopped is the pending scan, we will clear mPendingRequestInfo and // notify the user. private synchronized void doStopScan(int scanId) { if (mLiveRequestInfo != null && scanId == mLiveRequestInfo.mScanId) { mLiveRequestInfo.mPhone.stopNetworkScan( mHandler.obtainMessage(EVENT_STOP_NETWORK_SCAN_DONE, mLiveRequestInfo)); } else if (mPendingRequestInfo != null && scanId == mPendingRequestInfo.mScanId) { notifyMessenger(mPendingRequestInfo, TelephonyScanManager.CALLBACK_SCAN_COMPLETE, NetworkScan.SUCCESS, null); mPendingRequestInfo = null; } else { Log.e(TAG, "stopScan: scan " + scanId + " does not exist!"); } } private void stopScanDone(AsyncResult ar) { NetworkScanRequestInfo nsri = (NetworkScanRequestInfo) ar.userObj; if (nsri == null) { Log.e(TAG, "EVENT_STOP_NETWORK_SCAN_DONE: nsri is null"); return; } if (ar.exception == null && ar.result != null) { NetworkScanResult nsr = (NetworkScanResult) ar.result; deleteScanAndMayNotify(nsri, rilErrorToScanError(nsr.scanError), true); } else { logEmptyResultOrException(ar); deleteScanAndMayNotify(nsri, NetworkScan.ERROR_RIL_ERROR, true); } nsri.mPhone.mCi.unregisterForNetworkScanResult(mHandler); } // Interrupts the live scan is the scanId matches the mScanId of the mLiveRequestInfo. private synchronized void doInterruptScan(int scanId) { if (mLiveRequestInfo != null && scanId == mLiveRequestInfo.mScanId) { mLiveRequestInfo.mPhone.stopNetworkScan(mHandler.obtainMessage( EVENT_INTERRUPT_NETWORK_SCAN_DONE, mLiveRequestInfo)); } else { Log.e(TAG, "doInterruptScan: scan " + scanId + " does not exist!"); } } private void interruptScanDone(AsyncResult ar) { NetworkScanRequestInfo nsri = (NetworkScanRequestInfo) ar.userObj; if (nsri == null) { Log.e(TAG, "EVENT_INTERRUPT_NETWORK_SCAN_DONE: nsri is null"); return; } nsri.mPhone.mCi.unregisterForNetworkScanResult(mHandler); deleteScanAndMayNotify(nsri, 0, false); } // Interrupts the live scan and caches nsri in mPendingRequestInfo. Once the live scan is // stopped, a new scan will automatically start with nsri. // The new scan can interrupt the live scan only when all the below requirements are met: // 1. There is 1 live scan and no other pending scan // 2. The new scan is requested by system process // 3. The live scan is not requested by system process private synchronized boolean interruptLiveScan(NetworkScanRequestInfo nsri) { if (mLiveRequestInfo != null && mPendingRequestInfo == null && nsri.mUid == Process.SYSTEM_UID && mLiveRequestInfo.mUid != Process.SYSTEM_UID) { doInterruptScan(mLiveRequestInfo.mScanId); mPendingRequestInfo = nsri; notifyMessenger(mLiveRequestInfo, TelephonyScanManager.CALLBACK_SCAN_ERROR, NetworkScan.ERROR_INTERRUPTED, null); return true; } return false; } private boolean cacheScan(NetworkScanRequestInfo nsri) { // TODO(30954762): Cache periodic scan for OC-MR1. return false; } // Starts a new scan with nsri if there is no live scan running. private synchronized boolean startNewScan(NetworkScanRequestInfo nsri) { if (mLiveRequestInfo == null) { mLiveRequestInfo = nsri; nsri.mPhone.startNetworkScan(nsri.getRequest(), mHandler.obtainMessage(EVENT_START_NETWORK_SCAN_DONE, nsri)); return true; } return false; } // Deletes the mLiveRequestInfo and notify the user if it matches nsri. private synchronized void deleteScanAndMayNotify(NetworkScanRequestInfo nsri, int error, boolean notify) { if (mLiveRequestInfo != null && nsri.mScanId == mLiveRequestInfo.mScanId) { if (notify) { if (error == NetworkScan.SUCCESS) { notifyMessenger(nsri, TelephonyScanManager.CALLBACK_SCAN_COMPLETE, error, null); } else { notifyMessenger(nsri, TelephonyScanManager.CALLBACK_SCAN_ERROR, error, null); } } mLiveRequestInfo = null; if (mPendingRequestInfo != null) { startNewScan(mPendingRequestInfo); mPendingRequestInfo = null; } } } } /** * Interrupts an ongoing network scan * * This method is similar to stopNetworkScan, since they both stops an ongoing scan. The * difference is that stopNetworkScan is only used by the callers to stop their own scans, so * sanity check will be done to make sure the request is valid; while this method is only * internally used by NetworkScanRequestTracker so sanity check is not needed. */ private void interruptNetworkScan(int scanId) { // scanId will be stored at Message.arg1 mHandler.obtainMessage(CMD_INTERRUPT_NETWORK_SCAN, scanId, 0).sendToTarget(); } /** * Starts a new network scan * * This function only wraps all the incoming information and delegate then to the handler thread * which will actually handles the scan request. So a new scanId will always be generated and * returned to the user, no matter how this scan will be actually handled. */ public int startNetworkScan( NetworkScanRequest request, Messenger messenger, IBinder binder, Phone phone) { int scanId = mNextNetworkScanRequestId.getAndIncrement(); NetworkScanRequestInfo nsri = new NetworkScanRequestInfo(request, messenger, binder, scanId, phone); // nsri will be stored as Message.obj mHandler.obtainMessage(CMD_START_NETWORK_SCAN, nsri).sendToTarget(); return scanId; } /** * Stops an ongoing network scan * * The ongoing scan will be stopped only when the input scanId and caller's uid matches the * corresponding information associated with it. */ public void stopNetworkScan(int scanId) { synchronized (mScheduler) { if ((mScheduler.mLiveRequestInfo != null && scanId == mScheduler.mLiveRequestInfo.mScanId && Binder.getCallingUid() == mScheduler.mLiveRequestInfo.mUid) || (mScheduler.mPendingRequestInfo != null && scanId == mScheduler.mPendingRequestInfo.mScanId && Binder.getCallingUid() == mScheduler.mPendingRequestInfo.mUid)) { // scanId will be stored at Message.arg1 mHandler.obtainMessage(CMD_STOP_NETWORK_SCAN, scanId, 0).sendToTarget(); } else { throw new IllegalArgumentException("Scan with id: " + scanId + " does not exist!"); } } } } src/java/com/android/internal/telephony/PhoneInternalInterface.java +2 −2 Original line number Diff line number Diff line Loading @@ -24,7 +24,7 @@ import android.os.WorkSource; import android.telephony.CarrierConfigManager; import android.telephony.CellLocation; import android.telephony.ImsiEncryptionInfo; import android.telephony.PhoneStateListener; import android.telephony.NetworkScanRequest; import android.telephony.ServiceState; import com.android.internal.telephony.PhoneConstants.*; // ???? Loading Loading @@ -655,7 +655,7 @@ public interface PhoneInternalInterface { * on failure.</li> * </ul> */ void startNetworkScan(Message response); void startNetworkScan(NetworkScanRequest nsr, Message response); /** * Stop ongoing network scan. This method is asynchronous; . Loading src/java/com/android/internal/telephony/RIL.java +1 −2 Original line number Diff line number Diff line Loading @@ -1810,7 +1810,7 @@ public final class RIL extends BaseCommands implements CommandsInterface { } @Override public void startNetworkScan(Message result) { public void startNetworkScan(NetworkScanRequest nsr, Message result) { IRadio radioProxy = getRadioProxy(result); if (radioProxy != null) { android.hardware.radio.V1_1.IRadio radioProxy11 = Loading @@ -1822,7 +1822,6 @@ public final class RIL extends BaseCommands implements CommandsInterface { result.sendToTarget(); } } else { NetworkScanRequest nsr = (NetworkScanRequest) result.obj; android.hardware.radio.V1_1.NetworkScanRequest request = new android.hardware.radio.V1_1.NetworkScanRequest(); request.type = nsr.scanType; Loading Loading
src/java/com/android/internal/telephony/CommandsInterface.java +2 −1 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import android.os.Message; import android.os.WorkSource; import android.service.carrier.CarrierIdentifier; import android.telephony.ClientRequestStats; import android.telephony.NetworkScanRequest; import com.android.internal.telephony.cdma.CdmaSmsBroadcastConfigInfo; import com.android.internal.telephony.dataconnection.DataProfile; Loading Loading @@ -1293,7 +1294,7 @@ public interface CommandsInterface { * * ((AsyncResult)response.obj).result is a NetworkScanResult object */ void startNetworkScan(Message response); void startNetworkScan(NetworkScanRequest nsr, Message response); /** * Stops the ongoing network scan Loading
src/java/com/android/internal/telephony/GsmCdmaPhone.java +3 −2 Original line number Diff line number Diff line Loading @@ -56,6 +56,7 @@ import android.telecom.VideoProfile; import android.telephony.CarrierConfigManager; import android.telephony.CellLocation; import android.telephony.ImsiEncryptionInfo; import android.telephony.NetworkScanRequest; import android.telephony.PhoneNumberUtils; import android.telephony.Rlog; import android.telephony.ServiceState; Loading Loading @@ -1815,8 +1816,8 @@ public class GsmCdmaPhone extends Phone { } @Override public void startNetworkScan(Message response) { mCi.startNetworkScan(response); public void startNetworkScan(NetworkScanRequest nsr, Message response) { mCi.startNetworkScan(nsr, response); } @Override Loading
src/java/com/android/internal/telephony/NetworkScanRequestTracker.java 0 → 100644 +483 −0 Original line number Diff line number Diff line /* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.internal.telephony; import static android.telephony.RadioNetworkConstants.RadioAccessNetworks.EUTRAN; import static android.telephony.RadioNetworkConstants.RadioAccessNetworks.GERAN; import static android.telephony.RadioNetworkConstants.RadioAccessNetworks.UTRAN; import android.hardware.radio.V1_0.RadioError; import android.os.AsyncResult; import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.Messenger; import android.os.Process; import android.os.RemoteException; import android.telephony.NetworkScan; import android.telephony.NetworkScanRequest; import android.telephony.RadioAccessSpecifier; import android.telephony.TelephonyScanManager; import android.util.Log; import java.util.concurrent.atomic.AtomicInteger; /** * Manages radio access network scan requests. * * Provides methods to start and stop network scan requests, and keeps track of all the live scans. * * {@hide} */ public final class NetworkScanRequestTracker { private static final String TAG = "ScanRequestTracker"; private static final int CMD_START_NETWORK_SCAN = 1; private static final int EVENT_START_NETWORK_SCAN_DONE = 2; private static final int EVENT_RECEIVE_NETWORK_SCAN_RESULT = 3; private static final int CMD_STOP_NETWORK_SCAN = 4; private static final int EVENT_STOP_NETWORK_SCAN_DONE = 5; private static final int CMD_INTERRUPT_NETWORK_SCAN = 6; private static final int EVENT_INTERRUPT_NETWORK_SCAN_DONE = 7; private final Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case CMD_START_NETWORK_SCAN: mScheduler.doStartScan((NetworkScanRequestInfo) msg.obj); break; case EVENT_START_NETWORK_SCAN_DONE: mScheduler.startScanDone((AsyncResult) msg.obj); break; case EVENT_RECEIVE_NETWORK_SCAN_RESULT: mScheduler.receiveResult((AsyncResult) msg.obj); break; case CMD_STOP_NETWORK_SCAN: mScheduler.doStopScan(msg.arg1); break; case EVENT_STOP_NETWORK_SCAN_DONE: mScheduler.stopScanDone((AsyncResult) msg.obj); break; case CMD_INTERRUPT_NETWORK_SCAN: mScheduler.doInterruptScan(msg.arg1); break; case EVENT_INTERRUPT_NETWORK_SCAN_DONE: mScheduler.interruptScanDone((AsyncResult) msg.obj); break; } } }; // The sequence number of NetworkScanRequests private final AtomicInteger mNextNetworkScanRequestId = new AtomicInteger(1); private final NetworkScanRequestScheduler mScheduler = new NetworkScanRequestScheduler(); private void logEmptyResultOrException(AsyncResult ar) { if (ar.result == null) { Log.e(TAG, "NetworkScanResult: Empty result"); } else { Log.e(TAG, "NetworkScanResult: Exception: " + ar.exception); } } private boolean isValidScan(NetworkScanRequestInfo nsri) { if (nsri.mRequest.specifiers.length > NetworkScanRequest.MAX_RADIO_ACCESS_NETWORKS) { return false; } for (RadioAccessSpecifier ras : nsri.mRequest.specifiers) { if (ras.radioAccessNetwork != GERAN && ras.radioAccessNetwork != UTRAN && ras.radioAccessNetwork != EUTRAN) { return false; } if (ras.bands.length > NetworkScanRequest.MAX_BANDS || ras.channels.length > NetworkScanRequest.MAX_CHANNELS) { return false; } } return true; } /** Sends a message back to the application via its callback. */ private void notifyMessenger(NetworkScanRequestInfo nsri, int what, int err, Object result) { Messenger messenger = nsri.mMessenger; Message message = Message.obtain(); message.what = what; message.arg1 = err; message.arg2 = nsri.mScanId; message.obj = result; try { messenger.send(message); } catch (RemoteException e) { Log.e(TAG, "Exception in notifyMessenger: " + e); } } /** * Tracks info about the radio network scan. * * Also used to notice when the calling process dies so we can self-expire. */ class NetworkScanRequestInfo implements IBinder.DeathRecipient { private final NetworkScanRequest mRequest; private final Messenger mMessenger; private final IBinder mBinder; private final Phone mPhone; private final int mScanId; private final int mUid; private final int mPid; private boolean mIsBinderDead; NetworkScanRequestInfo(NetworkScanRequest r, Messenger m, IBinder b, int id, Phone phone) { super(); mRequest = r; mMessenger = m; mBinder = b; mScanId = id; mPhone = phone; mUid = Binder.getCallingUid(); mPid = Binder.getCallingPid(); mIsBinderDead = false; try { mBinder.linkToDeath(this, 0); } catch (RemoteException e) { binderDied(); } } synchronized void setIsBinderDead(boolean val) { mIsBinderDead = val; } synchronized boolean getIsBinderDead() { return mIsBinderDead; } NetworkScanRequest getRequest() { return mRequest; } void unlinkDeathRecipient() { if (mBinder != null) { mBinder.unlinkToDeath(this, 0); } } @Override public void binderDied() { Log.e(TAG, "PhoneInterfaceManager NetworkScanRequestInfo binderDied(" + mRequest + ", " + mBinder + ")"); setIsBinderDead(true); interruptNetworkScan(mScanId); } } /** * Handles multiplexing and scheduling for multiple requests. */ private class NetworkScanRequestScheduler { private NetworkScanRequestInfo mLiveRequestInfo; private NetworkScanRequestInfo mPendingRequestInfo; private int rilErrorToScanError(int rilError) { switch (rilError) { case RadioError.NONE: return NetworkScan.SUCCESS; case RadioError.RADIO_NOT_AVAILABLE: Log.e(TAG, "rilErrorToScanError: RADIO_NOT_AVAILABLE"); return NetworkScan.ERROR_MODEM_ERROR; case RadioError.REQUEST_NOT_SUPPORTED: Log.e(TAG, "rilErrorToScanError: REQUEST_NOT_SUPPORTED"); return NetworkScan.ERROR_UNSUPPORTED; case RadioError.NO_MEMORY: Log.e(TAG, "rilErrorToScanError: NO_MEMORY"); return NetworkScan.ERROR_MODEM_ERROR; case RadioError.INTERNAL_ERR: Log.e(TAG, "rilErrorToScanError: INTERNAL_ERR"); return NetworkScan.ERROR_MODEM_ERROR; case RadioError.MODEM_ERR: Log.e(TAG, "rilErrorToScanError: MODEM_ERR"); return NetworkScan.ERROR_MODEM_ERROR; case RadioError.OPERATION_NOT_ALLOWED: Log.e(TAG, "rilErrorToScanError: OPERATION_NOT_ALLOWED"); return NetworkScan.ERROR_MODEM_ERROR; case RadioError.INVALID_ARGUMENTS: Log.e(TAG, "rilErrorToScanError: INVALID_ARGUMENTS"); return NetworkScan.ERROR_INVALID_SCAN; case RadioError.DEVICE_IN_USE: Log.e(TAG, "rilErrorToScanError: DEVICE_IN_USE"); return NetworkScan.ERROR_MODEM_BUSY; default: Log.e(TAG, "rilErrorToScanError: Unexpected RadioError " + rilError); return NetworkScan.ERROR_RIL_ERROR; } } private void doStartScan(NetworkScanRequestInfo nsri) { if (nsri == null) { Log.e(TAG, "CMD_START_NETWORK_SCAN: nsri is null"); return; } if (!isValidScan(nsri)) { notifyMessenger(nsri, TelephonyScanManager.CALLBACK_SCAN_ERROR, NetworkScan.ERROR_INVALID_SCAN, null); return; } if (nsri.getIsBinderDead()) { Log.e(TAG, "CMD_START_NETWORK_SCAN: Binder has died"); return; } if (!startNewScan(nsri)) { if (!interruptLiveScan(nsri)) { if (!cacheScan(nsri)) { notifyMessenger(nsri, TelephonyScanManager.CALLBACK_SCAN_ERROR, NetworkScan.ERROR_MODEM_BUSY, null); } } } } private synchronized void startScanDone(AsyncResult ar) { NetworkScanRequestInfo nsri = (NetworkScanRequestInfo) ar.userObj; if (nsri == null) { Log.e(TAG, "EVENT_START_NETWORK_SCAN_DONE: nsri is null"); return; } if (mLiveRequestInfo == null || nsri.mScanId != mLiveRequestInfo.mScanId) { Log.e(TAG, "EVENT_START_NETWORK_SCAN_DONE: nsri does not match mLiveRequestInfo"); return; } if (ar.exception == null && ar.result != null) { NetworkScanResult nsr = (NetworkScanResult) ar.result; if (nsr.scanError == NetworkScan.SUCCESS) { // Register for the scan results if the scan started successfully. nsri.mPhone.mCi.registerForNetworkScanResult(mHandler, EVENT_RECEIVE_NETWORK_SCAN_RESULT, nsri); } else { deleteScanAndMayNotify(nsri, rilErrorToScanError(nsr.scanError), true); } } else { logEmptyResultOrException(ar); deleteScanAndMayNotify(nsri, NetworkScan.ERROR_RIL_ERROR, true); } } private void receiveResult(AsyncResult ar) { NetworkScanRequestInfo nsri = (NetworkScanRequestInfo) ar.userObj; if (nsri == null) { Log.e(TAG, "EVENT_RECEIVE_NETWORK_SCAN_RESULT: nsri is null"); return; } if (ar.exception == null && ar.result != null) { NetworkScanResult nsr = (NetworkScanResult) ar.result; if (nsr.scanError == NetworkScan.SUCCESS) { notifyMessenger(nsri, TelephonyScanManager.CALLBACK_SCAN_RESULTS, rilErrorToScanError(nsr.scanError), nsr.networkInfos); if (nsr.scanStatus == NetworkScanResult.SCAN_STATUS_COMPLETE) { deleteScanAndMayNotify(nsri, NetworkScan.SUCCESS, true); nsri.mPhone.mCi.unregisterForNetworkScanResult(mHandler); } } else { if (nsr.networkInfos != null) { notifyMessenger(nsri, TelephonyScanManager.CALLBACK_SCAN_RESULTS, NetworkScan.SUCCESS, nsr.networkInfos); } deleteScanAndMayNotify(nsri, rilErrorToScanError(nsr.scanError), true); nsri.mPhone.mCi.unregisterForNetworkScanResult(mHandler); } } else { logEmptyResultOrException(ar); deleteScanAndMayNotify(nsri, NetworkScan.ERROR_RIL_ERROR, true); nsri.mPhone.mCi.unregisterForNetworkScanResult(mHandler); } } // Stops the scan if the scanId and uid match the mScanId and mUid. // If the scan to be stopped is the live scan, we only send the request to RIL, while the // mLiveRequestInfo will not be cleared and the user will not be notified either. // If the scan to be stopped is the pending scan, we will clear mPendingRequestInfo and // notify the user. private synchronized void doStopScan(int scanId) { if (mLiveRequestInfo != null && scanId == mLiveRequestInfo.mScanId) { mLiveRequestInfo.mPhone.stopNetworkScan( mHandler.obtainMessage(EVENT_STOP_NETWORK_SCAN_DONE, mLiveRequestInfo)); } else if (mPendingRequestInfo != null && scanId == mPendingRequestInfo.mScanId) { notifyMessenger(mPendingRequestInfo, TelephonyScanManager.CALLBACK_SCAN_COMPLETE, NetworkScan.SUCCESS, null); mPendingRequestInfo = null; } else { Log.e(TAG, "stopScan: scan " + scanId + " does not exist!"); } } private void stopScanDone(AsyncResult ar) { NetworkScanRequestInfo nsri = (NetworkScanRequestInfo) ar.userObj; if (nsri == null) { Log.e(TAG, "EVENT_STOP_NETWORK_SCAN_DONE: nsri is null"); return; } if (ar.exception == null && ar.result != null) { NetworkScanResult nsr = (NetworkScanResult) ar.result; deleteScanAndMayNotify(nsri, rilErrorToScanError(nsr.scanError), true); } else { logEmptyResultOrException(ar); deleteScanAndMayNotify(nsri, NetworkScan.ERROR_RIL_ERROR, true); } nsri.mPhone.mCi.unregisterForNetworkScanResult(mHandler); } // Interrupts the live scan is the scanId matches the mScanId of the mLiveRequestInfo. private synchronized void doInterruptScan(int scanId) { if (mLiveRequestInfo != null && scanId == mLiveRequestInfo.mScanId) { mLiveRequestInfo.mPhone.stopNetworkScan(mHandler.obtainMessage( EVENT_INTERRUPT_NETWORK_SCAN_DONE, mLiveRequestInfo)); } else { Log.e(TAG, "doInterruptScan: scan " + scanId + " does not exist!"); } } private void interruptScanDone(AsyncResult ar) { NetworkScanRequestInfo nsri = (NetworkScanRequestInfo) ar.userObj; if (nsri == null) { Log.e(TAG, "EVENT_INTERRUPT_NETWORK_SCAN_DONE: nsri is null"); return; } nsri.mPhone.mCi.unregisterForNetworkScanResult(mHandler); deleteScanAndMayNotify(nsri, 0, false); } // Interrupts the live scan and caches nsri in mPendingRequestInfo. Once the live scan is // stopped, a new scan will automatically start with nsri. // The new scan can interrupt the live scan only when all the below requirements are met: // 1. There is 1 live scan and no other pending scan // 2. The new scan is requested by system process // 3. The live scan is not requested by system process private synchronized boolean interruptLiveScan(NetworkScanRequestInfo nsri) { if (mLiveRequestInfo != null && mPendingRequestInfo == null && nsri.mUid == Process.SYSTEM_UID && mLiveRequestInfo.mUid != Process.SYSTEM_UID) { doInterruptScan(mLiveRequestInfo.mScanId); mPendingRequestInfo = nsri; notifyMessenger(mLiveRequestInfo, TelephonyScanManager.CALLBACK_SCAN_ERROR, NetworkScan.ERROR_INTERRUPTED, null); return true; } return false; } private boolean cacheScan(NetworkScanRequestInfo nsri) { // TODO(30954762): Cache periodic scan for OC-MR1. return false; } // Starts a new scan with nsri if there is no live scan running. private synchronized boolean startNewScan(NetworkScanRequestInfo nsri) { if (mLiveRequestInfo == null) { mLiveRequestInfo = nsri; nsri.mPhone.startNetworkScan(nsri.getRequest(), mHandler.obtainMessage(EVENT_START_NETWORK_SCAN_DONE, nsri)); return true; } return false; } // Deletes the mLiveRequestInfo and notify the user if it matches nsri. private synchronized void deleteScanAndMayNotify(NetworkScanRequestInfo nsri, int error, boolean notify) { if (mLiveRequestInfo != null && nsri.mScanId == mLiveRequestInfo.mScanId) { if (notify) { if (error == NetworkScan.SUCCESS) { notifyMessenger(nsri, TelephonyScanManager.CALLBACK_SCAN_COMPLETE, error, null); } else { notifyMessenger(nsri, TelephonyScanManager.CALLBACK_SCAN_ERROR, error, null); } } mLiveRequestInfo = null; if (mPendingRequestInfo != null) { startNewScan(mPendingRequestInfo); mPendingRequestInfo = null; } } } } /** * Interrupts an ongoing network scan * * This method is similar to stopNetworkScan, since they both stops an ongoing scan. The * difference is that stopNetworkScan is only used by the callers to stop their own scans, so * sanity check will be done to make sure the request is valid; while this method is only * internally used by NetworkScanRequestTracker so sanity check is not needed. */ private void interruptNetworkScan(int scanId) { // scanId will be stored at Message.arg1 mHandler.obtainMessage(CMD_INTERRUPT_NETWORK_SCAN, scanId, 0).sendToTarget(); } /** * Starts a new network scan * * This function only wraps all the incoming information and delegate then to the handler thread * which will actually handles the scan request. So a new scanId will always be generated and * returned to the user, no matter how this scan will be actually handled. */ public int startNetworkScan( NetworkScanRequest request, Messenger messenger, IBinder binder, Phone phone) { int scanId = mNextNetworkScanRequestId.getAndIncrement(); NetworkScanRequestInfo nsri = new NetworkScanRequestInfo(request, messenger, binder, scanId, phone); // nsri will be stored as Message.obj mHandler.obtainMessage(CMD_START_NETWORK_SCAN, nsri).sendToTarget(); return scanId; } /** * Stops an ongoing network scan * * The ongoing scan will be stopped only when the input scanId and caller's uid matches the * corresponding information associated with it. */ public void stopNetworkScan(int scanId) { synchronized (mScheduler) { if ((mScheduler.mLiveRequestInfo != null && scanId == mScheduler.mLiveRequestInfo.mScanId && Binder.getCallingUid() == mScheduler.mLiveRequestInfo.mUid) || (mScheduler.mPendingRequestInfo != null && scanId == mScheduler.mPendingRequestInfo.mScanId && Binder.getCallingUid() == mScheduler.mPendingRequestInfo.mUid)) { // scanId will be stored at Message.arg1 mHandler.obtainMessage(CMD_STOP_NETWORK_SCAN, scanId, 0).sendToTarget(); } else { throw new IllegalArgumentException("Scan with id: " + scanId + " does not exist!"); } } } }
src/java/com/android/internal/telephony/PhoneInternalInterface.java +2 −2 Original line number Diff line number Diff line Loading @@ -24,7 +24,7 @@ import android.os.WorkSource; import android.telephony.CarrierConfigManager; import android.telephony.CellLocation; import android.telephony.ImsiEncryptionInfo; import android.telephony.PhoneStateListener; import android.telephony.NetworkScanRequest; import android.telephony.ServiceState; import com.android.internal.telephony.PhoneConstants.*; // ???? Loading Loading @@ -655,7 +655,7 @@ public interface PhoneInternalInterface { * on failure.</li> * </ul> */ void startNetworkScan(Message response); void startNetworkScan(NetworkScanRequest nsr, Message response); /** * Stop ongoing network scan. This method is asynchronous; . Loading
src/java/com/android/internal/telephony/RIL.java +1 −2 Original line number Diff line number Diff line Loading @@ -1810,7 +1810,7 @@ public final class RIL extends BaseCommands implements CommandsInterface { } @Override public void startNetworkScan(Message result) { public void startNetworkScan(NetworkScanRequest nsr, Message result) { IRadio radioProxy = getRadioProxy(result); if (radioProxy != null) { android.hardware.radio.V1_1.IRadio radioProxy11 = Loading @@ -1822,7 +1822,6 @@ public final class RIL extends BaseCommands implements CommandsInterface { result.sendToTarget(); } } else { NetworkScanRequest nsr = (NetworkScanRequest) result.obj; android.hardware.radio.V1_1.NetworkScanRequest request = new android.hardware.radio.V1_1.NetworkScanRequest(); request.type = nsr.scanType; Loading