Home | History | Annotate | Download | only in telephony
      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