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.server.telecom;
18 
19 import android.telecom.Log;
20 import android.telephony.PhoneNumberUtils;
21 import android.telephony.SubscriptionInfo;
22 import android.telephony.SubscriptionManager;
23 import android.telephony.TelephonyManager;
24 import android.telephony.TelephonyRegistryManager;
25 import android.telephony.emergency.EmergencyNumber;
26 
27 import java.util.List;
28 import java.util.Objects;
29 import java.util.Optional;
30 
31 /**
32  * Send a {@link TelephonyManager#ACTION_PHONE_STATE_CHANGED} broadcast when the call state
33  * changes.
34  */
35 final class PhoneStateBroadcaster extends CallsManagerListenerBase {
36 
37     private final CallsManager mCallsManager;
38     private final TelephonyRegistryManager mRegistry;
39     private int mCurrentState = TelephonyManager.CALL_STATE_IDLE;
40 
41     public PhoneStateBroadcaster(CallsManager callsManager) {
42         mCallsManager = callsManager;
43         mRegistry = callsManager.getContext().getSystemService(TelephonyRegistryManager.class);
44         if (mRegistry == null) {
45             Log.w(this, "TelephonyRegistry is null");
46         }
47     }
48 
49     @Override
50     public void onCallStateChanged(Call call, int oldState, int newState) {
51         if (call.isExternalCall()) {
52             return;
53         }
54         updateStates(call);
55     }
56 
57     @Override
58     public void onCallAdded(Call call) {
59         if (call.isExternalCall()) {
60             return;
61         }
62         updateStates(call);
63 
64         if (call.isEmergencyCall() && !call.isIncoming()) {
65             sendOutgoingEmergencyCallEvent(call);
66         }
67     }
68 
69     @Override
70     public void onCallRemoved(Call call) {
71         if (call.isExternalCall()) {
72             return;
73         }
74         updateStates(call);
75     }
76 
77     /**
78      * Handles changes to a call's external property.  If the call becomes external, we end up
79      * updating the call state to idle.  If the call becomes non-external, then the call state can
80      * update to off hook.
81      *
82      * @param call The call.
83      * @param isExternalCall {@code True} if the call is external, {@code false} otherwise.
84      */
85     @Override
86     public void onExternalCallChanged(Call call, boolean isExternalCall) {
87         updateStates(call);
88     }
89 
90     private void updateStates(Call call) {
91         // Recalculate the current phone state based on the consolidated state of the remaining
92         // calls in the call list.
93         // Note: CallsManager#hasRingingCall() and CallsManager#getFirstCallWithState(..) do not
94         // consider external calls, so an external call is going to cause the state to be idle.
95         int callState = TelephonyManager.CALL_STATE_IDLE;
96         if (mCallsManager.hasRingingOrSimulatedRingingCall()) {
97             callState = TelephonyManager.CALL_STATE_RINGING;
98         } else if (mCallsManager.getFirstCallWithState(CallState.DIALING, CallState.PULLING,
99                 CallState.ACTIVE, CallState.ON_HOLD) != null) {
100             callState = TelephonyManager.CALL_STATE_OFFHOOK;
101         }
102         sendPhoneStateChangedBroadcast(call, callState);
103     }
104 
105     int getCallState() {
106         return mCurrentState;
107     }
108 
109     private void sendPhoneStateChangedBroadcast(Call call, int phoneState) {
110         if (phoneState == mCurrentState) {
111             return;
112         }
113 
114         mCurrentState = phoneState;
115 
116         String callHandle = null;
117         // Only report phone numbers in phone state broadcast for regular mobile calls; do not
118         // include numbers from 3rd party apps.
119         if (!call.isSelfManaged() && call.getHandle() != null) {
120             callHandle = call.getHandle().getSchemeSpecificPart();
121         }
122 
123         if (mRegistry != null) {
124             mRegistry.notifyCallStateChangedForAllSubscriptions(phoneState, callHandle);
125             Log.i(this, "Broadcasted state change: %s", mCurrentState);
126         }
127     }
128 
129     private void sendOutgoingEmergencyCallEvent(Call call) {
130         TelephonyManager tm = mCallsManager.getContext().getSystemService(TelephonyManager.class);
131         String strippedNumber =
132                 PhoneNumberUtils.stripSeparators(call.getHandle().getSchemeSpecificPart());
133         Optional<EmergencyNumber> emergencyNumber;
134         try {
135             emergencyNumber = tm.getEmergencyNumberList().values().stream()
136                     .flatMap(List::stream)
137                     .filter(numberObj -> Objects.equals(numberObj.getNumber(), strippedNumber))
138                     .findFirst();
139         } catch (IllegalStateException ie) {
140             emergencyNumber = Optional.empty();
141         } catch (RuntimeException r) {
142             emergencyNumber = Optional.empty();
143         }
144 
145         int subscriptionId = tm.getSubscriptionId(call.getTargetPhoneAccount());
146         SubscriptionManager subscriptionManager =
147                 mCallsManager.getContext().getSystemService(SubscriptionManager.class);
148         int simSlotIndex = SubscriptionManager.DEFAULT_PHONE_INDEX;
149         if (subscriptionManager != null) {
150             SubscriptionInfo subInfo =
151                     subscriptionManager.getActiveSubscriptionInfo(subscriptionId);
152             if (subInfo != null) {
153                 simSlotIndex = subInfo.getSimSlotIndex();
154             }
155         }
156 
157         if (emergencyNumber.isPresent()) {
158             mRegistry.notifyOutgoingEmergencyCall(
159                     simSlotIndex, subscriptionId, emergencyNumber.get());
160         }
161     }
162 }
163