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

Commit e9d38721 authored by Nathan Harold's avatar Nathan Harold
Browse files

Abort NetworkScans when Phone Process Crashes

If the phone process crashes during a network scan, today
the scan hangs indefinitely. This CL adds a binder death
recipient to the wakefulness binder so that if the phone
process crashes, we detect it and cancel the scan.

Because there's no error code for "Telephony crashed" today,
the closest error code is to say that the modem is unavailable.

In addition, fix an issue where onError() did not actually remove
scans from the list of cached scans. This left dangling scan
objects in the cache after an error.

Bug: 155853346
Test: manual -
      1) start scan
      2) crash phone process
      3) verify error is returned to scan request
Change-Id: I8bd3823805fcc68623a685848517f2d11555e9c7
parent 55941995
Loading
Loading
Loading
Loading
+67 −17
Original line number Original line Diff line number Diff line
@@ -31,6 +31,7 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceManager;
import android.util.SparseArray;
import android.util.SparseArray;


import com.android.internal.annotations.GuardedBy;
import com.android.internal.telephony.ITelephony;
import com.android.internal.telephony.ITelephony;
import com.android.telephony.Rlog;
import com.android.telephony.Rlog;


@@ -56,6 +57,8 @@ public final class TelephonyScanManager {
    public static final int CALLBACK_SCAN_COMPLETE = 3;
    public static final int CALLBACK_SCAN_COMPLETE = 3;
    /** @hide */
    /** @hide */
    public static final int CALLBACK_RESTRICTED_SCAN_RESULTS = 4;
    public static final int CALLBACK_RESTRICTED_SCAN_RESULTS = 4;
    /** @hide */
    public static final int CALLBACK_TELEPHONY_DIED = 5;


    /** @hide */
    /** @hide */
    public static final int INVALID_SCAN_ID = -1;
    public static final int INVALID_SCAN_ID = -1;
@@ -104,17 +107,44 @@ public final class TelephonyScanManager {
    }
    }


    private final Looper mLooper;
    private final Looper mLooper;
    private final Handler mHandler;
    private final Messenger mMessenger;
    private final Messenger mMessenger;
    private final SparseArray<NetworkScanInfo> mScanInfo = new SparseArray<NetworkScanInfo>();
    private final SparseArray<NetworkScanInfo> mScanInfo = new SparseArray<NetworkScanInfo>();
    private final Binder.DeathRecipient mDeathRecipient;


    public TelephonyScanManager() {
    public TelephonyScanManager() {
        HandlerThread thread = new HandlerThread(TAG);
        HandlerThread thread = new HandlerThread(TAG);
        thread.start();
        thread.start();
        mLooper = thread.getLooper();
        mLooper = thread.getLooper();
        mMessenger = new Messenger(new Handler(mLooper) {
        mHandler = new Handler(mLooper) {
            @Override
            @Override
            public void handleMessage(Message message) {
            public void handleMessage(Message message) {
                checkNotNull(message, "message cannot be null");
                checkNotNull(message, "message cannot be null");
                if (message.what == CALLBACK_TELEPHONY_DIED) {
                    // If there are no objects in mScanInfo then binder death will simply return.
                    synchronized (mScanInfo) {
                        for (int i = 0; i < mScanInfo.size(); i++) {
                            NetworkScanInfo nsi = mScanInfo.valueAt(i);
                            // At this point we go into panic mode and ignore errors that would
                            // normally stop the show in order to try and clean up as gracefully
                            // as possible.
                            if (nsi == null) continue; // shouldn't be possible
                            Executor e = nsi.mExecutor;
                            NetworkScanCallback cb = nsi.mCallback;
                            if (e == null || cb == null) continue;
                            try {
                                e.execute(
                                        () -> cb.onError(NetworkScan.ERROR_MODEM_UNAVAILABLE));
                            } catch (java.util.concurrent.RejectedExecutionException ignore) {
                                // ignore so that we can continue
                            }
                        }

                        mScanInfo.clear();
                    }
                    return;
                }

                NetworkScanInfo nsi;
                NetworkScanInfo nsi;
                synchronized (mScanInfo) {
                synchronized (mScanInfo) {
                    nsi = mScanInfo.get(message.arg2);
                    nsi = mScanInfo.get(message.arg2);
@@ -159,6 +189,9 @@ public final class TelephonyScanManager {
                                Rlog.d(TAG, "onError: " + errorCode);
                                Rlog.d(TAG, "onError: " + errorCode);
                                callback.onError(errorCode);
                                callback.onError(errorCode);
                            });
                            });
                            synchronized (mScanInfo) {
                                mScanInfo.remove(message.arg2);
                            }
                        } catch (Exception e) {
                        } catch (Exception e) {
                            Rlog.e(TAG, "Exception in networkscan callback onError", e);
                            Rlog.e(TAG, "Exception in networkscan callback onError", e);
                        }
                        }
@@ -169,7 +202,9 @@ public final class TelephonyScanManager {
                                Rlog.d(TAG, "onComplete");
                                Rlog.d(TAG, "onComplete");
                                callback.onComplete();
                                callback.onComplete();
                            });
                            });
                            synchronized (mScanInfo) {
                                mScanInfo.remove(message.arg2);
                                mScanInfo.remove(message.arg2);
                            }
                        } catch (Exception e) {
                        } catch (Exception e) {
                            Rlog.e(TAG, "Exception in networkscan callback onComplete", e);
                            Rlog.e(TAG, "Exception in networkscan callback onComplete", e);
                        }
                        }
