diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 572101c11bd30..be84032df9a47 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -13398,6 +13398,7 @@ package android.telephony { method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public android.telephony.PinResult changeIccLockPin(@NonNull String, @NonNull String); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int checkCarrierPrivilegesForPackage(String); method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int checkCarrierPrivilegesForPackageAnyPhone(String); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void clearRadioPowerOffForReason(int); method public void dial(String); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean disableDataConnectivity(); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean enableDataConnectivity(); @@ -13443,6 +13444,7 @@ package android.telephony { method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public String[] getMergedImsisFromGroup(); method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.PhoneCapability getPhoneCapability(); method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public long getPreferredNetworkTypeBitmask(); + method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.Set getRadioPowerOffReasons(); method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public int getRadioPowerState(); method public int getSimApplicationState(); method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getSimApplicationState(int); @@ -13497,6 +13499,7 @@ package android.telephony { method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.MODIFY_PHONE_STATE}) public void requestCellInfoUpdate(@NonNull android.os.WorkSource, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CellInfoCallback); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void requestModemActivityInfo(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void requestNumberVerification(@NonNull android.telephony.PhoneNumberRange, long, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.NumberVerificationCallback); + method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void requestRadioPowerOffForReason(int); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void resetAllCarrierActions(); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void resetCarrierKeysForImsiEncryption(); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @WorkerThread public void resetIms(int); @@ -13521,9 +13524,9 @@ package android.telephony { method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setNrDualConnectivityState(int); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setOpportunisticNetworkState(boolean); method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setPreferredNetworkTypeBitmask(long); - method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setRadio(boolean); - method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setRadioEnabled(boolean); - method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setRadioPower(boolean); + method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setRadio(boolean); + method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setRadioEnabled(boolean); + method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setRadioPower(boolean); method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setSimPowerState(int); method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setSimPowerState(int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer); method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setSimPowerStateForSlot(int, int); @@ -13605,6 +13608,10 @@ package android.telephony { field public static final int PREPARE_UNATTENDED_REBOOT_SUCCESS = 0; // 0x0 field public static final int RADIO_POWER_OFF = 0; // 0x0 field public static final int RADIO_POWER_ON = 1; // 0x1 + field public static final int RADIO_POWER_REASON_CARRIER = 2; // 0x2 + field public static final int RADIO_POWER_REASON_NEARBY_DEVICE = 3; // 0x3 + field public static final int RADIO_POWER_REASON_THERMAL = 1; // 0x1 + field public static final int RADIO_POWER_REASON_USER = 0; // 0x0 field public static final int RADIO_POWER_UNAVAILABLE = 2; // 0x2 field public static final int SET_CARRIER_RESTRICTION_ERROR = 2; // 0x2 field public static final int SET_CARRIER_RESTRICTION_NOT_SUPPORTED = 1; // 0x1 diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 789399205f23a..2453f3cafaaa1 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -140,6 +140,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; @@ -234,6 +235,54 @@ public class TelephonyManager { public static final int NETWORK_SELECTION_MODE_AUTO = 1; public static final int NETWORK_SELECTION_MODE_MANUAL = 2; + /** + * Reasons for Radio being powered off. + * + * @hide + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = {"RADIO_POWER_REASON_"}, + value = { + RADIO_POWER_REASON_USER, + RADIO_POWER_REASON_THERMAL, + RADIO_POWER_REASON_CARRIER, + RADIO_POWER_REASON_NEARBY_DEVICE}) + public @interface RadioPowerReason {} + + /** + * This reason is used when users want to turn off radio, e.g., users turn on airplane mode. + * + * @hide + */ + @SystemApi + public static final int RADIO_POWER_REASON_USER = 0; + /** + * This reason is used when radio needs to be turned off due to thermal. + * + * @hide + */ + @SystemApi + public static final int RADIO_POWER_REASON_THERMAL = 1; + /** + * This reason is used when carriers want to turn off radio. A privileged app can request to + * turn off radio via the system service + * {@link com.android.carrierdefaultapp.CaptivePortalLoginActivity}, which subsequently calls + * the system APIs {@link requestRadioPowerOffForReason} and + * {@link clearRadioPowerOffForReason}. + * + * @hide + */ + @SystemApi + public static final int RADIO_POWER_REASON_CARRIER = 2; + /** + * Used to reduce power on a battery-constrained device when Telephony services are available + * via a paired device which is nearby. + * + * @hide + */ + @SystemApi + public static final int RADIO_POWER_REASON_NEARBY_DEVICE = 3; + /** The otaspMode passed to PhoneStateListener#onOtaspChanged */ /** @hide */ static public final int OTASP_UNINITIALIZED = 0; @@ -10506,34 +10555,155 @@ public class TelephonyManager { } } - /** @hide */ + /** + * @deprecated - use the APIs {@link requestRadioPowerOffForReason} and + * {@link clearRadioPowerOffForReason}. + * + * @hide + */ + @Deprecated @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) public boolean setRadio(boolean turnOn) { + boolean result = true; try { - ITelephony telephony = getITelephony(); - if (telephony != null) - return telephony.setRadio(turnOn); - } catch (RemoteException e) { - Log.e(TAG, "Error calling ITelephony#setRadio", e); + if (turnOn) { + clearRadioPowerOffForReason(RADIO_POWER_REASON_USER); + } else { + requestRadioPowerOffForReason(RADIO_POWER_REASON_USER); + } + } catch (Exception e) { + String calledFunction = + turnOn ? "clearRadioPowerOffForReason" : "requestRadioPowerOffForReason"; + Log.e(TAG, "Error calling " + calledFunction, e); + result = false; } - return false; + return result; } - /** @hide */ + /** + * @deprecated - use the APIs {@link requestRadioPowerOffForReason} and + * {@link clearRadioPowerOffForReason}. + * + * @hide + */ + @Deprecated @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) public boolean setRadioPower(boolean turnOn) { + boolean result = true; + try { + if (turnOn) { + clearRadioPowerOffForReason(RADIO_POWER_REASON_USER); + } else { + requestRadioPowerOffForReason(RADIO_POWER_REASON_USER); + } + } catch (Exception e) { + String calledFunction = + turnOn ? "clearRadioPowerOffForReason" : "requestRadioPowerOffForReason"; + Log.e(TAG, "Error calling " + calledFunction, e); + result = false; + } + return result; + } + + /** + * Vote on powering off the radio for a reason. The radio will be turned on only when there is + * no reason to power it off. When any of the voters want to power it off, it will be turned + * off. In case of emergency, the radio will be turned on even if there are some reasons for + * powering it off, and these radio off votes will be cleared. + * Multiple apps can vote for the same reason and the last vote will take effect. Each app is + * responsible for its vote. A powering-off vote of a reason will be maintained until it is + * cleared by calling {@link clearRadioPowerOffForReason} for that reason, or an emergency call + * is made, or the device is rebooted. When an app comes backup from a crash, it needs to make + * sure if its vote is as expected. An app can use the API {@link getRadioPowerOffReasons} to + * check its vote. + * + * @param reason The reason for powering off radio. + * @throws SecurityException if the caller does not have MODIFY_PHONE_STATE permission. + * @throws IllegalStateException if the Telephony service is not currently available. + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + public void requestRadioPowerOffForReason(@RadioPowerReason int reason) { try { ITelephony telephony = getITelephony(); - if (telephony != null) - return telephony.setRadioPower(turnOn); + if (telephony != null) { + if (!telephony.requestRadioPowerOffForReason(getSubId(), reason)) { + throw new IllegalStateException("Telephony service is not available."); + } + } else { + throw new IllegalStateException("Telephony service is null."); + } } catch (RemoteException e) { - Log.e(TAG, "Error calling ITelephony#setRadioPower", e); + Log.e(TAG, "Error calling ITelephony#requestRadioPowerOffForReason", e); + e.rethrowAsRuntimeException(); } - return false; + } + + /** + * Remove the vote on powering off the radio for a reason, as requested by + * {@link requestRadioPowerOffForReason}. + * + * @param reason The reason for powering off radio. + * @throws SecurityException if the caller does not have MODIFY_PHONE_STATE permission. + * @throws IllegalStateException if the Telephony service is not currently available. + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + public void clearRadioPowerOffForReason(@RadioPowerReason int reason) { + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + if (!telephony.clearRadioPowerOffForReason(getSubId(), reason)) { + throw new IllegalStateException("Telephony service is not available."); + } + } else { + throw new IllegalStateException("Telephony service is null."); + } + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#clearRadioPowerOffForReason", e); + e.rethrowAsRuntimeException(); + } + } + + /** + * Get reasons for powering off radio, as requested by {@link requestRadioPowerOffForReason}. + * If the reason set is empty, the radio is on in all cases. + * + * @return Set of reasons for powering off radio. + * @throws SecurityException if the caller does not have READ_PRIVILEGED_PHONE_STATE permission. + * @throws IllegalStateException if the Telephony service is not currently available. + * + * @hide + */ + @SystemApi + @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) + @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) + @NonNull + public Set getRadioPowerOffReasons() { + Set result = new HashSet<>(); + try { + ITelephony telephony = getITelephony(); + if (telephony != null) { + result.addAll(telephony.getRadioPowerOffReasons(getSubId(), + mContext.getOpPackageName(), mContext.getAttributionTag())); + } else { + throw new IllegalStateException("Telephony service is null."); + } + } catch (RemoteException e) { + Log.e(TAG, "Error calling ITelephony#getRadioPowerOffReasons", e); + e.rethrowAsRuntimeException(); + } + return result; } /** @@ -13037,20 +13207,21 @@ public class TelephonyManager { * * @param enabled control enable or disable radio. * @see #resetAllCarrierActions() + * + * @deprecated - use the APIs {@link requestRadioPowerOffForReason} and + * {@link clearRadioPowerOffForReason}. + * * @hide */ + @Deprecated @SystemApi @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) @RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS) public void setRadioEnabled(boolean enabled) { - try { - ITelephony service = getITelephony(); - if (service != null) { - service.carrierActionSetRadioEnabled( - getSubId(SubscriptionManager.getDefaultDataSubscriptionId()), enabled); - } - } catch (RemoteException e) { - Log.e(TAG, "Error calling ITelephony#carrierActionSetRadioEnabled", e); + if (enabled) { + clearRadioPowerOffForReason(RADIO_POWER_REASON_CARRIER); + } else { + requestRadioPowerOffForReason(RADIO_POWER_REASON_CARRIER); } } diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index 0ce6b14ce3b06..42cac6630506e 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -243,6 +243,44 @@ interface ITelephony { */ boolean setRadioPower(boolean turnOn); + /** + * Vote on powering off the radio for a reason. The radio will be turned on only when there is + * no reason to power it off. When any of the voters want to power it off, it will be turned + * off. In case of emergency, the radio will be turned on even if there are some reasons for + * powering it off, and these radio off votes will be cleared. + * Multiple apps can vote for the same reason and the last vote will take effect. Each app is + * responsible for its vote. A powering-off vote of a reason will be maintained until it is + * cleared by calling {@link clearRadioPowerOffForReason} for that reason, or an emergency call + * is made, or the device is rebooted. When an app comes backup from a crash, it needs to make + * sure if its vote is as expected. An app can use the API {@link getRadioPowerOffReasons} to + * check its vote. + * + * @param subId The subscription ID. + * @param reason The reason for powering off radio. + * @return true on success and false on failure. + */ + boolean requestRadioPowerOffForReason(int subId, int reason); + + /** + * Remove the vote on powering off the radio for a reasonas, requested by + * {@link requestRadioPowerOffForReason}. + * + * @param subId The subscription ID. + * @param reason The reason for powering off radio. + * @return true on success and false on failure. + */ + boolean clearRadioPowerOffForReason(int subId, int reason); + + /** + * Get reasons for powering off radio, as requested by {@link requestRadioPowerOffForReason}. + * + * @param subId The subscription ID. + * @param callingPackage The package making the call. + * @param callingFeatureId The feature in the package. + * @return List of reasons for powering off radio. + */ + List getRadioPowerOffReasons(int subId, String callingPackage, String callingFeatureId); + /** * This method has been removed due to security and stability issues. */