1 /* 2 * Copyright (C) 2014 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.services.telephony; 18 19 import android.content.BroadcastReceiver; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.content.IntentFilter; 23 import android.net.Uri; 24 import android.os.AsyncResult; 25 import android.os.Bundle; 26 import android.os.Handler; 27 import android.os.Message; 28 import android.os.UserHandle; 29 import android.telecom.CallState; 30 import android.telecom.PhoneAccount; 31 import android.telecom.TelecomManager; 32 import android.telephony.TelephonyManager; 33 import android.text.TextUtils; 34 35 import com.android.internal.telephony.Call; 36 import com.android.internal.telephony.Connection; 37 import com.android.internal.telephony.Phone; 38 import com.android.internal.telephony.PhoneConstants; 39 import com.android.internal.telephony.PhoneProxy; 40 import com.android.internal.telephony.TelephonyIntents; 41 import com.android.internal.telephony.cdma.CdmaCallWaitingNotification; 42 43 import com.google.common.base.Preconditions; 44 45 import java.util.Objects; 46 47 /** 48 * Listens to incoming-call events from the associated phone object and notifies Telecom upon each 49 * occurence. One instance of these exists for each of the telephony-based call services. 50 */ 51 final class PstnIncomingCallNotifier { 52 /** New ringing connection event code. */ 53 private static final int EVENT_NEW_RINGING_CONNECTION = 100; 54 private static final int EVENT_CDMA_CALL_WAITING = 101; 55 private static final int EVENT_UNKNOWN_CONNECTION = 102; 56 57 /** The phone proxy object to listen to. */ 58 private final PhoneProxy mPhoneProxy; 59 60 /** 61 * The base phone implementation behind phone proxy. The underlying phone implementation can 62 * change underneath when the radio technology changes. We listen for these events and update 63 * the base phone in this variable. We save it so that when the change happens, we can 64 * unregister from the events we were listening to. 65 */ 66 private Phone mPhoneBase; 67 68 /** 69 * Used to listen to events from {@link #mPhoneBase}. 70 */ 71 private final Handler mHandler = new Handler() { 72 @Override 73 public void handleMessage(Message msg) { 74 switch(msg.what) { 75 case EVENT_NEW_RINGING_CONNECTION: 76 handleNewRingingConnection((AsyncResult) msg.obj); 77 break; 78 case EVENT_CDMA_CALL_WAITING: 79 handleCdmaCallWaiting((AsyncResult) msg.obj); 80 break; 81 case EVENT_UNKNOWN_CONNECTION: 82 handleNewUnknownConnection((AsyncResult) msg.obj); 83 break; 84 default: 85 break; 86 } 87 } 88 }; 89 90 /** 91 * Receiver to listen for radio technology change events. 92 */ 93 private final BroadcastReceiver mRATReceiver = new BroadcastReceiver() { 94 @Override 95 public void onReceive(Context context, Intent intent) { 96 String action = intent.getAction(); 97 if (TelephonyIntents.ACTION_RADIO_TECHNOLOGY_CHANGED.equals(action)) { 98 String newPhone = intent.getStringExtra(PhoneConstants.PHONE_NAME_KEY); 99 Log.d(this, "Radio technology switched. Now %s is active.", newPhone); 100 101 registerForNotifications(); 102 } 103 } 104 }; 105 106 /** 107 * Persists the specified parameters and starts listening to phone events. 108 * 109 * @param phoneProxy The phone object for listening to incoming calls. 110 */ 111 PstnIncomingCallNotifier(PhoneProxy phoneProxy) { 112 Preconditions.checkNotNull(phoneProxy); 113 114 mPhoneProxy = phoneProxy; 115 116 registerForNotifications(); 117 118 IntentFilter intentFilter = 119 new IntentFilter(TelephonyIntents.ACTION_RADIO_TECHNOLOGY_CHANGED); 120 mPhoneProxy.getContext().registerReceiver(mRATReceiver, intentFilter); 121 } 122 123 void teardown() { 124 unregisterForNotifications(); 125 mPhoneProxy.getContext().unregisterReceiver(mRATReceiver); 126 } 127 128 /** 129 * Register for notifications from the base phone. 130 * TODO: We should only need to interact with the phoneproxy directly. However, 131 * since the phoneproxy only interacts directly with CallManager we either listen to callmanager 132 * or we have to poke into the proxy like this. Neither is desirable. It would be better if 133 * this class and callManager could register generically with the phone proxy instead and get 134 * radio techonology changes directly. Or better yet, just register for the notifications 135 * directly with phone proxy and never worry about the technology changes. This requires a 136 * change in opt/telephony code. 137 */ 138 private void registerForNotifications() { 139 Phone newPhone = mPhoneProxy.getActivePhone(); 140 if (newPhone != mPhoneBase) { 141 unregisterForNotifications(); 142 143 if (newPhone != null) { 144 Log.i(this, "Registering: %s", newPhone); 145 mPhoneBase = newPhone; 146 mPhoneBase.registerForNewRingingConnection( 147 mHandler, EVENT_NEW_RINGING_CONNECTION, null); 148 mPhoneBase.registerForCallWaiting( 149 mHandler, EVENT_CDMA_CALL_WAITING, null); 150 mPhoneBase.registerForUnknownConnection(mHandler, EVENT_UNKNOWN_CONNECTION, 151 null); 152 } 153 } 154 } 155 156 private void unregisterForNotifications() { 157 if (mPhoneBase != null) { 158 Log.i(this, "Unregistering: %s", mPhoneBase); 159 mPhoneBase.unregisterForNewRingingConnection(mHandler); 160 mPhoneBase.unregisterForCallWaiting(mHandler); 161 mPhoneBase.unregisterForUnknownConnection(mHandler); 162 } 163 } 164 165 /** 166 * Verifies the incoming call and triggers sending the incoming-call intent to Telecom. 167 * 168 * @param asyncResult The result object from the new ringing event. 169 */ 170 private void handleNewRingingConnection(AsyncResult asyncResult) { 171 Log.d(this, "handleNewRingingConnection"); 172 Connection connection = (Connection) asyncResult.result; 173 if (connection != null) { 174 Call call = connection.getCall(); 175 176 // Final verification of the ringing state before sending the intent to Telecom. 177 if (call != null && call.getState().isRinging()) { 178 sendIncomingCallIntent(connection); 179 } 180 } 181 } 182 183 private void handleCdmaCallWaiting(AsyncResult asyncResult) { 184 Log.d(this, "handleCdmaCallWaiting"); 185 CdmaCallWaitingNotification ccwi = (CdmaCallWaitingNotification) asyncResult.result; 186 Call call = mPhoneBase.getRingingCall(); 187 if (call.getState() == Call.State.WAITING) { 188 Connection connection = call.getLatestConnection(); 189 if (connection != null) { 190 String number = connection.getAddress(); 191 if (number != null && Objects.equals(number, ccwi.number)) { 192 sendIncomingCallIntent(connection); 193 } 194 } 195 } 196 } 197 198 private void handleNewUnknownConnection(AsyncResult asyncResult) { 199 Log.i(this, "handleNewUnknownConnection"); 200 if (!(asyncResult.result instanceof Connection)) { 201 Log.w(this, "handleNewUnknownConnection called with non-Connection object"); 202 return; 203 } 204 Connection connection = (Connection) asyncResult.result; 205 if (connection != null) { 206 Call call = connection.getCall(); 207 if (call != null && call.getState().isAlive()) { 208 addNewUnknownCall(connection); 209 } 210 } 211 } 212 213 private void addNewUnknownCall(Connection connection) { 214 Bundle extras = null; 215 if (connection.getNumberPresentation() == TelecomManager.PRESENTATION_ALLOWED && 216 !TextUtils.isEmpty(connection.getAddress())) { 217 extras = new Bundle(); 218 Uri uri = Uri.fromParts(PhoneAccount.SCHEME_TEL, connection.getAddress(), null); 219 extras.putParcelable(TelecomManager.EXTRA_UNKNOWN_CALL_HANDLE, uri); 220 } 221 TelecomManager.from(mPhoneProxy.getContext()).addNewUnknownCall( 222 TelecomAccountRegistry.makePstnPhoneAccountHandle(mPhoneProxy), extras); 223 } 224 225 /** 226 * Sends the incoming call intent to telecom. 227 */ 228 private void sendIncomingCallIntent(Connection connection) { 229 Bundle extras = null; 230 if (connection.getNumberPresentation() == TelecomManager.PRESENTATION_ALLOWED && 231 !TextUtils.isEmpty(connection.getAddress())) { 232 extras = new Bundle(); 233 Uri uri = Uri.fromParts(PhoneAccount.SCHEME_TEL, connection.getAddress(), null); 234 extras.putParcelable(TelephonyManager.EXTRA_INCOMING_NUMBER, uri); 235 } 236 TelecomManager.from(mPhoneProxy.getContext()).addNewIncomingCall( 237 TelecomAccountRegistry.makePstnPhoneAccountHandle(mPhoneProxy), extras); 238 } 239 } 240