Update Telephony APIs for Cell Broadcast

Bug: 256043136
Test: atest CtsTelephonyTestCases
Test: manual
Change-Id: Ib1b149c5902c1615b6f1aa118c6f809c0486ff33
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index b3cbba2..bdf20f1 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -13080,6 +13080,17 @@
     method @NonNull public java.util.List<android.telephony.CbGeoUtils.LatLng> getVertices();
   }
 
+  public final class CellBroadcastIdRange implements android.os.Parcelable {
+    ctor public CellBroadcastIdRange(int, int, int, boolean) throws java.lang.IllegalArgumentException;
+    method public int describeContents();
+    method public int getEndId();
+    method public int getStartId();
+    method public int getType();
+    method public boolean isEnabled();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CellBroadcastIdRange> CREATOR;
+  }
+
   public class CellBroadcastIntents {
     method public static void sendSmsCbReceivedBroadcast(@NonNull android.content.Context, @Nullable android.os.UserHandle, @NonNull android.telephony.SmsCbMessage, @Nullable android.content.BroadcastReceiver, @Nullable android.os.Handler, int, int);
     field public static final String ACTION_AREA_INFO_UPDATED = "android.telephony.action.AREA_INFO_UPDATED";
@@ -13639,10 +13650,10 @@
   }
 
   public final class SmsManager {
-    method public boolean disableCellBroadcastRange(int, int, int);
-    method public boolean enableCellBroadcastRange(int, int, int);
+    method @Deprecated public boolean disableCellBroadcastRange(int, int, int);
+    method @Deprecated public boolean enableCellBroadcastRange(int, int, int);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getPremiumSmsConsent(@NonNull String);
-    method @RequiresPermission(android.Manifest.permission.MODIFY_CELL_BROADCASTS) public void resetAllCellBroadcastRanges();
+    method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_CELL_BROADCASTS) public void resetAllCellBroadcastRanges();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void sendMultipartTextMessageWithoutPersisting(String, String, java.util.List<java.lang.String>, java.util.List<android.app.PendingIntent>, java.util.List<android.app.PendingIntent>);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setPremiumSmsConsent(@NonNull String, int);
     field public static final int PREMIUM_SMS_CONSENT_ALWAYS_ALLOW = 3; // 0x3
@@ -13847,6 +13858,7 @@
     method public String getCdmaPrlVersion();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCdmaRoamingMode();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getCdmaSubscriptionMode();
