Merge "add nazaninb to OWNERS files"
diff --git a/Android.bp b/Android.bp
index 2b5c513..8023fc5 100644
--- a/Android.bp
+++ b/Android.bp
@@ -544,6 +544,7 @@
         "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",
diff --git a/api/system-current.txt b/api/system-current.txt
index 945dbf1..966cb32 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -4471,8 +4471,8 @@
 
   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 @@
     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 @@
     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 @@
     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
diff --git a/telephony/java/android/telephony/NumberVerificationCallback.java b/telephony/java/android/telephony/NumberVerificationCallback.java
new file mode 100644
index 0000000..b00c573
--- /dev/null
+++ b/telephony/java/android/telephony/NumberVerificationCallback.java
@@ -0,0 +1,88 @@
+/*
+ * 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) { }
+}
diff --git a/telephony/java/android/telephony/PhoneNumberRange.aidl b/telephony/java/android/telephony/PhoneNumberRange.aidl
new file mode 100644
index 0000000..b0727be
--- /dev/null
+++ b/telephony/java/android/telephony/PhoneNumberRange.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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;
diff --git a/telephony/java/android/telephony/PhoneNumberRange.java b/telephony/java/android/telephony/PhoneNumberRange.java
new file mode 100644
index 0000000..d65156f
--- /dev/null
+++ b/telephony/java/android/telephony/PhoneNumberRange.java
@@ -0,0 +1,176 @@
+/*
+ * 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;
+        }
+    }
+}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index e80eb2b..d4788e3 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -77,6 +77,7 @@
 import com.android.internal.telecom.ITelecomService;
 import com.android.internal.telephony.CellNetworkScanResult;
 import com.android.internal.telephony.IAns;
+import com.android.internal.telephony.INumberVerificationCallback;
 import com.android.internal.telephony.IPhoneSubInfo;
 import com.android.internal.telephony.ITelephony;
 import com.android.internal.telephony.ITelephonyRegistry;
@@ -1332,6 +1333,13 @@
      */
     public static final String EXTRA_RECOVERY_ACTION = "recoveryAction";
 
+    /**
+     * The max value for the timeout passed in {@link #requestNumberVerification}.
+     * @hide
+     */
+    @SystemApi
+    public static final long MAX_NUMBER_VERIFICATION_TIMEOUT_MILLIS = 60000;
+
     //
     //
     // Device Info
@@ -5478,6 +5486,52 @@
     }
 
     /**
+     * Request that the next incoming call from a number matching {@code range} be intercepted.
+     *
+     * This API is intended for OEMs to provide a service for apps to verify the device's phone
+     * number. When called, the Telephony stack will store the provided {@link PhoneNumberRange} and
+     * intercept the next incoming call from a number that lies within the range, within a timeout
+     * specified by {@code timeoutMillis}.
+     *
+     * If such a phone call is received, the caller will be notified via
+     * {@link NumberVerificationCallback#onCallReceived(String)} on the provided {@link Executor}.
+     * If verification fails for any reason, the caller will be notified via
+     * {@link NumberVerificationCallback#onVerificationFailed(int)}
+     * on the provided {@link Executor}.
+     *
+     * In addition to the {@link Manifest.permission#MODIFY_PHONE_STATE} permission, callers of this
+     * API must also be listed in the device configuration as an authorized app in
+     * {@code packages/services/Telephony/res/values/config.xml} under the
+     * {@code config_number_verification_package_name} key.
+     *
+     * @hide
+     * @param range The range of phone numbers the caller expects a phone call from.
+     * @param timeoutMillis The amount of time to wait for such a call, or
+     *                      {@link #MAX_NUMBER_VERIFICATION_TIMEOUT_MILLIS}, whichever is lesser.
+     * @param executor The {@link Executor} that callbacks should be executed on.
+     * @param callback The callback to use for delivering results.
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
+    public void requestNumberVerification(@NonNull PhoneNumberRange range, long timeoutMillis,
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull NumberVerificationCallback callback) {
+        INumberVerificationCallback internalCallback = new INumberVerificationCallback.Stub() {
+            @Override
+            public void onCallReceived(String phoneNumber) throws RemoteException {
+                Binder.withCleanCallingIdentity(() -> callback.onCallReceived(phoneNumber));
+            }
+
+            @Override
+            public void onVerificationFailed(int reason) throws RemoteException {
+                Binder.withCleanCallingIdentity(() -> callback.onVerificationFailed(reason));
+            }
+        };
+
+        // TODO -- call the aidl method
+    }
+
+    /**
      * Sets a per-phone telephony property with the value specified.
      *
      * @hide
diff --git a/telephony/java/com/android/internal/telephony/INumberVerificationCallback.aidl b/telephony/java/com/android/internal/telephony/INumberVerificationCallback.aidl
new file mode 100644
index 0000000..76918af
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/INumberVerificationCallback.aidl
@@ -0,0 +1,22 @@
+/*
+ * 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 com.android.internal.telephony;
+
+oneway interface INumberVerificationCallback {
+    void onCallReceived(String phoneNumber);
+    void onVerificationFailed(int reason);
+}