1 /* 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 17 package com.android.phone; 18 19 import android.content.Context; 20 import android.os.Handler; 21 import android.os.Looper; 22 import android.os.RemoteException; 23 import android.telephony.NumberVerificationCallback; 24 import android.telephony.PhoneNumberRange; 25 import android.telephony.PhoneNumberUtils; 26 import android.telephony.ServiceState; 27 import android.text.TextUtils; 28 import android.util.Log; 29 30 import com.android.internal.telephony.Call; 31 import com.android.internal.telephony.INumberVerificationCallback; 32 import com.android.internal.telephony.Phone; 33 import com.android.internal.telephony.PhoneFactory; 34 import com.android.internal.telephony.flags.Flags; 35 import com.android.telephony.Rlog; 36 37 /** 38 * Singleton for managing the call based number verification requests. 39 */ 40 public class NumberVerificationManager { 41 private static final String TAG = "NumberVerification"; 42 43 interface PhoneListSupplier { getPhones()44 Phone[] getPhones(); 45 } 46 47 private static NumberVerificationManager sInstance; 48 private static String sAuthorizedPackageOverride; 49 50 private PhoneNumberRange mCurrentRange; 51 private INumberVerificationCallback mCallback; 52 private final PhoneListSupplier mPhoneListSupplier; 53 54 // We don't really care what thread this runs on, since it's only used for a non-blocking 55 // timeout. 56 private Handler mHandler; 57 NumberVerificationManager(PhoneListSupplier phoneListSupplier)58 NumberVerificationManager(PhoneListSupplier phoneListSupplier) { 59 mPhoneListSupplier = phoneListSupplier; 60 mHandler = new Handler(Looper.getMainLooper()); 61 } 62 NumberVerificationManager()63 private NumberVerificationManager() { 64 this(PhoneFactory::getPhones); 65 } 66 67 /** 68 * Check whether the incoming call matches one of the active filters. If so, call the callback 69 * that says that the number has been successfully verified. 70 * @param number A phone number 71 * @param networkCountryISO the network country ISO for the number 72 * @return true if the number matches, false otherwise 73 */ checkIncomingCall(String number, String networkCountryISO)74 public synchronized boolean checkIncomingCall(String number, String networkCountryISO) { 75 if (mCurrentRange == null || mCallback == null) { 76 return false; 77 } 78 79 Log.i(TAG, "checkIncomingCall: number=" + Rlog.piiHandle(number) + ", country=" 80 + networkCountryISO); 81 82 String numberInE164Format; 83 if (Flags.robustNumberVerification()) { 84 // Reformat the number in E.164 format prior to performing matching. 85 numberInE164Format = PhoneNumberUtils.formatNumberToE164(number, 86 networkCountryISO); 87 if (TextUtils.isEmpty(numberInE164Format)) { 88 // Parsing failed, so we will fall back to just passing the number as-is and hope 89 // for the best. Chances are this number is an unknown number (ie no caller id), 90 // so it is most likely empty. 91 numberInE164Format = number; 92 } 93 } else { 94 // Default behavior. 95 numberInE164Format = number; 96 } 97 98 if (mCurrentRange.matches(numberInE164Format)) { 99 mCurrentRange = null; 100 try { 101 // Pass back the network-matched number as-is to the caller of 102 // TelephonyManager#requestNumberVerification -- do not send them the E.164 format 103 // number as that changes the format of the number from what the API consumer may 104 // be expecting. 105 mCallback.onCallReceived(number); 106 return true; 107 } catch (RemoteException e) { 108 Log.w(NumberVerificationManager.class.getSimpleName(), 109 "Remote exception calling verification complete callback"); 110 // Intercept the call even if there was a remote exception -- it's still going to be 111 // a strange call from a robot number 112 return true; 113 } finally { 114 mCallback = null; 115 } 116 } 117 return false; 118 } 119 requestVerification(PhoneNumberRange numberRange, INumberVerificationCallback callback, long timeoutMillis)120 synchronized void requestVerification(PhoneNumberRange numberRange, 121 INumberVerificationCallback callback, long timeoutMillis) { 122 if (!checkNumberVerificationFeasibility(callback)) { 123 return; 124 } 125 126 mCallback = callback; 127 mCurrentRange = numberRange; 128 129 mHandler.postDelayed(() -> { 130 synchronized (NumberVerificationManager.this) { 131 // Check whether the verification finished already -- if so, don't call anything. 132 if (mCallback != null && mCurrentRange != null) { 133 try { 134 mCallback.onVerificationFailed(NumberVerificationCallback.REASON_TIMED_OUT); 135 } catch (RemoteException e) { 136 Log.w(NumberVerificationManager.class.getSimpleName(), 137 "Remote exception calling verification error callback"); 138 } 139 mCallback = null; 140 mCurrentRange = null; 141 } 142 } 143 }, timeoutMillis); 144 } 145 checkNumberVerificationFeasibility(INumberVerificationCallback callback)146 private boolean checkNumberVerificationFeasibility(INumberVerificationCallback callback) { 147 int reason = -1; 148 try { 149 if (mCurrentRange != null || mCallback != null) { 150 reason = NumberVerificationCallback.REASON_CONCURRENT_REQUESTS; 151 return false; 152 } 153 boolean doesAnyPhoneHaveRoomForIncomingCall = false; 154 boolean isAnyPhoneVoiceRegistered = false; 155 for (Phone phone : mPhoneListSupplier.getPhones()) { 156 // abort if any phone is in an emergency call or ecbm 157 if (phone.isInEmergencyCall()) { 158 reason = NumberVerificationCallback.REASON_IN_EMERGENCY_CALL; 159 return false; 160 } 161 if (phone.isInEcm()) { 162 reason = NumberVerificationCallback.REASON_IN_ECBM; 163 return false; 164 } 165 166 // make sure at least one phone is registered for voice 167 if (phone.getServiceState().getState() == ServiceState.STATE_IN_SERVICE) { 168 isAnyPhoneVoiceRegistered = true; 169 } 170 // make sure at least one phone has room for an incoming call. 171 if (phone.getRingingCall().getState() == Call.State.IDLE 172 && (phone.getForegroundCall().getState() == Call.State.IDLE 173 || phone.getBackgroundCall().getState() == Call.State.IDLE)) { 174 doesAnyPhoneHaveRoomForIncomingCall = true; 175 } 176 } 177 if (!isAnyPhoneVoiceRegistered) { 178 reason = NumberVerificationCallback.REASON_NETWORK_NOT_AVAILABLE; 179 return false; 180 } 181 if (!doesAnyPhoneHaveRoomForIncomingCall) { 182 reason = NumberVerificationCallback.REASON_TOO_MANY_CALLS; 183 return false; 184 } 185 } finally { 186 if (reason >= 0) { 187 try { 188 callback.onVerificationFailed(reason); 189 } catch (RemoteException e) { 190 Log.w(NumberVerificationManager.class.getSimpleName(), 191 "Remote exception calling verification error callback"); 192 } 193 } 194 } 195 return true; 196 } 197 198 /** 199 * Get the singleton instance of NumberVerificationManager. 200 * @return 201 */ getInstance()202 public static NumberVerificationManager getInstance() { 203 if (sInstance == null) { 204 sInstance = new NumberVerificationManager(); 205 } 206 return sInstance; 207 } 208 getAuthorizedPackage(Context context)209 static String getAuthorizedPackage(Context context) { 210 return !TextUtils.isEmpty(sAuthorizedPackageOverride) ? sAuthorizedPackageOverride : 211 context.getResources().getString(R.string.platform_number_verification_package); 212 } 213 214 /** 215 * Used by shell commands to override the authorized package name for number verification. 216 * @param pkgName 217 */ overrideAuthorizedPackage(String pkgName)218 static void overrideAuthorizedPackage(String pkgName) { 219 // Make sure we don't have any lingering callbacks kicking around as this could crash other 220 // test runs that follow; also old invocations from previous test invocations could also 221 // mess up things here too. 222 getInstance().mCallback = null; 223 getInstance().mCurrentRange = null; 224 getInstance().mHandler.removeMessages(0); 225 sAuthorizedPackageOverride = pkgName; 226 } 227 } 228