@@ -179,7 +214,14 @@ public final class TelephonyScanManager {
                        break;
                        break;
                }
                }
            }
            }
        });
        };
        mMessenger = new Messenger(mHandler);
        mDeathRecipient = new Binder.DeathRecipient() {
            @Override
            public void binderDied() {
                mHandler.obtainMessage(CALLBACK_TELEPHONY_DIED).sendToTarget();
            }
        };
    }
    }


    /**
    /**
@@ -190,7 +232,7 @@ public final class TelephonyScanManager {
     *
     *
     * <p>
     * <p>
     * Requires Permission:
     * Requires Permission:
     * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} and
     * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} and
     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
     *   {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE}
     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
     * Or the calling app has carrier privileges. @see #hasCarrierPrivileges
     *
     *
@@ -203,9 +245,9 @@ public final class TelephonyScanManager {
            NetworkScanRequest request, Executor executor, NetworkScanCallback callback,
            NetworkScanRequest request, Executor executor, NetworkScanCallback callback,
            String callingPackage, String callingFeatureId) {
            String callingPackage, String callingFeatureId) {
        try {
        try {
            ITelephony telephony = getITelephony();
            final ITelephony telephony = getITelephony();
            if (telephony != null) {
            if (telephony == null) return null;
                synchronized (mScanInfo) {

            int scanId = telephony.requestNetworkScan(
            int scanId = telephony.requestNetworkScan(
                    subId, request, mMessenger, new Binder(), callingPackage,
                    subId, request, mMessenger, new Binder(), callingPackage,
                    callingFeatureId);
                    callingFeatureId);
@@ -213,10 +255,17 @@ public final class TelephonyScanManager {
                Rlog.e(TAG, "Failed to initiate network scan");
                Rlog.e(TAG, "Failed to initiate network scan");
                return null;
                return null;
            }
            }
            synchronized (mScanInfo) {
                // We link to death whenever a scan is started to ensure that we are linked
                // at the point that phone process death might matter.
                // We never unlink because:
                // - Duplicate links to death with the same callback do not result in
                //   extraneous callbacks (the tracking de-dupes).
                // - Receiving binderDeath() when no scans are active is a no-op.
                telephony.asBinder().linkToDeath(mDeathRecipient, 0);
                saveScanInfo(scanId, request, executor, callback);
                saveScanInfo(scanId, request, executor, callback);
                return new NetworkScan(scanId, subId);
                return new NetworkScan(scanId, subId);
            }
            }
            }
        } catch (RemoteException ex) {
        } catch (RemoteException ex) {
            Rlog.e(TAG, "requestNetworkScan RemoteException", ex);
            Rlog.e(TAG, "requestNetworkScan RemoteException", ex);
        } catch (NullPointerException ex) {
        } catch (NullPointerException ex) {
@@ -225,6 +274,7 @@ public final class TelephonyScanManager {
        return null;
        return null;
    }
    }


    @GuardedBy("mScanInfo")
    private void saveScanInfo(
    private void saveScanInfo(
            int id, NetworkScanRequest request, Executor executor, NetworkScanCallback callback) {
            int id, NetworkScanRequest request, Executor executor, NetworkScanCallback callback) {
        mScanInfo.put(id, new NetworkScanInfo(request, executor, callback));
        mScanInfo.put(id, new NetworkScanInfo(request, executor, callback));