blob: 2b199d2df1414a161e8530bea2e7e4b33ff7ceec [file] [log] [blame]
Hall Liu53140362018-11-28 10:44:22 -08001/*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.telephony;
18
19import android.annotation.NonNull;
Aurimas Liutikas4d1699d2019-08-28 13:01:05 -070020import android.annotation.Nullable;
Hall Liu53140362018-11-28 10:44:22 -080021import android.annotation.SystemApi;
22import android.os.Parcel;
23import android.os.Parcelable;
24import android.util.Log;
25
26import java.util.Objects;
27import java.util.regex.Pattern;
28
29/**
30 * This class is used to represent a range of phone numbers. Each range corresponds to a contiguous
31 * block of phone numbers.
32 *
33 * Example:
34 * {@code
35 * {
36 * mCountryCode = "1"
37 * mPrefix = "650555"
38 * mLowerBound = "0055"
39 * mUpperBound = "0899"
40 * }
41 * }
42 * would match 16505550089 and 6505550472, but not 63827593759 or 16505550900
43 * @hide
44 */
45@SystemApi
46public final class PhoneNumberRange implements Parcelable {
Jeff Sharkey9e8f83d2019-02-28 12:06:45 -070047 public static final @android.annotation.NonNull Creator<PhoneNumberRange> CREATOR = new Creator<PhoneNumberRange>() {
Hall Liu53140362018-11-28 10:44:22 -080048 @Override
49 public PhoneNumberRange createFromParcel(Parcel in) {
50 return new PhoneNumberRange(in);
51 }
52
53 @Override
54 public PhoneNumberRange[] newArray(int size) {
55 return new PhoneNumberRange[size];
56 }
57 };
58
59 private final String mCountryCode;
60 private final String mPrefix;
61 private final String mLowerBound;
62 private final String mUpperBound;
63
64 /**
65 * @param countryCode The country code, omitting the leading "+"
66 * @param prefix A prefix that all numbers matching the range must have.
67 * @param lowerBound When concatenated with the prefix, represents the lower bound of phone
68 * numbers that match this range.
69 * @param upperBound When concatenated with the prefix, represents the upper bound of phone
70 * numbers that match this range.
71 */
72 public PhoneNumberRange(@NonNull String countryCode, @NonNull String prefix,
73 @NonNull String lowerBound, @NonNull String upperBound) {
74 validateLowerAndUpperBounds(lowerBound, upperBound);
Hall Liua1ea5d72018-12-05 13:57:42 -080075 if (!Pattern.matches("[0-9]*", countryCode)) {
Hall Liu53140362018-11-28 10:44:22 -080076 throw new IllegalArgumentException("Country code must be all numeric");
77 }
Hall Liua1ea5d72018-12-05 13:57:42 -080078 if (!Pattern.matches("[0-9]*", prefix)) {
Hall Liu53140362018-11-28 10:44:22 -080079 throw new IllegalArgumentException("Prefix must be all numeric");
80 }
81 mCountryCode = countryCode;
82 mPrefix = prefix;
83 mLowerBound = lowerBound;
84 mUpperBound = upperBound;
85 }
86
87 private PhoneNumberRange(Parcel in) {
Sooraj Sasindranbdca31b2019-12-10 19:29:01 -080088 mCountryCode = in.readString();
89 mPrefix = in.readString();
90 mLowerBound = in.readString();
91 mUpperBound = in.readString();
Hall Liu53140362018-11-28 10:44:22 -080092 }
93
94 @Override
95 public void writeToParcel(Parcel dest, int flags) {
Sooraj Sasindranbdca31b2019-12-10 19:29:01 -080096 dest.writeString(mCountryCode);
97 dest.writeString(mPrefix);
98 dest.writeString(mLowerBound);
99 dest.writeString(mUpperBound);
Hall Liu53140362018-11-28 10:44:22 -0800100 }
101
102 @Override
103 public int describeContents() {
104 return 0;
105 }
106
107 @Override
Aurimas Liutikas4d1699d2019-08-28 13:01:05 -0700108 public boolean equals(@Nullable Object o) {
Hall Liu53140362018-11-28 10:44:22 -0800109 if (this == o) return true;
110 if (o == null || getClass() != o.getClass()) return false;
111 PhoneNumberRange that = (PhoneNumberRange) o;
112 return Objects.equals(mCountryCode, that.mCountryCode)
113 && Objects.equals(mPrefix, that.mPrefix)
114 && Objects.equals(mLowerBound, that.mLowerBound)
115 && Objects.equals(mUpperBound, that.mUpperBound);
116 }
117
118 @Override
119 public int hashCode() {
120 return Objects.hash(mCountryCode, mPrefix, mLowerBound, mUpperBound);
121 }
122
Aurimas Liutikas4d1699d2019-08-28 13:01:05 -0700123 @NonNull
Hall Liu53140362018-11-28 10:44:22 -0800124 @Override
125 public String toString() {
126 return "PhoneNumberRange{"
127 + "mCountryCode='" + mCountryCode + '\''
128 + ", mPrefix='" + mPrefix + '\''
129 + ", mLowerBound='" + mLowerBound + '\''
130 + ", mUpperBound='" + mUpperBound + '\''
131 + '}';
132 }
133
134 private void validateLowerAndUpperBounds(String lowerBound, String upperBound) {
135 if (lowerBound.length() != upperBound.length()) {
136 throw new IllegalArgumentException("Lower and upper bounds must have the same length");
137 }
Hall Liua1ea5d72018-12-05 13:57:42 -0800138 if (!Pattern.matches("[0-9]*", lowerBound)) {
Hall Liu53140362018-11-28 10:44:22 -0800139 throw new IllegalArgumentException("Lower bound must be all numeric");
140 }
Hall Liua1ea5d72018-12-05 13:57:42 -0800141 if (!Pattern.matches("[0-9]*", upperBound)) {
Hall Liu53140362018-11-28 10:44:22 -0800142 throw new IllegalArgumentException("Upper bound must be all numeric");
143 }
144 if (Integer.parseInt(lowerBound) > Integer.parseInt(upperBound)) {
145 throw new IllegalArgumentException("Lower bound must be lower than upper bound");
146 }
147 }
148
149 /**
150 * Checks to see if the provided phone number matches this range.
151 * @param number A phone number, with or without separators or a country code.
152 * @return {@code true} if the number matches, {@code false} otherwise.
153 */
Hall Liu1059cd52019-02-28 18:54:15 -0800154 public boolean matches(@NonNull String number) {
Hall Liu53140362018-11-28 10:44:22 -0800155 // Check the prefix, make sure it matches either with or without the country code.
156 String normalizedNumber = number.replaceAll("[^0-9]", "");
157 String prefixWithCountryCode = mCountryCode + mPrefix;
158 String numberPostfix;
159 if (normalizedNumber.startsWith(prefixWithCountryCode)) {
160 numberPostfix = normalizedNumber.substring(prefixWithCountryCode.length());
161 } else if (normalizedNumber.startsWith(mPrefix)) {
162 numberPostfix = normalizedNumber.substring(mPrefix.length());
163 } else {
164 return false;
165 }
166
167 // Next check the postfix to make sure it lies within the bounds.
168 try {
169 int lower = Integer.parseInt(mLowerBound);
170 int upper = Integer.parseInt(mUpperBound);
171 int numberToCheck = Integer.parseInt(numberPostfix);
172 return numberToCheck <= upper && numberToCheck >= lower;
173 } catch (NumberFormatException e) {
174 Log.e(PhoneNumberRange.class.getSimpleName(), "Invalid bounds or number.", e);
175 return false;
176 }
177 }
178}