blob: 0d36c8ac6014b60d807eb0780b24f5801ece9b2b [file] [log] [blame]
Santos Cordon10e68322013-12-12 16:06:56 -08001/*
2 * Copyright (C) 2013 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.telecomm;
18
19import android.app.Activity;
Yorke Leecce5deb2014-06-18 11:27:42 -070020import android.content.ComponentName;
21import android.content.Context;
Santos Cordon10e68322013-12-12 16:06:56 -080022import android.content.Intent;
23import android.content.res.Configuration;
Nancy Chen0d3076c2014-07-30 14:45:44 -070024import android.net.Uri;
Santos Cordon10e68322013-12-12 16:06:56 -080025import android.os.Bundle;
Yorke Lee35bcc892014-08-13 15:27:16 -070026import android.os.UserManager;
Evan Charlton89176372014-07-19 18:23:09 -070027import android.telecomm.PhoneAccountHandle;
Evan Charltonb35fc312014-07-19 15:02:55 -070028import android.telecomm.TelecommManager;
Yorke Leed7255872014-08-25 15:03:51 -070029import android.telephony.DisconnectCause;
Nancy Chen0d3076c2014-07-30 14:45:44 -070030import android.telephony.PhoneNumberUtils;
Yorke Leecce5deb2014-06-18 11:27:42 -070031import android.text.TextUtils;
Yorke Lee35bcc892014-08-13 15:27:16 -070032import android.widget.Toast;
Santos Cordon10e68322013-12-12 16:06:56 -080033
34/**
35 * Activity that handles system CALL actions and forwards them to {@link CallsManager}.
36 * Handles all three CALL action types: CALL, CALL_PRIVILEGED, and CALL_EMERGENCY.
Yorke Leecce5deb2014-06-18 11:27:42 -070037 *
38 * Pre-L, the only way apps were were allowed to make outgoing emergency calls was the
39 * ACTION_CALL_PRIVILEGED action (which requires the system only CALL_PRIVILEGED permission).
40 *
41 * In L, any app that has the CALL_PRIVILEGED permission can continue to make outgoing emergency
42 * calls via ACTION_CALL_PRIVILEGED.
43 *
44 * In addition, the default dialer (identified via {@link TelecommManager#getDefaultPhoneApp()}
45 * will also be granted the ability to make emergency outgoing calls using the CALL action. In
46 * order to do this, it must call startActivityForResult on the CALL intent to allow its package
47 * name to be passed to {@link CallActivity}. Calling startActivity will continue to work on all
48 * non-emergency numbers just like it did pre-L.
Santos Cordon10e68322013-12-12 16:06:56 -080049 */
50public class CallActivity extends Activity {
Yorke Leed7255872014-08-25 15:03:51 -070051
Ben Giladdd8c6082013-12-30 14:44:08 -080052 private CallsManager mCallsManager = CallsManager.getInstance();
Tyler Gunn8ad2d8d2014-08-13 14:37:16 -070053 private boolean mIsVoiceCapable;
Ben Giladdd8c6082013-12-30 14:44:08 -080054
Santos Cordon10e68322013-12-12 16:06:56 -080055 /**
56 * {@inheritDoc}
57 *
58 * This method is the single point of entry for the CALL intent, which is used by built-in apps
59 * like Contacts & Dialer, as well as 3rd party apps to initiate outgoing calls.
60 */
61 @Override
62 protected void onCreate(Bundle bundle) {
63 super.onCreate(bundle);
64
Tyler Gunn8ad2d8d2014-08-13 14:37:16 -070065 mIsVoiceCapable = isVoiceCapable();
66
Santos Cordondf399862014-08-06 04:39:15 -070067 // TODO: This activity will be displayed until the next screen which could be
Santos Cordon10e68322013-12-12 16:06:56 -080068 // the in-call UI and error dialog or potentially a call-type selection dialog.
69 // Traditionally, this has been a black screen with a spinner. We need to reevaluate if this
70 // is still desired and add back if necessary. Currently, the activity is set to NoDisplay
71 // theme which means it shows no UI.
72
73 Intent intent = getIntent();
74 Configuration configuration = getResources().getConfiguration();
75
Sailesh Nepalf1c191d2014-03-07 18:17:39 -080076 Log.d(this, "onCreate: this = %s, bundle = %s", this, bundle);
77 Log.d(this, " - intent = %s", intent);
78 Log.d(this, " - configuration = %s", configuration);
Santos Cordon10e68322013-12-12 16:06:56 -080079
Santos Cordondf399862014-08-06 04:39:15 -070080 // TODO: Figure out if there is something to restore from bundle.
Santos Cordon10e68322013-12-12 16:06:56 -080081 // See OutgoingCallBroadcaster in services/Telephony for more.
82
Santos Cordon523f6052014-02-20 16:39:34 -080083 processIntent(intent);
Santos Cordon10e68322013-12-12 16:06:56 -080084
Santos Cordon523f6052014-02-20 16:39:34 -080085 // This activity does not have associated UI, so close.
Santos Cordon10e68322013-12-12 16:06:56 -080086 finish();
87
Sailesh Nepalf1c191d2014-03-07 18:17:39 -080088 Log.d(this, "onCreate: end");
Santos Cordon10e68322013-12-12 16:06:56 -080089 }
90
91 /**
Santos Cordon523f6052014-02-20 16:39:34 -080092 * Processes intents sent to the activity.
93 *
94 * @param intent The intent.
95 */
96 private void processIntent(Intent intent) {
Tyler Gunn8ad2d8d2014-08-13 14:37:16 -070097 // Ensure call intents are not processed on devices that are not capable of calling.
98 if (!mIsVoiceCapable) {
99 setResult(RESULT_CANCELED);
100 return;
101 }
102
Santos Cordon523f6052014-02-20 16:39:34 -0800103 String action = intent.getAction();
104
Santos Cordona0e5f1a2014-03-31 21:43:00 -0700105 // TODO: Check for non-voice capable devices before reading any intents.
106
Santos Cordon523f6052014-02-20 16:39:34 -0800107 if (Intent.ACTION_CALL.equals(action) ||
108 Intent.ACTION_CALL_PRIVILEGED.equals(action) ||
109 Intent.ACTION_CALL_EMERGENCY.equals(action)) {
110 processOutgoingCallIntent(intent);
Evan Charltonb35fc312014-07-19 15:02:55 -0700111 } else if (TelecommManager.ACTION_INCOMING_CALL.equals(action)) {
Santos Cordon523f6052014-02-20 16:39:34 -0800112 processIncomingCallIntent(intent);
113 }
114 }
115
116 /**
Santos Cordon10e68322013-12-12 16:06:56 -0800117 * Processes CALL, CALL_PRIVILEGED, and CALL_EMERGENCY intents.
118 *
119 * @param intent Call intent containing data about the handle to call.
120 */
Ben Giladdd8c6082013-12-30 14:44:08 -0800121 private void processOutgoingCallIntent(Intent intent) {
Nancy Chen0d3076c2014-07-30 14:45:44 -0700122 String uriString = intent.getData().getSchemeSpecificPart();
123 Uri handle = Uri.fromParts(
124 PhoneNumberUtils.isUriNumber(uriString) ? "sip" : "tel", uriString, null);
Yorke Lee35bcc892014-08-13 15:27:16 -0700125
126 UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);
127 if (userManager.hasUserRestriction(UserManager.DISALLOW_OUTGOING_CALLS)
128 && !TelephonyUtil.shouldProcessAsEmergency(this, handle)) {
129 // Only emergency calls are allowed for users with the DISALLOW_OUTGOING_CALLS
130 // restriction.
131 Toast.makeText(this, getResources().getString(R.string.outgoing_call_not_allowed),
132 Toast.LENGTH_SHORT).show();
133 Log.d(this, "Rejecting non-emergency phone call due to DISALLOW_OUTGOING_CALLS "
134 + "restriction");
135 return;
136 }
137
Nancy Chen0d3076c2014-07-30 14:45:44 -0700138 PhoneAccountHandle phoneAccountHandle = intent.getParcelableExtra(
139 TelecommManager.EXTRA_PHONE_ACCOUNT_HANDLE);
140
Nancy Chena9d91da2014-08-12 14:31:06 -0700141 Bundle clientExtras = null;
142 if (intent.hasExtra(TelecommManager.EXTRA_OUTGOING_CALL_EXTRAS)) {
143 clientExtras = intent.getBundleExtra(TelecommManager.EXTRA_OUTGOING_CALL_EXTRAS);
144 }
145 if (clientExtras == null) {
146 clientExtras = Bundle.EMPTY;
147 }
148
Nancy Chen0d3076c2014-07-30 14:45:44 -0700149 // Send to CallsManager to ensure the InCallUI gets kicked off before the broadcast returns
Nancy Chena9d91da2014-08-12 14:31:06 -0700150 Call call = mCallsManager.startOutgoingCall(handle, phoneAccountHandle, clientExtras);
Nancy Chen0d3076c2014-07-30 14:45:44 -0700151
Yorke Leecce5deb2014-06-18 11:27:42 -0700152 NewOutgoingCallIntentBroadcaster broadcaster = new NewOutgoingCallIntentBroadcaster(
Nancy Chen0d3076c2014-07-30 14:45:44 -0700153 mCallsManager, call, intent, isDefaultDialer());
Yorke Leed7255872014-08-25 15:03:51 -0700154 final int result = broadcaster.processIntent();
155 final boolean success = result == DisconnectCause.NOT_DISCONNECTED;
156
Yorke Leee6b42e92014-08-22 16:41:17 -0700157 if (!success && call != null) {
Yorke Leed7255872014-08-25 15:03:51 -0700158 disconnectCallAndShowErrorDialog(call, result);
Yorke Leee6b42e92014-08-22 16:41:17 -0700159 }
Yorke Leecce5deb2014-06-18 11:27:42 -0700160 setResult(success ? RESULT_OK : RESULT_CANCELED);
Santos Cordon10e68322013-12-12 16:06:56 -0800161 }
Santos Cordon523f6052014-02-20 16:39:34 -0800162
163 /**
Evan Charlton89176372014-07-19 18:23:09 -0700164 * Processes INCOMING_CALL intents. Grabs the connection service information from the intent
Sailesh Nepalc92c4362014-07-04 18:33:21 -0700165 * extra and forwards that to the CallsManager to start the incoming call flow.
Santos Cordon523f6052014-02-20 16:39:34 -0800166 *
167 * @param intent The incoming call intent.
168 */
169 private void processIncomingCallIntent(Intent intent) {
Evan Charlton89176372014-07-19 18:23:09 -0700170 PhoneAccountHandle phoneAccountHandle = intent.getParcelableExtra(
171 TelecommManager.EXTRA_PHONE_ACCOUNT_HANDLE);
172 if (phoneAccountHandle == null) {
Sailesh Nepal905dfba2014-07-14 08:20:41 -0700173 Log.w(this, "Rejecting incoming call due to null phone account");
174 return;
175 }
Evan Charlton89176372014-07-19 18:23:09 -0700176 if (phoneAccountHandle.getComponentName() == null) {
Sailesh Nepal905dfba2014-07-14 08:20:41 -0700177 Log.w(this, "Rejecting incoming call due to null component name");
Santos Cordon523f6052014-02-20 16:39:34 -0800178 return;
179 }
180
Sailesh Nepal664837f2014-07-14 16:31:51 -0700181 Bundle clientExtras = null;
Evan Charltonb35fc312014-07-19 15:02:55 -0700182 if (intent.hasExtra(TelecommManager.EXTRA_INCOMING_CALL_EXTRAS)) {
183 clientExtras = intent.getBundleExtra(TelecommManager.EXTRA_INCOMING_CALL_EXTRAS);
Evan Charltona05805b2014-03-05 08:21:46 -0800184 }
Sailesh Nepal664837f2014-07-14 16:31:51 -0700185 if (clientExtras == null) {
186 clientExtras = Bundle.EMPTY;
187 }
Evan Charltona05805b2014-03-05 08:21:46 -0800188
Sailesh Nepal905dfba2014-07-14 08:20:41 -0700189 Log.d(this, "Processing incoming call from connection service [%s]",
Evan Charlton89176372014-07-19 18:23:09 -0700190 phoneAccountHandle.getComponentName());
191 mCallsManager.processIncomingCallIntent(phoneAccountHandle, clientExtras);
Santos Cordon523f6052014-02-20 16:39:34 -0800192 }
Yorke Leecce5deb2014-06-18 11:27:42 -0700193
194 private boolean isDefaultDialer() {
195 final String packageName = getCallingPackage();
196 if (TextUtils.isEmpty(packageName)) {
197 return false;
198 }
199
200 final TelecommManager telecommManager =
201 (TelecommManager) getSystemService(Context.TELECOMM_SERVICE);
202 final ComponentName defaultPhoneApp = telecommManager.getDefaultPhoneApp();
203 return (defaultPhoneApp != null
204 && TextUtils.equals(defaultPhoneApp.getPackageName(), packageName));
205 }
Tyler Gunn8ad2d8d2014-08-13 14:37:16 -0700206
207 /**
208 * Returns whether the device is voice-capable (e.g. a phone vs a tablet).
209 *
210 * @return {@code True} if the device is voice-capable.
211 */
212 private boolean isVoiceCapable() {
213 return getApplicationContext().getResources().getBoolean(
214 com.android.internal.R.bool.config_voice_capable);
215 }
Yorke Leed7255872014-08-25 15:03:51 -0700216
217 private void disconnectCallAndShowErrorDialog(Call call, int errorCode) {
218 call.disconnect();
219 final Intent errorIntent = new Intent(this, ErrorDialogActivity.class);
220 int errorMessageId = -1;
221 switch (errorCode) {
222 case DisconnectCause.INVALID_NUMBER:
223 errorMessageId = R.string.outgoing_call_error_no_phone_number_supplied;
224 break;
225 case DisconnectCause.VOICEMAIL_NUMBER_MISSING:
226 errorIntent.putExtra(ErrorDialogActivity.SHOW_MISSING_VOICEMAIL_NO_DIALOG_EXTRA,
227 true);
228 break;
229 }
230 if (errorMessageId != -1) {
231 errorIntent.putExtra(ErrorDialogActivity.ERROR_MESSAGE_ID_EXTRA, errorMessageId);
232 }
233 startActivity(errorIntent);
234 }
Santos Cordon10e68322013-12-12 16:06:56 -0800235}