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

Commit 5a8bb0a7 authored by Brad Ebinger's avatar Brad Ebinger
Browse files

Add new ImsException to better handle ImsService errors

The ImsService can crash unexpectantly or not be available
for certain device configurations. Define a checked exception
ImsException to handle these cases instead of using
RuntimeExceptions.

Bug: 122480210
Test: atest FrameworksTelephonyTests
Merged-In: Ie3221d56a235c0e037d71e197f4972df31faa09b
Change-Id: Ie3221d56a235c0e037d71e197f4972df31faa09b
parent c09b41ca
Loading
Loading
Loading
Loading
+15 −5
Original line number Diff line number Diff line
@@ -6740,6 +6740,16 @@ package android.telephony.ims {
    field public final java.util.HashMap<java.lang.String,android.os.Bundle> mParticipants;
  }
  public class ImsException extends java.lang.Exception {
    ctor public ImsException(@Nullable String);
    ctor public ImsException(@Nullable String, int);
    ctor public ImsException(@Nullable String, int, Throwable);
    method public int getCode();
    field public static final int CODE_ERROR_SERVICE_UNAVAILABLE = 1; // 0x1
    field public static final int CODE_ERROR_UNSPECIFIED = 0; // 0x0
    field public static final int CODE_ERROR_UNSUPPORTED_OPERATION = 2; // 0x2
  }
  public final class ImsExternalCallState implements android.os.Parcelable {
    ctor public ImsExternalCallState(String, android.net.Uri, android.net.Uri, boolean, int, int, boolean);
    method public int describeContents();
@@ -6757,7 +6767,7 @@ package android.telephony.ims {
  }
  public class ImsMmTelManager {
    method public static android.telephony.ims.ImsMmTelManager createForSubscriptionId(android.content.Context, int);
    method public static android.telephony.ims.ImsMmTelManager createForSubscriptionId(int);
    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getVoWiFiModeSetting();
    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getVoWiFiRoamingModeSetting();
    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isAdvancedCallingSettingEnabled();
@@ -6766,8 +6776,8 @@ package android.telephony.ims {
    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isVoWiFiRoamingSettingEnabled();
    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isVoWiFiSettingEnabled();
    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isVtSettingEnabled();
    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerImsRegistrationCallback(java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsMmTelManager.RegistrationCallback);
    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerMmTelCapabilityCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsMmTelManager.CapabilityCallback);
    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerImsRegistrationCallback(java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsMmTelManager.RegistrationCallback) throws android.telephony.ims.ImsException;
    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerMmTelCapabilityCallback(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.ims.ImsMmTelManager.CapabilityCallback) throws android.telephony.ims.ImsException;
    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setAdvancedCallingSetting(boolean);
    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setRttCapabilitySetting(boolean);
    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setVoWiFiModeSetting(int);
@@ -7202,11 +7212,11 @@ package android.telephony.ims {
  }
  public class ProvisioningManager {
    method public static android.telephony.ims.ProvisioningManager createForSubscriptionId(android.content.Context, int);
    method public static android.telephony.ims.ProvisioningManager createForSubscriptionId(int);
    method @WorkerThread @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getProvisioningIntValue(int);
    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean getProvisioningStatusForCapability(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int);
    method @WorkerThread @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String getProvisioningStringValue(int);
    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerProvisioningChangedCallback(java.util.concurrent.Executor, @NonNull android.telephony.ims.ProvisioningManager.Callback);
    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void registerProvisioningChangedCallback(java.util.concurrent.Executor, @NonNull android.telephony.ims.ProvisioningManager.Callback) throws android.telephony.ims.ImsException;
    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningIntValue(int, int);
    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setProvisioningStatusForCapability(@android.telephony.ims.feature.MmTelFeature.MmTelCapabilities.MmTelCapability int, int, boolean);
    method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public int setProvisioningStringValue(int, String);
+113 −0
Original line number Diff line number Diff line
/*
 * Copyright (c) 2019 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 android.telephony.ims;

import android.annotation.IntDef;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.text.TextUtils;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
 * This class defines an IMS-related exception that has been thrown while interacting with a
 * device or carrier provided ImsService implementation.
 * @hide
 */
@SystemApi
public class ImsException extends Exception {

    /**
     * The operation has failed due to an unknown or unspecified error.
     */
    public static final int CODE_ERROR_UNSPECIFIED = 0;
    /**
     * The operation has failed because there is no {@link ImsService} available to service it. This
     * may be due to an {@link ImsService} crash or other illegal state.
     * <p>
     * This is a temporary error and the operation may be retried until the connection to the
     * {@link ImsService} is restored.
     */
    public static final int CODE_ERROR_SERVICE_UNAVAILABLE = 1;

    /**
     * This device or carrier configuration does not support IMS for this subscription.
     * <p>
     * This is a permanent configuration error and there should be no retry.
     */
    public static final int CODE_ERROR_UNSUPPORTED_OPERATION = 2;

    /**@hide*/
    @Retention(RetentionPolicy.SOURCE)
    @IntDef(prefix = "CODE_ERROR_", value = {
            CODE_ERROR_UNSPECIFIED,
            CODE_ERROR_SERVICE_UNAVAILABLE,
            CODE_ERROR_UNSUPPORTED_OPERATION
    })
    public @interface ImsErrorCode {}

    private int mCode = CODE_ERROR_UNSPECIFIED;

    /**
     * A new {@link ImsException} with an unspecified {@link ImsErrorCode} code.
     * @param message an optional message to detail the error condition more specifically.
     */
    public ImsException(@Nullable String message) {
        super(getMessage(message, CODE_ERROR_UNSPECIFIED));
    }

    /**
     * A new {@link ImsException} that includes an {@link ImsErrorCode} error code.
     * @param message an optional message to detail the error condition more specifically.
     */
    public ImsException(@Nullable String message, @ImsErrorCode int code) {
        super(getMessage(message, code));
        mCode = code;
    }

    /**
     * A new {@link ImsException} that includes an {@link ImsErrorCode} error code and a
     * {@link Throwable} that contains the original error that was thrown to lead to this Exception.
     * @param message an optional message to detail the error condition more specifically.
     * @param cause the {@link Throwable} that caused this {@link ImsException} to be created.
     */
    public ImsException(@Nullable String message, @ImsErrorCode  int code, Throwable cause) {
        super(getMessage(message, code), cause);
        mCode = code;
    }

    /**
     * @return the IMS Error code that is associated with this {@link ImsException}.
     */
    public @ImsErrorCode int getCode() {
        return mCode;
    }

    private static String getMessage(String message, int code) {
        StringBuilder builder;
        if (!TextUtils.isEmpty(message)) {
            builder = new StringBuilder(message);
            builder.append(" (code: ");
            builder.append(code);
            builder.append(")");
            return builder.toString();
        } else {
            return "code: " + code;
        }
    }
}
+19 −24
Original line number Diff line number Diff line
@@ -54,7 +54,7 @@ import java.util.concurrent.Executor;
 * registration and MmTel capability status callbacks, as well as query/modify user settings for the
 * associated subscription.
 *
 * @see #createForSubscriptionId(Context, int)
 * @see #createForSubscriptionId(int)
 * @hide
 */
@SystemApi
@@ -315,15 +315,12 @@ public class ImsMmTelManager {
    /**
     * Create an instance of ImsManager for the subscription id specified.
     *
     * @param context The context to create this ImsMmTelManager instance within.
     * @param subId The ID of the subscription that this ImsMmTelManager will use.
     * @see android.telephony.SubscriptionManager#getActiveSubscriptionInfoList()
     * @throws IllegalArgumentException if the subscription is invalid or
     *         the subscription ID is not an active subscription.
     * @throws IllegalArgumentException if the subscription is invalid.
     */
    public static ImsMmTelManager createForSubscriptionId(Context context, int subId) {
        if (!SubscriptionManager.isValidSubscriptionId(subId)
                || !getSubscriptionManager(context).isActiveSubscriptionId(subId)) {
    public static ImsMmTelManager createForSubscriptionId(int subId) {
        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
            throw new IllegalArgumentException("Invalid subscription ID");
        }

@@ -331,7 +328,7 @@ public class ImsMmTelManager {
    }

    /**
     * Only visible for testing, use {@link #createForSubscriptionId(Context, int)} instead.
     * Only visible for testing, use {@link #createForSubscriptionId(int)} instead.
     * @hide
     */
    @VisibleForTesting
@@ -341,7 +338,7 @@ public class ImsMmTelManager {

    /**
     * Registers a {@link RegistrationCallback} with the system, which will provide registration
     * updates for the subscription specified in {@link #createForSubscriptionId(Context, int)}. Use
     * updates for the subscription specified in {@link #createForSubscriptionId(int)}. Use
     * {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to Subscription changed
     * events and call {@link #unregisterImsRegistrationCallback(RegistrationCallback)} to clean up.
     *
@@ -354,13 +351,14 @@ public class ImsMmTelManager {
     * @throws IllegalArgumentException if the subscription associated with this callback is not
     * active (SIM is not inserted, ESIM inactive) or invalid, or a null {@link Executor} or
     * {@link CapabilityCallback} callback.
     * @throws IllegalStateException if the subscription associated with this callback is valid, but
     * @throws ImsException if the subscription associated with this callback is valid, but
     * the {@link ImsService} associated with the subscription is not available. This can happen if
     * the service crashed, for example.
     * the service crashed, for example. See {@link ImsException#getCode()} for a more detailed
     * reason.
     */
    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
    public void registerImsRegistrationCallback(@CallbackExecutor Executor executor,
            @NonNull RegistrationCallback c) {
            @NonNull RegistrationCallback c) throws ImsException {
        if (c == null) {
            throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
        }
@@ -372,6 +370,8 @@ public class ImsMmTelManager {
            getITelephony().registerImsRegistrationCallback(mSubId, c.getBinder());
        } catch (RemoteException e) {
            throw e.rethrowAsRuntimeException();
        } catch (IllegalStateException e) {
            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
        }
    }

@@ -403,7 +403,7 @@ public class ImsMmTelManager {
    /**
     * Registers a {@link CapabilityCallback} with the system, which will provide MmTel service
     * availability updates for the subscription specified in
     * {@link #createForSubscriptionId(Context, int)}. The method {@link #isAvailable(int, int)}
     * {@link #createForSubscriptionId(int)}. The method {@link #isAvailable(int, int)}
     * can also be used to query this information at any time.
     *
     * Use {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to
@@ -419,13 +419,14 @@ public class ImsMmTelManager {
     * @throws IllegalArgumentException if the subscription associated with this callback is not
     * active (SIM is not inserted, ESIM inactive) or invalid, or a null {@link Executor} or
     * {@link CapabilityCallback} callback.
     * @throws IllegalStateException if the subscription associated with this callback is valid, but
     * @throws ImsException if the subscription associated with this callback is valid, but
     * the {@link ImsService} associated with the subscription is not available. This can happen if
     * the service crashed, for example.
     * the service crashed, for example. See {@link ImsException#getCode()} for a more detailed
     * reason.
     */
    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
    public void registerMmTelCapabilityCallback(@NonNull @CallbackExecutor Executor executor,
            @NonNull CapabilityCallback c) {
            @NonNull CapabilityCallback c) throws ImsException {
        if (c == null) {
            throw new IllegalArgumentException("Must include a non-null RegistrationCallback.");
        }
@@ -437,6 +438,8 @@ public class ImsMmTelManager {
            getITelephony().registerMmTelCapabilityCallback(mSubId, c.getBinder());
        } catch (RemoteException e) {
            throw e.rethrowAsRuntimeException();
        }  catch (IllegalStateException e) {
            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
        }
    }

@@ -796,14 +799,6 @@ public class ImsMmTelManager {
        }
    }

    private static SubscriptionManager getSubscriptionManager(Context context) {
        SubscriptionManager manager = context.getSystemService(SubscriptionManager.class);
        if (manager == null) {
            throw new RuntimeException("Could not find SubscriptionManager.");
        }
        return manager;
    }

    private static ITelephony getITelephony() {
        ITelephony binder = ITelephony.Stub.asInterface(
                ServiceManager.getService(Context.TELEPHONY_SERVICE));
+10 −17
Original line number Diff line number Diff line
@@ -172,15 +172,13 @@ public class ProvisioningManager {

    /**
     * Create a new {@link ProvisioningManager} for the subscription specified.
     * @param context The context that this manager will use.
     *
     * @param subId The ID of the subscription that this ProvisioningManager will use.
     * @see android.telephony.SubscriptionManager#getActiveSubscriptionInfoList()
     * @throws IllegalArgumentException if the subscription is invalid or
     *         the subscription ID is not an active subscription.
     * @throws IllegalArgumentException if the subscription is invalid.
     */
    public static ProvisioningManager createForSubscriptionId(Context context, int subId) {
        if (!SubscriptionManager.isValidSubscriptionId(subId)
                || !getSubscriptionManager(context).isActiveSubscriptionId(subId)) {
    public static ProvisioningManager createForSubscriptionId(int subId) {
        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
            throw new IllegalArgumentException("Invalid subscription ID");
        }

@@ -202,18 +200,21 @@ public class ProvisioningManager {
     * @see SubscriptionManager.OnSubscriptionsChangedListener
     * @throws IllegalArgumentException if the subscription associated with this callback is not
     * active (SIM is not inserted, ESIM inactive) or the subscription is invalid.
     * @throws IllegalStateException if the subscription associated with this callback is valid, but
     * @throws ImsException if the subscription associated with this callback is valid, but
     * the {@link ImsService} associated with the subscription is not available. This can happen if
     * the service crashed, for example.
     * the service crashed, for example. See {@link ImsException#getCode()} for a more detailed
     * reason.
     */
    @RequiresPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
    public void registerProvisioningChangedCallback(@CallbackExecutor Executor executor,
            @NonNull Callback callback) {
            @NonNull Callback callback) throws ImsException {
        callback.setExecutor(executor);
        try {
            getITelephony().registerImsProvisioningChangedCallback(mSubId, callback.getBinder());
        } catch (RemoteException e) {
            throw e.rethrowAsRuntimeException();
        }  catch (IllegalStateException e) {
            throw new ImsException(e.getMessage(), ImsException.CODE_ERROR_SERVICE_UNAVAILABLE);
        }
    }

@@ -369,14 +370,6 @@ public class ProvisioningManager {
        }
    }

    private static SubscriptionManager getSubscriptionManager(Context context) {
        SubscriptionManager manager = context.getSystemService(SubscriptionManager.class);
        if (manager == null) {
            throw new RuntimeException("Could not find SubscriptionManager.");
        }
        return manager;
    }

    private static ITelephony getITelephony() {
        ITelephony binder = ITelephony.Stub.asInterface(
                ServiceManager.getService(Context.TELEPHONY_SERVICE));
+2 −0
Original line number Diff line number Diff line
@@ -21,8 +21,10 @@ import android.telephony.ims.ImsReasonInfo;
/**
 * This class defines a general IMS-related exception.
 *
 * @deprecated Use {@link android.telephony.ims.ImsException} instead.
 * @hide
 */
@Deprecated
public class ImsException extends Exception {

    /**