• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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