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

Commit 239b0de6 authored by Hall Liu's avatar Hall Liu Committed by Gerrit Code Review
Browse files

Merge "Add API scaffolding for number verification"

parents 1615f574 5314036b
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
@@ -4471,8 +4471,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);
  }

}
@@ -5186,6 +5186,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);
@@ -5350,6 +5370,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);
@@ -5379,6 +5400,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