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

Commit 5314036b authored by Hall Liu's avatar Hall Liu
Browse files

Add API scaffolding for number verification

Add the PhoneNumberRange and NumberVerificationCallback classes. Add a
method in TelephonyManager to activate the API, but it does nothing for
now.

Bug: 119675160
Test: todo
Change-Id: I3ccd62b47f02a3aa324b675fdb16c8e7a1e9feec
parent bfd3237e
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -544,6 +544,7 @@ java_defaults {
        "telephony/java/com/android/internal/telephony/IApnSourceService.aidl",
        "telephony/java/com/android/internal/telephony/ICarrierConfigLoader.aidl",
        "telephony/java/com/android/internal/telephony/IMms.aidl",
        "telephony/java/com/android/internal/telephony/INumberVerificationCallback.aidl",
        "telephony/java/com/android/internal/telephony/IOnSubscriptionsChangedListener.aidl",
        "telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl",
        "telephony/java/com/android/internal/telephony/IPhoneSubInfo.aidl",
+23 −1
Original line number Diff line number Diff line
@@ -4466,8 +4466,8 @@ package android.service.carrier {

  public abstract class ApnService extends android.app.Service {
    ctor public ApnService();
    method public abstract java.util.List<android.content.ContentValues> onRestoreApns(int);
    method public android.os.IBinder onBind(android.content.Intent);
    method public abstract java.util.List<android.content.ContentValues> onRestoreApns(int);
  }

}
@@ -5181,6 +5181,26 @@ package android.telephony {
    field public static final int RESULT_SUCCESS = 0; // 0x0
  }

  public abstract interface NumberVerificationCallback {
    method public default void onCallReceived(java.lang.String);
    method public default void onVerificationFailed(int);
    field public static final int REASON_CONCURRENT_REQUESTS = 4; // 0x4
    field public static final int REASON_IN_ECBM = 5; // 0x5
    field public static final int REASON_IN_EMERGENCY_CALL = 6; // 0x6
    field public static final int REASON_NETWORK_NOT_AVAILABLE = 2; // 0x2
    field public static final int REASON_TIMED_OUT = 1; // 0x1
    field public static final int REASON_TOO_MANY_CALLS = 3; // 0x3
    field public static final int REASON_UNSPECIFIED = 0; // 0x0
  }

  public final class PhoneNumberRange implements android.os.Parcelable {
    ctor public PhoneNumberRange(java.lang.String, java.lang.String, java.lang.String, java.lang.String);
    method public int describeContents();
    method public boolean matches(java.lang.String);
    method public void writeToParcel(android.os.Parcel, int);
    field public static final android.os.Parcelable.Creator<android.telephony.PhoneNumberRange> CREATOR;
  }

  public class PhoneStateListener {
    method public void onRadioPowerStateChanged(int);
    method public void onSrvccStateChanged(int);
@@ -5345,6 +5365,7 @@ package android.telephony {
    method public boolean needsOtaServiceProvisioning();
    method public boolean rebootRadio();
    method public void requestCellInfoUpdate(android.os.WorkSource, java.util.concurrent.Executor, android.telephony.TelephonyManager.CellInfoCallback);
    method public void requestNumberVerification(android.telephony.PhoneNumberRange, long, java.util.concurrent.Executor, android.telephony.NumberVerificationCallback);
    method public boolean resetRadioConfig();
    method public int setAllowedCarriers(int, java.util.List<android.service.carrier.CarrierIdentifier>);
    method public void setCarrierDataEnabled(boolean);
@@ -5374,6 +5395,7 @@ package android.telephony {
    field public static final java.lang.String EXTRA_SIM_STATE = "android.telephony.extra.SIM_STATE";
    field public static final java.lang.String EXTRA_VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL = "android.telephony.extra.VISUAL_VOICEMAIL_ENABLED_BY_USER_BOOL";
    field public static final java.lang.String EXTRA_VOICEMAIL_SCRAMBLED_PIN_STRING = "android.telephony.extra.VOICEMAIL_SCRAMBLED_PIN_STRING";
    field public static final long MAX_NUMBER_VERIFICATION_TIMEOUT_MILLIS = 60000L; // 0xea60L
    field public static final int NETWORK_MODE_CDMA_EVDO = 4; // 0x4
    field public static final int NETWORK_MODE_CDMA_NO_EVDO = 5; // 0x5
    field public static final int NETWORK_MODE_EVDO_NO_CDMA = 6; // 0x6
+88 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 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.IntDef;
import android.annotation.NonNull;
import android.annotation.SystemApi;

/**
 * A callback for number verification. After a request for number verification is received,
 * the system will call {@link #onCallReceived(String)} if a phone call was received from a number
 * matching the provided {@link PhoneNumberRange} or it will call {@link #onVerificationFailed(int)}
 * if an error occurs.
 * @hide
 */
@SystemApi
public interface NumberVerificationCallback {
    /** @hide */
    @IntDef(value = {REASON_UNSPECIFIED, REASON_TIMED_OUT, REASON_NETWORK_NOT_AVAILABLE,
            REASON_TOO_MANY_CALLS, REASON_CONCURRENT_REQUESTS, REASON_IN_ECBM,
            REASON_IN_EMERGENCY_CALL},
            prefix = {"REASON_"})
    @interface NumberVerificationFailureReason {}

    /**
     * Verification failed for an unspecified reason.
     */
    int REASON_UNSPECIFIED = 0;

    /**
     * Verification failed because no phone call was received from a matching number within the
     * provided timeout.
     */
    int REASON_TIMED_OUT = 1;

    /**
     * Verification failed because no cellular voice network is available.
     */
    int REASON_NETWORK_NOT_AVAILABLE = 2;

    /**
     * Verification failed because there are currently too many ongoing phone calls for a new
     * incoming phone call to be received.
     */
    int REASON_TOO_MANY_CALLS = 3;

    /**
     * Verification failed because a previous request for verification has not yet completed.
     */
    int REASON_CONCURRENT_REQUESTS = 4;

    /**
     * Verification failed because the phone is in emergency callback mode.
     */
    int REASON_IN_ECBM = 5;

    /**
     * Verification failed because the phone is currently in an emergency call.
     */
    int REASON_IN_EMERGENCY_CALL = 6;

    /**
     * Called when the device receives a phone call from the provided {@link PhoneNumberRange}.
     * @param phoneNumber The phone number within the range that called. May or may not contain the
     *                    country code, but will be entirely numeric.
     */
    default void onCallReceived(@NonNull String phoneNumber) { }

    /**
     * Called when verification fails for some reason.
     * @param reason The reason for failure.
     */
    default void onVerificationFailed(@NumberVerificationFailureReason int reason) { }
}
+19 −0
Original line number Diff line number Diff line
/*
 * Copyright 2018 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 PhoneNumberRange;
+176 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 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 android.util.Log;

import java.util.Objects;
import java.util.regex.Pattern;

/**
 * This class is used to represent a range of phone numbers. Each range corresponds to a contiguous
 * block of phone numbers.
 *
 * Example:
 * {@code
 * {
 *     mCountryCode = "1"
 *     mPrefix = "650555"
 *     mLowerBound = "0055"
 *     mUpperBound = "0899"
 * }
 * }
 * would match 16505550089 and 6505550472, but not 63827593759 or 16505550900
 * @hide
 */
@SystemApi
public final class PhoneNumberRange implements Parcelable {
    public static final Creator<PhoneNumberRange> CREATOR = new Creator<PhoneNumberRange>() {
        @Override
        public PhoneNumberRange createFromParcel(Parcel in) {
            return new PhoneNumberRange(in);
        }

        @Override
        public PhoneNumberRange[] newArray(int size) {
            return new PhoneNumberRange[size];
        }
    };

    private final String mCountryCode;
    private final String mPrefix;
    private final String mLowerBound;
    private final String mUpperBound;

    /**
     * @param countryCode The country code, omitting the leading "+"
     * @param prefix A prefix that all numbers matching the range must have.
     * @param lowerBound When concatenated with the prefix, represents the lower bound of phone
     *                   numbers that match this range.
     * @param upperBound When concatenated with the prefix, represents the upper bound of phone
     *                   numbers that match this range.
     */
    public PhoneNumberRange(@NonNull String countryCode, @NonNull String prefix,
            @NonNull String lowerBound, @NonNull String upperBound) {
        validateLowerAndUpperBounds(lowerBound, upperBound);
        if (!Pattern.matches("[0-9]+", countryCode)) {
            throw new IllegalArgumentException("Country code must be all numeric");
        }
        if (!Pattern.matches("[0-9]+", prefix)) {
            throw new IllegalArgumentException("Prefix must be all numeric");
        }
        mCountryCode = countryCode;
        mPrefix = prefix;
        mLowerBound = lowerBound;
        mUpperBound = upperBound;
    }

    private PhoneNumberRange(Parcel in) {
        mCountryCode = in.readStringNoHelper();
        mPrefix = in.readStringNoHelper();
        mLowerBound = in.readStringNoHelper();
        mUpperBound = in.readStringNoHelper();
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeStringNoHelper(mCountryCode);
        dest.writeStringNoHelper(mPrefix);
        dest.writeStringNoHelper(mLowerBound);
        dest.writeStringNoHelper(mUpperBound);
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        PhoneNumberRange that = (PhoneNumberRange) o;
        return Objects.equals(mCountryCode, that.mCountryCode)
                && Objects.equals(mPrefix, that.mPrefix)
                && Objects.equals(mLowerBound, that.mLowerBound)
                && Objects.equals(mUpperBound, that.mUpperBound);
    }

    @Override
    public int hashCode() {
        return Objects.hash(mCountryCode, mPrefix, mLowerBound, mUpperBound);
    }

    @Override
    public String toString() {
        return "PhoneNumberRange{"
                + "mCountryCode='" + mCountryCode + '\''
                + ", mPrefix='" + mPrefix + '\''
                + ", mLowerBound='" + mLowerBound + '\''
                + ", mUpperBound='" + mUpperBound + '\''
                + '}';
    }

    private void validateLowerAndUpperBounds(String lowerBound, String upperBound) {
        if (lowerBound.length() != upperBound.length()) {
            throw new IllegalArgumentException("Lower and upper bounds must have the same length");
        }
        if (!Pattern.matches("[0-9]+", lowerBound)) {
            throw new IllegalArgumentException("Lower bound must be all numeric");
        }
        if (!Pattern.matches("[0-9]+", upperBound)) {
            throw new IllegalArgumentException("Upper bound must be all numeric");
        }
        if (Integer.parseInt(lowerBound) > Integer.parseInt(upperBound)) {
            throw new IllegalArgumentException("Lower bound must be lower than upper bound");
        }
    }

    /**
     * Checks to see if the provided phone number matches this range.
     * @param number A phone number, with or without separators or a country code.
     * @return {@code true} if the number matches, {@code false} otherwise.
     */
    public boolean matches(String number) {
        // Check the prefix, make sure it matches either with or without the country code.
        String normalizedNumber = number.replaceAll("[^0-9]", "");
        String prefixWithCountryCode = mCountryCode + mPrefix;
        String numberPostfix;
        if (normalizedNumber.startsWith(prefixWithCountryCode)) {
            numberPostfix = normalizedNumber.substring(prefixWithCountryCode.length());
        } else if (normalizedNumber.startsWith(mPrefix)) {
            numberPostfix = normalizedNumber.substring(mPrefix.length());
        } else {
            return false;
        }

        // Next check the postfix to make sure it lies within the bounds.
        try {
            int lower = Integer.parseInt(mLowerBound);
            int upper = Integer.parseInt(mUpperBound);
            int numberToCheck = Integer.parseInt(numberPostfix);
            return numberToCheck <= upper && numberToCheck >= lower;
        } catch (NumberFormatException e) {
            Log.e(PhoneNumberRange.class.getSimpleName(), "Invalid bounds or number.", e);
            return false;
        }
    }
}
Loading