+    method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_CELL_BROADCASTS) public java.util.List<android.telephony.CellBroadcastIdRange> getCellBroadcastIdRanges();
     method public int getCurrentPhoneType();
     method public int getCurrentPhoneType(int);
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getDataActivationState();
@@ -13935,6 +13947,7 @@
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setCarrierRestrictionRules(@NonNull android.telephony.CarrierRestrictionRules);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCdmaRoamingMode(int);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCdmaSubscriptionMode(int);
+    method @RequiresPermission(android.Manifest.permission.MODIFY_CELL_BROADCASTS) public void setCellBroadcastIdRanges(@NonNull java.util.List<android.telephony.CellBroadcastIdRange>, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataActivationState(int);
     method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(int, boolean);
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataRoamingEnabled(boolean);
@@ -13998,6 +14011,11 @@
     field public static final int CDMA_SUBSCRIPTION_NV = 1; // 0x1
     field public static final int CDMA_SUBSCRIPTION_RUIM_SIM = 0; // 0x0
     field public static final int CDMA_SUBSCRIPTION_UNKNOWN = -1; // 0xffffffff
+    field public static final int CELLBROADCAST_RESULT_FAIL_ACTIVATION = 3; // 0x3
+    field public static final int CELLBROADCAST_RESULT_FAIL_CONFIG = 2; // 0x2
+    field public static final int CELLBROADCAST_RESULT_SUCCESS = 0; // 0x0
+    field public static final int CELLBROADCAST_RESULT_UNKNOWN = -1; // 0xffffffff
+    field public static final int CELLBROADCAST_RESULT_UNSUPPORTED = 1; // 0x1
     field public static final int ENABLE_NR_DUAL_CONNECTIVITY_INVALID_STATE = 4; // 0x4
     field public static final int ENABLE_NR_DUAL_CONNECTIVITY_NOT_SUPPORTED = 1; // 0x1
     field public static final int ENABLE_NR_DUAL_CONNECTIVITY_RADIO_ERROR = 3; // 0x3
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 4109425..1069445 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -501,6 +501,7 @@
         <permission name="android.permission.CAPTURE_MEDIA_OUTPUT" />
         <permission name="android.permission.CAPTURE_TUNER_AUDIO_INPUT" />
         <permission name="android.permission.CAPTURE_VOICE_COMMUNICATION_OUTPUT" />
+        <permission name="android.permission.MODIFY_CELL_BROADCASTS" />
     </privapp-permissions>
 
     <privapp-permissions package="com.android.statementservice">
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 47794b8..682774e 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -701,6 +701,7 @@
 
     <!-- Permission required for CTS test - CtsTelephonyTestCases -->
     <uses-permission android:name="android.permission.BIND_TELECOM_CONNECTION_SERVICE" />
+    <uses-permission android:name="android.permission.MODIFY_CELL_BROADCASTS" />
 
     <!-- Permission required for CTS test - CtsPersistentDataBlockManagerTestCases -->
     <uses-permission android:name="android.permission.ACCESS_PDB_STATE" />
diff --git a/telephony/java/android/telephony/CellBroadcastIdRange.aidl b/telephony/java/android/telephony/CellBroadcastIdRange.aidl
new file mode 100644
index 0000000..ddaceff
--- /dev/null
+++ b/telephony/java/android/telephony/CellBroadcastIdRange.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2022, 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;
+
+parcelable CellBroadcastIdRange;
diff --git a/telephony/java/android/telephony/CellBroadcastIdRange.java b/telephony/java/android/telephony/CellBroadcastIdRange.java
new file mode 100644
index 0000000..eaf4f1c
--- /dev/null
+++ b/telephony/java/android/telephony/CellBroadcastIdRange.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2022 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;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Describes a particular cell broadcast message identifier range.
+ * @hide
+ */
+@SystemApi
+public final class CellBroadcastIdRange implements Parcelable {
+
+    private int mStartId;
+    private int mEndId;
+    private int mType;
+    private boolean mIsEnabled;
+
+    /**
+     * Create a new CellBroacastRange
+     *
+     * @param startId first message identifier as specified in TS 23.041 (3GPP)
+     * or C.R1001-G (3GPP2)
+     * @param endId last message identifier as specified in TS 23.041 (3GPP)
+     * or C.R1001-G (3GPP2)
+     * @param type the message format as defined in {@link SmsCbMessage}
+     * @param isEnabled whether the range is enabled
+     *
+     * @throws IllegalArgumentException if endId < startId or invalid value
+     */
+    public CellBroadcastIdRange(int startId, int endId,
+            @android.telephony.SmsCbMessage.MessageFormat int type, boolean isEnabled)
+            throws IllegalArgumentException {
+        if (startId < 0 || endId < 0) {
+            throw new IllegalArgumentException("invalid id");
+        }
+        if (endId < startId) {
+            throw new IllegalArgumentException("endId must be greater than or equal to startId");
+        }
+        mStartId = startId;
+        mEndId = endId;
+        mType = type;
+        mIsEnabled = isEnabled;
+    }
+
+    /**
+     * Return the first message identifier of this range as specified in TS 23.041 (3GPP)
+     * or C.R1001-G (3GPP2)
+     */
+    public int getStartId() {
+        return mStartId;
+    }
+
+    /**
+     * Return the last message identifier of this range as specified in TS 23.041 (3GPP)
+     * or C.R1001-G (3GPP2)
+     */
+    public int getEndId() {
+        return mEndId;
+    }
+
+    /**
+     * Return the message format of this range as defined in {@link SmsCbMessage}
+     */
+    public @android.telephony.SmsCbMessage.MessageFormat int getType() {
+        return mType;
+    }
+
+    /**
+     * Return whether the range is enabled
+     */
+    public boolean isEnabled() {
+        return mIsEnabled;
+    }
+
+    /**
+     * {@link Parcelable#writeToParcel}
+     */
+    @Override
+    public void writeToParcel(@NonNull Parcel out, int flags) {
+        out.writeInt(mStartId);
+        out.writeInt(mEndId);
+        out.writeInt(mType);
+        out.writeBoolean(mIsEnabled);
+    }
+
+    /**
+     * {@link Parcelable.Creator}
+     *
+     */
+    public static final @NonNull Parcelable.Creator<CellBroadcastIdRange> CREATOR =
+            new Creator<CellBroadcastIdRange>() {
+                @NonNull
+                @Override
+                public CellBroadcastIdRange createFromParcel(Parcel in) {
+                    int startId = in.readInt();
+                    int endId = in.readInt();
+                    int type = in.readInt();
+                    boolean isEnabled = in.readBoolean();
+
+                    return new CellBroadcastIdRange(startId, endId, type, isEnabled);
+                }
+
+                @NonNull
+                @Override
+                public CellBroadcastIdRange[] newArray(int size) {
+                    return new CellBroadcastIdRange[size];
+                }
+            };
+
+    /**
+     * {@link Parcelable#describeContents}
+     */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mStartId, mEndId, mType, mIsEnabled);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof CellBroadcastIdRange)) {
+            return false;
+        }
+
+        CellBroadcastIdRange other = (CellBroadcastIdRange) obj;
+
+        return mStartId == other.mStartId && mEndId == other.mEndId && mType == other.mType
+                && mIsEnabled == other.mIsEnabled;
+    }
+
+    @Override
+    public String toString() {
+        return "CellBroadcastIdRange[" + mStartId + ", " + mEndId + ", " + mType + ", "
+                + mIsEnabled + "]";
+    }
+}
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index d1f19ee..8106819 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -1914,8 +1914,10 @@
      * @see #disableCellBroadcastRange(int, int, int)
      *
      * @throws IllegalArgumentException if endMessageId < startMessageId
