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.ServiceState; 26 import android.text.TextUtils; 27 import android.util.Log; 28 29 import com.android.internal.telephony.Call; 30 import com.android.internal.telephony.INumberVerificationCallback; 31 import com.android.internal.telephony.Phone; 32 import com.android.internal.telephony.PhoneFactory; 33 34 /** 35 * Singleton for managing the call based number verification requests. 36 */ 37 public class NumberVerificationManager { 38 interface PhoneListSupplier { getPhones()39 Phone[] getPhones(); 40 } 41 42 private static NumberVerificationManager sInstance; 43 private static String sAuthorizedPackageOverride; 44 45 private PhoneNumberRange mCurrentRange; 46 private INumberVerificationCallback mCallback; 47 private final PhoneListSupplier mPhoneListSupplier; 48 49 // We don't really care what thread this runs on, since it's only used for a non-blocking 50 // timeout. 51 private Handler mHandler; 52 NumberVerificationManager(PhoneListSupplier phoneListSupplier)53 NumberVerificationManager(PhoneListSupplier phoneListSupplier) { 54 mPhoneListSupplier = phoneListSupplier; 55 mHandler = new Handler(Looper.getMainLooper()); 56 } 57 NumberVerificationManager()58 private NumberVerificationManager() { 59 this(PhoneFactory::getPhones); 60 } 61 62 /** 63 * Check whether the incoming call matches one of the active filters. If so, call the callback 64 * that says that the number has been successfully verified. 65 * @param number A phone number 66 * @return true if the number matches, false otherwise 67 */ checkIncomingCall(String number)68 public synchronized boolean checkIncomingCall(String number) { 69 if (mCurrentRange == null || mCallback == null) { 70 return false; 71 } 72 73 if (mCurrentRange.matches(number)) { 74 mCurrentRange = null; 75 try { 76 mCallback.onCallReceived(number); 77 return true; 78 } catch (RemoteException e) { 79 Log.w(NumberVerificationManager.class.getSimpleName(), 80 "Remote exception calling verification complete callback"); 81 // Intercept the call even if there was a remote exception -- it's still going to be 82 // a strange call from a robot number 83 return true; 84 } finally { 85 mCallback = null; 86 } 87 } 88 return false; 89 } 90 requestVerification(PhoneNumberRange numberRange, INumberVerificationCallback callback, long timeoutMillis)91 synchronized void requestVerification(PhoneNumberRange numberRange, 92 INumberVerificationCallback callback, long timeoutMillis) { 93 if (!checkNumberVerificationFeasibility(callback)) { 94 return; 95 } 96 97 mCallback = callback; 98 mCurrentRange = numberRange; 99 100 mHandler.postDelayed(() -> { 101 synchronized (NumberVerificationManager.this) { 102 // Check whether the verification finished already -- if so, don't call anything. 103 if (mCallback != null && mCurrentRange != null) { 104 try { 105 mCallback.onVerificationFailed(NumberVerificationCallback.REASON_TIMED_OUT); 106 } catch (RemoteException e) { 107 Log.w(NumberVerificationManager.class.getSimpleName(), 108 "Remote exception calling verification error callback"); 109 } 110 mCallback = null; 111 mCurrentRange = null; 112 } 113 } 114 }, timeoutMillis); 115 } 116 checkNumberVerificationFeasibility(INumberVerificationCallback callback)117 private boolean checkNumberVerificationFeasibility(INumberVerificationCallback callback) { 118 int reason = -1; 119 try { 120 if (mCurrentRange != null || mCallback != null) { 121 reason = NumberVerificationCallback.REASON_CONCURRENT_REQUESTS; 122 return false; 123 } 124 boolean doesAnyPhoneHaveRoomForIncomingCall = false; 125 boolean isAnyPhoneVoiceRegistered = false; 126 for (Phone phone : mPhoneListSupplier.getPhones()) { 127 // abort if any phone is in an emergency call or ecbm 128 if (phone.isInEmergencyCall()) { 129 reason = NumberVerificationCallback.REASON_IN_EMERGENCY_CALL; 130 return false; 131 } 132 if (phone.isInEcm()) { 133 reason = NumberVerificationCallback.REASON_IN_ECBM; 134 return false; 135 } 136 137 // make sure at least one phone is registered for voice 138 if (phone.getServiceState().getVoiceRegState() == ServiceState.STATE_IN_SERVICE) { 139 isAnyPhoneVoiceRegistered = true; 140 } 141 // make sure at least one phone has room for an incoming call. 142 if (phone.getRingingCall().getState() == Call.State.IDLE 143 && (phone.getForegroundCall().getState() == Call.State.IDLE 144 || phone.getBackgroundCall().getState() == Call.State.IDLE)) { 145 doesAnyPhoneHaveRoomForIncomingCall = true; 146 } 147 } 148 if (!isAnyPhoneVoiceRegistered) { 149 reason = NumberVerificationCallback.REASON_NETWORK_NOT_AVAILABLE; 150 return false; 151 } 152 if (!doesAnyPhoneHaveRoomForIncomingCall) { 153 reason = NumberVerificationCallback.REASON_TOO_MANY_CALLS; 154 return false; 155 } 156 } finally { 157 if (reason >= 0) { 158 try { 159 callback.onVerificationFailed(reason); 160 } catch (RemoteException e) { 161 Log.w(NumberVerificationManager.class.getSimpleName(), 162 "Remote exception calling verification error callback"); 163 } 164 } 165 } 166 return true; 167 } 168 169 /** 170 * Get the singleton instance of NumberVerificationManager. 171 * @return 172 */ getInstance()173 public static NumberVerificationManager getInstance() { 174 if (sInstance == null) { 175 sInstance = new NumberVerificationManager(); 176 } 177 return sInstance; 178 } 179 getAuthorizedPackage(Context context)180 static String getAuthorizedPackage(Context context) { 181 return !TextUtils.isEmpty(sAuthorizedPackageOverride) ? sAuthorizedPackageOverride : 182 context.getResources().getString(R.string.platform_number_verification_package); 183 } 184 185 /** 186 * Used by shell commands to override the authorized package name for number verification. 187 * @param pkgName 188 */ overrideAuthorizedPackage(String pkgName)189 static void overrideAuthorizedPackage(String pkgName) { 190 sAuthorizedPackageOverride = pkgName; 191 } 192 } 193