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

Commit 8c23380d authored by Nathan Harold's avatar Nathan Harold
Browse files

Ensure Async APIs receive callbacks

There are currently a couple silent failure for
APIs that are defined as asynchronous.
-If the remote binder object is null then in some cases
 the response is dropped. It's important that the response
 be triggered on a thread other than the calling one due
 to the oddball possibility of non-reentrant locking.
-If the remote dies while awaiting a response, the RemoteException
 is generally dropped, and because the API is asynchronous there is
 no way for the caller to know, leaving a dangling async request.

This CL ensures that remote exceptions due to process death invoke
the asynchronous callbacks, delivering an error message using the
BackgroundThread to ensure that the calls are received on a thread
other than the original caller's.

Bug: 182185642
Test: atest CtsTelephonyTestCases
Change-Id: I5121734194f07c53f94fb6585da3c90e3e2c4bf7
parent 01a61615
Loading
Loading
Loading
Loading
+73 −67
Original line number Diff line number Diff line
@@ -99,6 +99,7 @@ import android.util.Pair;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BackgroundThread;
import com.android.internal.telephony.CellNetworkScanResult;
import com.android.internal.telephony.IBooleanConsumer;
import com.android.internal.telephony.ICallForwardingInfoCallback;
@@ -128,6 +129,7 @@ import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.Executor;
import java.util.concurrent.RejectedExecutionException;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
@@ -416,6 +418,27 @@ public class TelephonyManager {
        return Process.myUid() == Process.SYSTEM_UID;
    }
    /**
     * Post a runnable to the BackgroundThread.
     *
     * Used to invoke user callbacks without calling into the caller's executor from the caller's
     * calling thread context, for example to provide asynchronous error information that is
     * generated locally (not over a binder thread).
     *
     * <p>This is not necessary unless you are invoking caller's code asynchronously from within
     * the caller's thread context.
     *
     * @param r a runnable.
     */
    private static void runOnBackgroundThread(@NonNull Runnable r) {
        try {
            BackgroundThread.getExecutor().execute(r);
        } catch (RejectedExecutionException e) {
            throw new IllegalStateException(
                    "Failed to post a callback from the caller's thread context.", e);
        }
    }
    /**
     * Returns the multi SIM variant
     * Returns DSDS for Dual SIM Dual Standby
@@ -5912,7 +5935,8 @@ public class TelephonyManager {
            @NonNull @CallbackExecutor Executor executor, @NonNull CellInfoCallback callback) {
        try {
            ITelephony telephony = getITelephony();
            if (telephony == null) return;
            if (telephony == null) throw new IllegalStateException("Telephony is null");
            telephony.requestCellInfoUpdate(
                    getSubId(),
                    new ICellInfoCallback.Stub() {
@@ -5939,6 +5963,8 @@ public class TelephonyManager {
                        }
                    }, getOpPackageName(), getAttributionTag());
        } catch (RemoteException ex) {
            runOnBackgroundThread(() -> executor.execute(
                    () -> callback.onError(CellInfoCallback.ERROR_MODEM_ERROR, ex)));
        }
    }
@@ -5966,7 +5992,7 @@ public class TelephonyManager {
            @NonNull @CallbackExecutor Executor executor, @NonNull CellInfoCallback callback) {
        try {
            ITelephony telephony = getITelephony();
            if (telephony == null) return;
            if (telephony == null) throw new IllegalStateException("Telephony is null");
            telephony.requestCellInfoUpdateWithWorkSource(
                    getSubId(),
                    new ICellInfoCallback.Stub() {
@@ -5994,6 +6020,8 @@ public class TelephonyManager {
                        }
                    }, getOpPackageName(), getAttributionTag(), workSource);
        } catch (RemoteException ex) {
            runOnBackgroundThread(() -> executor.execute(
                    () -> callback.onError(CellInfoCallback.ERROR_MODEM_ERROR, ex)));
        }
    }
@@ -6960,14 +6988,15 @@ public class TelephonyManager {
        try {
            ITelephony telephony = getITelephony();
            if (telephony != null) {
            if (telephony == null) throw new IllegalStateException("Telephony is null");
            telephony.requestNumberVerification(range, timeoutMillis, internalCallback,
                    getOpPackageName());
            }
        } catch (RemoteException ex) {
            Rlog.e(TAG, "requestNumberVerification RemoteException", ex);
            executor.execute(() ->
                    callback.onVerificationFailed(NumberVerificationCallback.REASON_UNSPECIFIED));
            runOnBackgroundThread(() -> executor.execute(
                    () -> callback.onVerificationFailed(
                            NumberVerificationCallback.REASON_UNSPECIFIED)));
        }
    }
@@ -10333,6 +10362,8 @@ public class TelephonyManager {
        }
        try {
            ITelephony telephony = getITelephony();
            if (telephony == null) throw new IllegalStateException("Telephony is null.");
            IIntegerConsumer internalCallback = new IIntegerConsumer.Stub() {
                @Override
                public void accept(int result) {
@@ -10340,11 +10371,11 @@ public class TelephonyManager {
                            Binder.withCleanCallingIdentity(() -> callback.accept(result)));
                }
            };
            if (telephony != null) {
            telephony.setSimPowerStateForSlotWithCallback(slotIndex, state, internalCallback);
            }
        } catch (RemoteException e) {
            Log.e(TAG, "Error calling ITelephony#setSimPowerStateForSlot", e);
            runOnBackgroundThread(() -> executor.execute(
                    () -> callback.accept(SET_SIM_POWER_STATE_MODEM_ERROR)));
        } catch (SecurityException e) {
            Log.e(TAG, "Permission error calling ITelephony#setSimPowerStateForSlot",
                    e);
@@ -12774,22 +12805,7 @@ public class TelephonyManager {
        try {
            IOns iOpportunisticNetworkService = getIOns();
            if (iOpportunisticNetworkService == null) {
                if (executor == null || callback == null) {
                    return;
                }
                final long identity = Binder.clearCallingIdentity();
                try {
                    executor.execute(() -> {
                        if (Compatibility.isChangeEnabled(CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) {
                            callback.accept(SET_OPPORTUNISTIC_SUB_REMOTE_SERVICE_EXCEPTION);
                        } else {
                            callback.accept(SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION);
                        }
                    });
                } finally {
                    Binder.restoreCallingIdentity(identity);
                }
                return;
                throw new IllegalStateException("Opportunistic Network Service is null");
            }
            ISetOpportunisticDataCallback callbackStub = new ISetOpportunisticDataCallback.Stub() {
                @Override
@@ -12812,10 +12828,19 @@ public class TelephonyManager {
                    .setPreferredDataSubscriptionId(subId, needValidation, callbackStub,
                            pkgForDebug);
        } catch (RemoteException ex) {
            Rlog.e(TAG, "setPreferredDataSubscriptionId RemoteException", ex);
        }
            Rlog.e(TAG, "setPreferredOpportunisticDataSubscription RemoteException", ex);
            if (executor == null || callback == null) {
                return;
            }
            runOnBackgroundThread(() -> executor.execute(() -> {
                if (Compatibility.isChangeEnabled(CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) {
                    callback.accept(SET_OPPORTUNISTIC_SUB_REMOTE_SERVICE_EXCEPTION);
                } else {
                    callback.accept(SET_OPPORTUNISTIC_SUB_INACTIVE_SUBSCRIPTION);
                }
            }));
        }
    }
    /**
     * Get preferred opportunistic data subscription Id
@@ -12871,37 +12896,13 @@ public class TelephonyManager {
            @Nullable @CallbackExecutor Executor executor,
            @UpdateAvailableNetworksResult @Nullable Consumer<Integer> callback) {
        String pkgForDebug = mContext != null ? mContext.getOpPackageName() : "<unknown>";
        Objects.requireNonNull(availableNetworks, "availableNetworks must not be null.");
        try {
            IOns iOpportunisticNetworkService = getIOns();
            if (iOpportunisticNetworkService == null || availableNetworks == null) {
                if (executor == null || callback == null) {
                    return;
                }
            if (iOpportunisticNetworkService == null) {
                    final long identity = Binder.clearCallingIdentity();
                    try {
                        executor.execute(() -> {
                            if (Compatibility.isChangeEnabled(CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) {
                                callback.accept(UPDATE_AVAILABLE_NETWORKS_REMOTE_SERVICE_EXCEPTION);
                            } else {
                                callback.accept(UPDATE_AVAILABLE_NETWORKS_UNKNOWN_FAILURE);
                            }
                        });
                    } finally {
                        Binder.restoreCallingIdentity(identity);
                    }
                } else {
                    final long identity = Binder.clearCallingIdentity();
                    try {
                        executor.execute(() -> {
                            callback.accept(UPDATE_AVAILABLE_NETWORKS_INVALID_ARGUMENTS);
                        });
                    } finally {
                        Binder.restoreCallingIdentity(identity);
                    }
                }
                return;
                throw new IllegalStateException("Opportunistic Network Service is null");
            }
            IUpdateAvailableNetworksCallback callbackStub =
                    new IUpdateAvailableNetworksCallback.Stub() {
                        @Override
@@ -12909,20 +12910,25 @@ public class TelephonyManager {
                            if (executor == null || callback == null) {
                                return;
                            }
                            final long identity = Binder.clearCallingIdentity();
                            try {
                                executor.execute(() -> {
                                    callback.accept(result);
                            Binder.withCleanCallingIdentity(() -> {
                                executor.execute(() -> callback.accept(result));
                            });
                            } finally {
                                Binder.restoreCallingIdentity(identity);
                            }
                        }
                    };
            iOpportunisticNetworkService.updateAvailableNetworks(availableNetworks, callbackStub,
                    pkgForDebug);
            iOpportunisticNetworkService
                    .updateAvailableNetworks(availableNetworks, callbackStub, pkgForDebug);
        } catch (RemoteException ex) {
            Rlog.e(TAG, "updateAvailableNetworks RemoteException", ex);
            if (executor == null || callback == null) {
                return;
            }
            runOnBackgroundThread(() -> executor.execute(() -> {
                if (Compatibility.isChangeEnabled(CALLBACK_ON_MORE_ERROR_CODE_CHANGE)) {
                    callback.accept(UPDATE_AVAILABLE_NETWORKS_REMOTE_SERVICE_EXCEPTION);
                } else {
                    callback.accept(UPDATE_AVAILABLE_NETWORKS_UNKNOWN_FAILURE);
                }
            }));
        }
    }