+     * @deprecated Use {@link TelephonyManager#setCellBroadcastRanges} instead.
      * {@hide}
      */
+    @Deprecated
     @SystemApi
     public boolean enableCellBroadcastRange(int startMessageId, int endMessageId,
             @android.telephony.SmsCbMessage.MessageFormat int ranType) {
@@ -1974,8 +1976,10 @@
      * @see #enableCellBroadcastRange(int, int, int)
      *
      * @throws IllegalArgumentException if endMessageId < startMessageId
+     * @deprecated Use {@link TelephonyManager#setCellBroadcastRanges} instead.
      * {@hide}
      */
+    @Deprecated
     @SystemApi
     public boolean disableCellBroadcastRange(int startMessageId, int endMessageId,
             @android.telephony.SmsCbMessage.MessageFormat int ranType) {
@@ -3374,8 +3378,10 @@
 
     /**
      * Reset all cell broadcast ranges. Previously enabled ranges will become invalid after this.
+     * @deprecated Use {@link TelephonyManager#resetAllCellBroadcastRanges} instead
      * @hide
      */
+    @Deprecated
     @SystemApi
     @RequiresPermission(android.Manifest.permission.MODIFY_CELL_BROADCASTS)
     public void resetAllCellBroadcastRanges() {
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index c926a23..950b26c 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -17821,4 +17821,115 @@
             ex.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * Get current cell broadcast message identifier ranges.
+     *
+     * @throws SecurityException if the caller does not have the required permission
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_CELL_BROADCASTS)
+    @NonNull
+    public List<CellBroadcastIdRange> getCellBroadcastIdRanges() {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                return telephony.getCellBroadcastIdRanges(getSubId());
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            ex.rethrowFromSystemServer();
+        }
+        return new ArrayList<>();
+    }
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"CELLBROADCAST_RESULT_"}, value = {
+            CELLBROADCAST_RESULT_UNKNOWN,
+            CELLBROADCAST_RESULT_SUCCESS,
+            CELLBROADCAST_RESULT_UNSUPPORTED,
+            CELLBROADCAST_RESULT_FAIL_CONFIG,
+            CELLBROADCAST_RESULT_FAIL_ACTIVATION})
+    public @interface CellBroadcastResult {}
+
+    /**
+     * The result of the cell broadcast request is unknown
+     * @hide
+     */
+    @SystemApi
+    public static final int CELLBROADCAST_RESULT_UNKNOWN = -1;
+
+    /**
+     * The cell broadcast request is successful.
+     * @hide
+     */
+    @SystemApi
+    public static final int CELLBROADCAST_RESULT_SUCCESS = 0;
+
+    /**
+     * The cell broadcast request is not supported.
+     * @hide
+     */
+    @SystemApi
+    public static final int CELLBROADCAST_RESULT_UNSUPPORTED = 1;
+
+    /**
+     * The cell broadcast request is failed due to the error to set config
+     * @hide
+     */
+    @SystemApi
+    public static final int CELLBROADCAST_RESULT_FAIL_CONFIG = 2;
+
+    /**
+     * The cell broadcast request is failed due to the error to set activation
+     * @hide
+     */
+    @SystemApi
+    public static final int CELLBROADCAST_RESULT_FAIL_ACTIVATION = 3;
+
+    /**
+     * Set reception of cell broadcast messages with the list of the given ranges
+     *
+     * <p>The ranges set previously will be overridden by the new one. Empty list
+     * can be used to clear the ranges.
+     *
+     * @param ranges the list of {@link CellBroadcastIdRange} to be set.
+     * @param executor The {@link Executor} that will be used to call the callback.
+     * @param callback A callback called on the supplied {@link Executor} to notify
+     * the result when the operation completes.
+     * @throws SecurityException if the caller does not have the required permission
+     * @throws IllegalArgumentException when the ranges are invalid.
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.MODIFY_CELL_BROADCASTS)
+    public void setCellBroadcastIdRanges(@NonNull List<CellBroadcastIdRange> ranges,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull Consumer<Integer> callback) {
+        IIntegerConsumer consumer = callback == null ? null : new IIntegerConsumer.Stub() {
+            @Override
+            public void accept(int result) {
+                final long identity = Binder.clearCallingIdentity();
+                try {
+                    executor.execute(() -> callback.accept(result));
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
+            }
+        };
+
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony != null) {
+                telephony.setCellBroadcastIdRanges(getSubId(), ranges, consumer);
+            } else {
+                throw new IllegalStateException("telephony service is null.");
+            }
+        } catch (RemoteException ex) {
+            ex.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 9445d076..33da454 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -35,6 +35,7 @@
 import android.telephony.CarrierRestrictionRules;
 import android.telephony.CellIdentity;
 import android.telephony.CellInfo;
+import android.telephony.CellBroadcastIdRange;
 import android.telephony.ClientRequestStats;
 import android.telephony.ThermalMitigationRequest;
 import android.telephony.gba.UaSecurityProtocolIdentifier;
@@ -2658,4 +2659,15 @@
     * @hide
     */
     boolean isNullCipherAndIntegrityPreferenceEnabled();
+
+    /**
+     * Get current broadcast ranges.
+     */
+    List<CellBroadcastIdRange> getCellBroadcastIdRanges(int subId);
+
+    /**
+     * Set reception of cell broadcast messages with the list of the given ranges
+     */
+    void setCellBroadcastIdRanges(int subId, in List<CellBroadcastIdRange> ranges,
+            IIntegerConsumer callback);
 }
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsBroadcastConfigInfo.java b/telephony/java/com/android/internal/telephony/gsm/SmsBroadcastConfigInfo.java
index f4f4036..b70d548 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsBroadcastConfigInfo.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsBroadcastConfigInfo.java
@@ -16,6 +16,8 @@
 
 package com.android.internal.telephony.gsm;
 
+import java.util.Objects;
+
 /**
  * SmsBroadcastConfigInfo defines one configuration of Cell Broadcast
  * Message (CBM) to be received by the ME
@@ -130,4 +132,24 @@
                 mFromCodeScheme + ',' + mToCodeScheme + "] " +
             (mSelected ? "ENABLED" : "DISABLED");
     }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mFromServiceId, mToServiceId,
+                mFromCodeScheme, mToCodeScheme, mSelected);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof SmsBroadcastConfigInfo)) {
+            return false;
+        }
+
+        SmsBroadcastConfigInfo other = (SmsBroadcastConfigInfo) obj;
+
+        return mFromServiceId == other.mFromServiceId
+                && mToServiceId == other.mToServiceId
+                && mFromCodeScheme == other.mFromCodeScheme
+                && mToCodeScheme == other.mToCodeScheme && mSelected == other.mSelected;
+    }
 }