Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e0d956e1 authored by yinxu's avatar yinxu
Browse files

Add the NetworkScanRequestTracker

NetworkScanRequestTracker is part of the new network scan API
implementaion, which stores all the live and pending network scans
and handles the multiplexing of multiple scans.

Test: Telephony sanity tests
Bug: 30954762

Merged-in: I2fd9825f0a5ce3a2fd341cf8d2d9480af81716bd
Change-Id: I2fd9825f0a5ce3a2fd341cf8d2d9480af81716bd
(cherry picked from commit 84de7198)
parent 2a096a92
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -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;
@@ -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
+3 −2
Original line number Diff line number Diff line
@@ -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;
@@ -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
+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!");
            }
        }
    }
}
+2 −2
Original line number Diff line number Diff line
@@ -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.*; // ????
@@ -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; .
+1 −2
Original line number Diff line number Diff line
@@ -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 =
@@ -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