blob: 9ec16f8fc9299e9f2efdbe6877a32da57111abcd [file] [log] [blame]
Hall Liud892bec2018-11-30 14:51:45 -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 com.android.phone;
18
19import android.content.Context;
20import android.os.Handler;
21import android.os.Looper;
22import android.os.RemoteException;
23import android.telephony.NumberVerificationCallback;
24import android.telephony.PhoneNumberRange;
25import android.telephony.ServiceState;
26import android.text.TextUtils;
27import android.util.Log;
28
29import com.android.internal.telephony.Call;
30import com.android.internal.telephony.INumberVerificationCallback;
31import com.android.internal.telephony.Phone;
32import com.android.internal.telephony.PhoneFactory;
33
34/**
35 * Singleton for managing the call based number verification requests.
36 */
37public class NumberVerificationManager {
38 interface PhoneListSupplier {
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
53 NumberVerificationManager(PhoneListSupplier phoneListSupplier) {
54 mPhoneListSupplier = phoneListSupplier;
55 mHandler = new Handler(Looper.getMainLooper());
56 }
57
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 */
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
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
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 */
173 public static NumberVerificationManager getInstance() {
174 if (sInstance == null) {
175 sInstance = new NumberVerificationManager();
176 }
177 return sInstance;
178 }
179
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 */
189 static void overrideAuthorizedPackage(String pkgName) {
190 sAuthorizedPackageOverride = pkgName;
191 }
192}