blob: b931644ac43cef8658f0f20d404bd847aa0fa52b [file] [log] [blame]
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001/*
2 * Copyright (C) 2006 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.phone;
18
19import android.app.ActivityManager;
20import android.app.AppOpsManager;
Sailesh Nepalbd76e4e2013-10-27 13:59:44 -070021import android.content.ComponentName;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070022import android.content.Context;
23import android.content.Intent;
24import android.net.ConnectivityManager;
25import android.net.Uri;
26import android.os.AsyncResult;
27import android.os.Binder;
28import android.os.Bundle;
29import android.os.Handler;
Gabriel Peal36ebb0d2014-03-20 09:20:43 -070030import android.os.IBinder;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070031import android.os.Looper;
32import android.os.Message;
33import android.os.Process;
Gabriel Peal36ebb0d2014-03-20 09:20:43 -070034import android.os.RemoteException;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070035import android.os.ServiceManager;
36import android.os.UserHandle;
Ihab Awadf2177b72013-11-25 13:33:23 -080037import android.provider.Settings;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070038import android.telephony.CellInfo;
Jake Hambye994d462014-02-03 13:10:13 -080039import android.telephony.NeighboringCellInfo;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070040import android.telephony.ServiceState;
Ihab Awadf2177b72013-11-25 13:33:23 -080041import android.telephony.TelephonyManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070042import android.text.TextUtils;
43import android.util.Log;
Jake Hambye994d462014-02-03 13:10:13 -080044import android.util.Pair;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070045
Shishir Agrawal566b7612013-10-28 14:41:00 -070046import com.android.internal.telephony.CallManager;
47import com.android.internal.telephony.CommandException;
Gabriel Peal36ebb0d2014-03-20 09:20:43 -070048import com.android.internal.telephony.Connection;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070049import com.android.internal.telephony.DefaultPhoneNotifier;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070050import com.android.internal.telephony.ITelephony;
Gabriel Peal36ebb0d2014-03-20 09:20:43 -070051import com.android.internal.telephony.ITelephonyListener;
Jake Hambye994d462014-02-03 13:10:13 -080052import com.android.internal.telephony.IccCard;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070053import com.android.internal.telephony.Phone;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070054import com.android.internal.telephony.PhoneConstants;
Shishir Agrawal566b7612013-10-28 14:41:00 -070055import com.android.internal.telephony.uicc.IccIoResult;
56import com.android.internal.telephony.uicc.IccUtils;
57import com.android.internal.telephony.uicc.UiccController;
Jake Hambye994d462014-02-03 13:10:13 -080058import com.android.internal.util.HexDump;
Gabriel Peal36ebb0d2014-03-20 09:20:43 -070059import com.android.services.telephony.common.Call;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070060
Santos Cordon7d4ddf62013-07-10 11:58:08 -070061import java.util.ArrayList;
Gabriel Peal36ebb0d2014-03-20 09:20:43 -070062import java.util.HashMap;
63import java.util.Iterator;
Jake Hambye994d462014-02-03 13:10:13 -080064import java.util.List;
Gabriel Peal36ebb0d2014-03-20 09:20:43 -070065import java.util.Map;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070066
67/**
68 * Implementation of the ITelephony interface.
69 */
Gabriel Peal36ebb0d2014-03-20 09:20:43 -070070public class PhoneInterfaceManager extends ITelephony.Stub implements CallModeler.Listener {
Santos Cordon7d4ddf62013-07-10 11:58:08 -070071 private static final String LOG_TAG = "PhoneInterfaceManager";
72 private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
73 private static final boolean DBG_LOC = false;
74
75 // Message codes used with mMainThreadHandler
76 private static final int CMD_HANDLE_PIN_MMI = 1;
77 private static final int CMD_HANDLE_NEIGHBORING_CELL = 2;
78 private static final int EVENT_NEIGHBORING_CELL_DONE = 3;
79 private static final int CMD_ANSWER_RINGING_CALL = 4;
80 private static final int CMD_END_CALL = 5; // not used yet
81 private static final int CMD_SILENCE_RINGER = 6;
Shishir Agrawal566b7612013-10-28 14:41:00 -070082 private static final int CMD_TRANSMIT_APDU = 7;
83 private static final int EVENT_TRANSMIT_APDU_DONE = 8;
84 private static final int CMD_OPEN_CHANNEL = 9;
85 private static final int EVENT_OPEN_CHANNEL_DONE = 10;
86 private static final int CMD_CLOSE_CHANNEL = 11;
87 private static final int EVENT_CLOSE_CHANNEL_DONE = 12;
Jake Hambye994d462014-02-03 13:10:13 -080088 private static final int CMD_NV_READ_ITEM = 13;
89 private static final int EVENT_NV_READ_ITEM_DONE = 14;
90 private static final int CMD_NV_WRITE_ITEM = 15;
91 private static final int EVENT_NV_WRITE_ITEM_DONE = 16;
92 private static final int CMD_NV_WRITE_CDMA_PRL = 17;
93 private static final int EVENT_NV_WRITE_CDMA_PRL_DONE = 18;
94 private static final int CMD_NV_RESET_CONFIG = 19;
95 private static final int EVENT_NV_RESET_CONFIG_DONE = 20;
Jake Hamby7c27be32014-03-03 13:25:59 -080096 private static final int CMD_GET_PREFERRED_NETWORK_TYPE = 21;
97 private static final int EVENT_GET_PREFERRED_NETWORK_TYPE_DONE = 22;
98 private static final int CMD_SET_PREFERRED_NETWORK_TYPE = 23;
99 private static final int EVENT_SET_PREFERRED_NETWORK_TYPE_DONE = 24;
Sailesh Nepal35b59452014-03-06 09:26:56 -0800100 private static final int CMD_SEND_ENVELOPE = 25;
101 private static final int EVENT_SEND_ENVELOPE_DONE = 26;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700102
103 /** The singleton instance. */
104 private static PhoneInterfaceManager sInstance;
105
106 PhoneGlobals mApp;
107 Phone mPhone;
108 CallManager mCM;
109 AppOpsManager mAppOps;
110 MainThreadHandler mMainThreadHandler;
Santos Cordon406c0342013-08-28 00:07:47 -0700111 CallHandlerServiceProxy mCallHandlerService;
Gabriel Peal36ebb0d2014-03-20 09:20:43 -0700112 CallModeler mCallModeler;
113 DTMFTonePlayer mDtmfTonePlayer;
114 Handler mDtmfStopHandler = new Handler();
115 Runnable mDtmfStopRunnable;
116
117 private final List<ITelephonyListener> mListeners = new ArrayList<ITelephonyListener>();
118 private final Map<IBinder, TelephonyListenerDeathRecipient> mDeathRecipients =
119 new HashMap<IBinder, TelephonyListenerDeathRecipient>();
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700120
121 /**
Shishir Agrawal566b7612013-10-28 14:41:00 -0700122 * A request object to use for transmitting data to an ICC.
123 */
124 private static final class IccAPDUArgument {
125 public int channel, cla, command, p1, p2, p3;
126 public String data;
127
128 public IccAPDUArgument(int channel, int cla, int command,
129 int p1, int p2, int p3, String data) {
130 this.channel = channel;
131 this.cla = cla;
132 this.command = command;
133 this.p1 = p1;
134 this.p2 = p2;
135 this.p3 = p3;
136 this.data = data;
137 }
138 }
139
140 /**
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700141 * A request object for use with {@link MainThreadHandler}. Requesters should wait() on the
142 * request after sending. The main thread will notify the request when it is complete.
143 */
144 private static final class MainThreadRequest {
145 /** The argument to use for the request */
146 public Object argument;
147 /** The result of the request that is run on the main thread */
148 public Object result;
149
150 public MainThreadRequest(Object argument) {
151 this.argument = argument;
152 }
153 }
154
Sailesh Nepalcc0375f2013-11-13 09:15:18 -0800155 private static final class IncomingThirdPartyCallArgs {
156 public final ComponentName component;
157 public final String callId;
158 public final String callerDisplayName;
159
160 public IncomingThirdPartyCallArgs(ComponentName component, String callId,
161 String callerDisplayName) {
162 this.component = component;
163 this.callId = callId;
164 this.callerDisplayName = callerDisplayName;
165 }
166 }
167
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700168 /**
169 * A handler that processes messages on the main thread in the phone process. Since many
170 * of the Phone calls are not thread safe this is needed to shuttle the requests from the
171 * inbound binder threads to the main thread in the phone process. The Binder thread
172 * may provide a {@link MainThreadRequest} object in the msg.obj field that they are waiting
173 * on, which will be notified when the operation completes and will contain the result of the
174 * request.
175 *
176 * <p>If a MainThreadRequest object is provided in the msg.obj field,
177 * note that request.result must be set to something non-null for the calling thread to
178 * unblock.
179 */
180 private final class MainThreadHandler extends Handler {
181 @Override
182 public void handleMessage(Message msg) {
183 MainThreadRequest request;
184 Message onCompleted;
185 AsyncResult ar;
186
187 switch (msg.what) {
188 case CMD_HANDLE_PIN_MMI:
189 request = (MainThreadRequest) msg.obj;
Jake Hambye994d462014-02-03 13:10:13 -0800190 request.result = mPhone.handlePinMmi((String) request.argument);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700191 // Wake up the requesting thread
192 synchronized (request) {
193 request.notifyAll();
194 }
195 break;
196
197 case CMD_HANDLE_NEIGHBORING_CELL:
198 request = (MainThreadRequest) msg.obj;
199 onCompleted = obtainMessage(EVENT_NEIGHBORING_CELL_DONE,
200 request);
201 mPhone.getNeighboringCids(onCompleted);
202 break;
203
204 case EVENT_NEIGHBORING_CELL_DONE:
205 ar = (AsyncResult) msg.obj;
206 request = (MainThreadRequest) ar.userObj;
207 if (ar.exception == null && ar.result != null) {
208 request.result = ar.result;
209 } else {
210 // create an empty list to notify the waiting thread
Jake Hambye994d462014-02-03 13:10:13 -0800211 request.result = new ArrayList<NeighboringCellInfo>(0);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700212 }
213 // Wake up the requesting thread
214 synchronized (request) {
215 request.notifyAll();
216 }
217 break;
218
219 case CMD_ANSWER_RINGING_CALL:
220 answerRingingCallInternal();
221 break;
222
223 case CMD_SILENCE_RINGER:
224 silenceRingerInternal();
225 break;
226
227 case CMD_END_CALL:
228 request = (MainThreadRequest) msg.obj;
Jake Hambye994d462014-02-03 13:10:13 -0800229 boolean hungUp;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700230 int phoneType = mPhone.getPhoneType();
231 if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
232 // CDMA: If the user presses the Power button we treat it as
233 // ending the complete call session
234 hungUp = PhoneUtils.hangupRingingAndActive(mPhone);
235 } else if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
236 // GSM: End the call as per the Phone state
237 hungUp = PhoneUtils.hangup(mCM);
238 } else {
239 throw new IllegalStateException("Unexpected phone type: " + phoneType);
240 }
241 if (DBG) log("CMD_END_CALL: " + (hungUp ? "hung up!" : "no call to hang up"));
242 request.result = hungUp;
243 // Wake up the requesting thread
244 synchronized (request) {
245 request.notifyAll();
246 }
247 break;
248
Shishir Agrawal566b7612013-10-28 14:41:00 -0700249 case CMD_TRANSMIT_APDU:
250 request = (MainThreadRequest) msg.obj;
251 IccAPDUArgument argument = (IccAPDUArgument) request.argument;
252 onCompleted = obtainMessage(EVENT_TRANSMIT_APDU_DONE, request);
253 UiccController.getInstance().getUiccCard().iccTransmitApduLogicalChannel(
254 argument.channel, argument.cla, argument.command,
255 argument.p1, argument.p2, argument.p3, argument.data,
256 onCompleted);
257 break;
258
259 case EVENT_TRANSMIT_APDU_DONE:
260 ar = (AsyncResult) msg.obj;
261 request = (MainThreadRequest) ar.userObj;
262 if (ar.exception == null && ar.result != null) {
263 request.result = ar.result;
264 } else {
265 request.result = new IccIoResult(0x6F, 0, (byte[])null);
266 if (ar.result == null) {
267 loge("iccTransmitApduLogicalChannel: Empty response");
Jake Hambye994d462014-02-03 13:10:13 -0800268 } else if (ar.exception instanceof CommandException) {
Shishir Agrawal566b7612013-10-28 14:41:00 -0700269 loge("iccTransmitApduLogicalChannel: CommandException: " +
Jake Hambye994d462014-02-03 13:10:13 -0800270 ar.exception);
Shishir Agrawal566b7612013-10-28 14:41:00 -0700271 } else {
272 loge("iccTransmitApduLogicalChannel: Unknown exception");
273 }
274 }
275 synchronized (request) {
276 request.notifyAll();
277 }
278 break;
279
Derek Tan4d5e5c12014-02-04 11:54:58 -0800280 case CMD_SEND_ENVELOPE:
281 request = (MainThreadRequest) msg.obj;
282 onCompleted = obtainMessage(EVENT_SEND_ENVELOPE_DONE, request);
Shishir Agrawal9f9877d2014-03-14 09:36:27 -0700283 UiccController.getInstance().getUiccCard().sendEnvelopeWithStatus(
Derek Tan4d5e5c12014-02-04 11:54:58 -0800284 (String)request.argument, onCompleted);
285 break;
286
287 case EVENT_SEND_ENVELOPE_DONE:
288 ar = (AsyncResult) msg.obj;
289 request = (MainThreadRequest) ar.userObj;
Shishir Agrawal9f9877d2014-03-14 09:36:27 -0700290 if (ar.exception == null && ar.result != null) {
291 request.result = ar.result;
Derek Tan4d5e5c12014-02-04 11:54:58 -0800292 } else {
Shishir Agrawal9f9877d2014-03-14 09:36:27 -0700293 request.result = new IccIoResult(0x6F, 0, (byte[])null);
294 if (ar.result == null) {
295 loge("sendEnvelopeWithStatus: Empty response");
296 } else if (ar.exception instanceof CommandException) {
297 loge("sendEnvelopeWithStatus: CommandException: " +
298 ar.exception);
299 } else {
300 loge("sendEnvelopeWithStatus: exception:" + ar.exception);
301 }
Derek Tan4d5e5c12014-02-04 11:54:58 -0800302 }
303 synchronized (request) {
304 request.notifyAll();
305 }
306 break;
307
Shishir Agrawal566b7612013-10-28 14:41:00 -0700308 case CMD_OPEN_CHANNEL:
309 request = (MainThreadRequest) msg.obj;
310 onCompleted = obtainMessage(EVENT_OPEN_CHANNEL_DONE, request);
311 UiccController.getInstance().getUiccCard().iccOpenLogicalChannel(
312 (String)request.argument, onCompleted);
313 break;
314
315 case EVENT_OPEN_CHANNEL_DONE:
316 ar = (AsyncResult) msg.obj;
317 request = (MainThreadRequest) ar.userObj;
318 if (ar.exception == null && ar.result != null) {
Jake Hambye994d462014-02-03 13:10:13 -0800319 request.result = ((int[]) ar.result)[0];
Shishir Agrawal566b7612013-10-28 14:41:00 -0700320 } else {
Jake Hambye994d462014-02-03 13:10:13 -0800321 request.result = -1;
Shishir Agrawal566b7612013-10-28 14:41:00 -0700322 if (ar.result == null) {
323 loge("iccOpenLogicalChannel: Empty response");
Jake Hambye994d462014-02-03 13:10:13 -0800324 } else if (ar.exception instanceof CommandException) {
Shishir Agrawal566b7612013-10-28 14:41:00 -0700325 loge("iccOpenLogicalChannel: CommandException: " +
Jake Hambye994d462014-02-03 13:10:13 -0800326 ar.exception);
Shishir Agrawal566b7612013-10-28 14:41:00 -0700327 } else {
328 loge("iccOpenLogicalChannel: Unknown exception");
329 }
330 }
331 synchronized (request) {
332 request.notifyAll();
333 }
334 break;
335
336 case CMD_CLOSE_CHANNEL:
337 request = (MainThreadRequest) msg.obj;
338 onCompleted = obtainMessage(EVENT_CLOSE_CHANNEL_DONE,
339 request);
340 UiccController.getInstance().getUiccCard().iccCloseLogicalChannel(
Jake Hambye994d462014-02-03 13:10:13 -0800341 (Integer) request.argument,
Shishir Agrawal566b7612013-10-28 14:41:00 -0700342 onCompleted);
343 break;
344
345 case EVENT_CLOSE_CHANNEL_DONE:
Jake Hambye994d462014-02-03 13:10:13 -0800346 handleNullReturnEvent(msg, "iccCloseLogicalChannel");
347 break;
348
349 case CMD_NV_READ_ITEM:
350 request = (MainThreadRequest) msg.obj;
351 onCompleted = obtainMessage(EVENT_NV_READ_ITEM_DONE, request);
352 mPhone.nvReadItem((Integer) request.argument, onCompleted);
353 break;
354
355 case EVENT_NV_READ_ITEM_DONE:
Shishir Agrawal566b7612013-10-28 14:41:00 -0700356 ar = (AsyncResult) msg.obj;
357 request = (MainThreadRequest) ar.userObj;
Jake Hambye994d462014-02-03 13:10:13 -0800358 if (ar.exception == null && ar.result != null) {
359 request.result = ar.result; // String
Shishir Agrawal566b7612013-10-28 14:41:00 -0700360 } else {
Jake Hambye994d462014-02-03 13:10:13 -0800361 request.result = "";
362 if (ar.result == null) {
363 loge("nvReadItem: Empty response");
364 } else if (ar.exception instanceof CommandException) {
365 loge("nvReadItem: CommandException: " +
366 ar.exception);
Shishir Agrawal566b7612013-10-28 14:41:00 -0700367 } else {
Jake Hambye994d462014-02-03 13:10:13 -0800368 loge("nvReadItem: Unknown exception");
Shishir Agrawal566b7612013-10-28 14:41:00 -0700369 }
370 }
371 synchronized (request) {
372 request.notifyAll();
373 }
374 break;
375
Jake Hambye994d462014-02-03 13:10:13 -0800376 case CMD_NV_WRITE_ITEM:
377 request = (MainThreadRequest) msg.obj;
378 onCompleted = obtainMessage(EVENT_NV_WRITE_ITEM_DONE, request);
379 Pair<Integer, String> idValue = (Pair<Integer, String>) request.argument;
380 mPhone.nvWriteItem(idValue.first, idValue.second, onCompleted);
381 break;
382
383 case EVENT_NV_WRITE_ITEM_DONE:
384 handleNullReturnEvent(msg, "nvWriteItem");
385 break;
386
387 case CMD_NV_WRITE_CDMA_PRL:
388 request = (MainThreadRequest) msg.obj;
389 onCompleted = obtainMessage(EVENT_NV_WRITE_CDMA_PRL_DONE, request);
390 mPhone.nvWriteCdmaPrl((byte[]) request.argument, onCompleted);
391 break;
392
393 case EVENT_NV_WRITE_CDMA_PRL_DONE:
394 handleNullReturnEvent(msg, "nvWriteCdmaPrl");
395 break;
396
397 case CMD_NV_RESET_CONFIG:
398 request = (MainThreadRequest) msg.obj;
399 onCompleted = obtainMessage(EVENT_NV_RESET_CONFIG_DONE, request);
400 mPhone.nvResetConfig((Integer) request.argument, onCompleted);
401 break;
402
403 case EVENT_NV_RESET_CONFIG_DONE:
404 handleNullReturnEvent(msg, "nvResetConfig");
405 break;
406
Jake Hamby7c27be32014-03-03 13:25:59 -0800407 case CMD_GET_PREFERRED_NETWORK_TYPE:
408 request = (MainThreadRequest) msg.obj;
409 onCompleted = obtainMessage(EVENT_GET_PREFERRED_NETWORK_TYPE_DONE, request);
410 mPhone.getPreferredNetworkType(onCompleted);
411 break;
412
413 case EVENT_GET_PREFERRED_NETWORK_TYPE_DONE:
414 ar = (AsyncResult) msg.obj;
415 request = (MainThreadRequest) ar.userObj;
416 if (ar.exception == null && ar.result != null) {
417 request.result = ar.result; // Integer
418 } else {
419 request.result = -1;
420 if (ar.result == null) {
421 loge("getPreferredNetworkType: Empty response");
422 } else if (ar.exception instanceof CommandException) {
423 loge("getPreferredNetworkType: CommandException: " +
424 ar.exception);
425 } else {
426 loge("getPreferredNetworkType: Unknown exception");
427 }
428 }
429 synchronized (request) {
430 request.notifyAll();
431 }
432 break;
433
434 case CMD_SET_PREFERRED_NETWORK_TYPE:
435 request = (MainThreadRequest) msg.obj;
436 onCompleted = obtainMessage(EVENT_SET_PREFERRED_NETWORK_TYPE_DONE, request);
437 int networkType = (Integer) request.argument;
438 mPhone.setPreferredNetworkType(networkType, onCompleted);
439 break;
440
441 case EVENT_SET_PREFERRED_NETWORK_TYPE_DONE:
442 handleNullReturnEvent(msg, "setPreferredNetworkType");
443 break;
444
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700445 default:
446 Log.w(LOG_TAG, "MainThreadHandler: unexpected message code: " + msg.what);
447 break;
448 }
449 }
Jake Hambye994d462014-02-03 13:10:13 -0800450
451 private void handleNullReturnEvent(Message msg, String command) {
452 AsyncResult ar = (AsyncResult) msg.obj;
453 MainThreadRequest request = (MainThreadRequest) ar.userObj;
454 if (ar.exception == null) {
455 request.result = true;
456 } else {
457 request.result = false;
458 if (ar.exception instanceof CommandException) {
459 loge(command + ": CommandException: " + ar.exception);
460 } else {
461 loge(command + ": Unknown exception");
462 }
463 }
464 synchronized (request) {
465 request.notifyAll();
466 }
467 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700468 }
469
470 /**
471 * Posts the specified command to be executed on the main thread,
472 * waits for the request to complete, and returns the result.
473 * @see #sendRequestAsync
474 */
475 private Object sendRequest(int command, Object argument) {
476 if (Looper.myLooper() == mMainThreadHandler.getLooper()) {
477 throw new RuntimeException("This method will deadlock if called from the main thread.");
478 }
479
480 MainThreadRequest request = new MainThreadRequest(argument);
481 Message msg = mMainThreadHandler.obtainMessage(command, request);
482 msg.sendToTarget();
483
484 // Wait for the request to complete
485 synchronized (request) {
486 while (request.result == null) {
487 try {
488 request.wait();
489 } catch (InterruptedException e) {
490 // Do nothing, go back and wait until the request is complete
491 }
492 }
493 }
494 return request.result;
495 }
496
497 /**
498 * Asynchronous ("fire and forget") version of sendRequest():
499 * Posts the specified command to be executed on the main thread, and
500 * returns immediately.
501 * @see #sendRequest
502 */
503 private void sendRequestAsync(int command) {
504 mMainThreadHandler.sendEmptyMessage(command);
505 }
506
507 /**
Sailesh Nepalbd76e4e2013-10-27 13:59:44 -0700508 * Same as {@link #sendRequestAsync(int)} except it takes an argument.
509 * @see {@link #sendRequest(int,Object)}
510 */
511 private void sendRequestAsync(int command, Object argument) {
512 MainThreadRequest request = new MainThreadRequest(argument);
513 Message msg = mMainThreadHandler.obtainMessage(command, request);
514 msg.sendToTarget();
515 }
516
517 /**
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700518 * Initialize the singleton PhoneInterfaceManager instance.
519 * This is only done once, at startup, from PhoneApp.onCreate().
520 */
Santos Cordon406c0342013-08-28 00:07:47 -0700521 /* package */ static PhoneInterfaceManager init(PhoneGlobals app, Phone phone,
Gabriel Peal36ebb0d2014-03-20 09:20:43 -0700522 CallHandlerServiceProxy callHandlerService, CallModeler callModeler,
523 DTMFTonePlayer dtmfTonePlayer) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700524 synchronized (PhoneInterfaceManager.class) {
525 if (sInstance == null) {
Gabriel Peal36ebb0d2014-03-20 09:20:43 -0700526 sInstance = new PhoneInterfaceManager(app, phone, callHandlerService, callModeler,
527 dtmfTonePlayer);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700528 } else {
529 Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance);
530 }
531 return sInstance;
532 }
533 }
534
535 /** Private constructor; @see init() */
Santos Cordon406c0342013-08-28 00:07:47 -0700536 private PhoneInterfaceManager(PhoneGlobals app, Phone phone,
Gabriel Peal36ebb0d2014-03-20 09:20:43 -0700537 CallHandlerServiceProxy callHandlerService, CallModeler callModeler,
538 DTMFTonePlayer dtmfTonePlayer) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700539 mApp = app;
540 mPhone = phone;
541 mCM = PhoneGlobals.getInstance().mCM;
542 mAppOps = (AppOpsManager)app.getSystemService(Context.APP_OPS_SERVICE);
543 mMainThreadHandler = new MainThreadHandler();
Santos Cordon406c0342013-08-28 00:07:47 -0700544 mCallHandlerService = callHandlerService;
Gabriel Peal36ebb0d2014-03-20 09:20:43 -0700545 mCallModeler = callModeler;
546 mCallModeler.addListener(this);
547 mDtmfTonePlayer = dtmfTonePlayer;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700548 publish();
549 }
550
551 private void publish() {
552 if (DBG) log("publish: " + this);
553
554 ServiceManager.addService("phone", this);
555 }
556
557 //
558 // Implementation of the ITelephony interface.
559 //
560
561 public void dial(String number) {
562 if (DBG) log("dial: " + number);
563 // No permission check needed here: This is just a wrapper around the
564 // ACTION_DIAL intent, which is available to any app since it puts up
565 // the UI before it does anything.
566
567 String url = createTelUrl(number);
568 if (url == null) {
569 return;
570 }
571
572 // PENDING: should we just silently fail if phone is offhook or ringing?
573 PhoneConstants.State state = mCM.getState();
574 if (state != PhoneConstants.State.OFFHOOK && state != PhoneConstants.State.RINGING) {
575 Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse(url));
576 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
577 mApp.startActivity(intent);
578 }
579 }
580
581 public void call(String callingPackage, String number) {
582 if (DBG) log("call: " + number);
583
584 // This is just a wrapper around the ACTION_CALL intent, but we still
585 // need to do a permission check since we're calling startActivity()
586 // from the context of the phone app.
587 enforceCallPermission();
588
589 if (mAppOps.noteOp(AppOpsManager.OP_CALL_PHONE, Binder.getCallingUid(), callingPackage)
590 != AppOpsManager.MODE_ALLOWED) {
591 return;
592 }
593
594 String url = createTelUrl(number);
595 if (url == null) {
596 return;
597 }
598
599 Intent intent = new Intent(Intent.ACTION_CALL, Uri.parse(url));
600 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
601 mApp.startActivity(intent);
602 }
603
604 private boolean showCallScreenInternal(boolean specifyInitialDialpadState,
Makoto Onukibcf20992013-09-12 17:59:30 -0700605 boolean showDialpad) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700606 if (!PhoneGlobals.sVoiceCapable) {
607 // Never allow the InCallScreen to appear on data-only devices.
608 return false;
609 }
610 if (isIdle()) {
611 return false;
612 }
613 // If the phone isn't idle then go to the in-call screen
614 long callingId = Binder.clearCallingIdentity();
Santos Cordon406c0342013-08-28 00:07:47 -0700615
Makoto Onukibcf20992013-09-12 17:59:30 -0700616 mCallHandlerService.bringToForeground(showDialpad);
Santos Cordon406c0342013-08-28 00:07:47 -0700617
618 Binder.restoreCallingIdentity(callingId);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700619 return true;
620 }
621
622 // Show the in-call screen without specifying the initial dialpad state.
623 public boolean showCallScreen() {
624 return showCallScreenInternal(false, false);
625 }
626
627 // The variation of showCallScreen() that specifies the initial dialpad state.
628 // (Ideally this would be called showCallScreen() too, just with a different
629 // signature, but AIDL doesn't allow that.)
630 public boolean showCallScreenWithDialpad(boolean showDialpad) {
631 return showCallScreenInternal(true, showDialpad);
632 }
633
634 /**
635 * End a call based on call state
636 * @return true is a call was ended
637 */
638 public boolean endCall() {
639 enforceCallPermission();
640 return (Boolean) sendRequest(CMD_END_CALL, null);
641 }
642
643 public void answerRingingCall() {
644 if (DBG) log("answerRingingCall...");
645 // TODO: there should eventually be a separate "ANSWER_PHONE" permission,
646 // but that can probably wait till the big TelephonyManager API overhaul.
647 // For now, protect this call with the MODIFY_PHONE_STATE permission.
648 enforceModifyPermission();
649 sendRequestAsync(CMD_ANSWER_RINGING_CALL);
650 }
651
652 /**
653 * Make the actual telephony calls to implement answerRingingCall().
654 * This should only be called from the main thread of the Phone app.
655 * @see #answerRingingCall
656 *
657 * TODO: it would be nice to return true if we answered the call, or
658 * false if there wasn't actually a ringing incoming call, or some
659 * other error occurred. (In other words, pass back the return value
660 * from PhoneUtils.answerCall() or PhoneUtils.answerAndEndActive().)
661 * But that would require calling this method via sendRequest() rather
662 * than sendRequestAsync(), and right now we don't actually *need* that
663 * return value, so let's just return void for now.
664 */
665 private void answerRingingCallInternal() {
666 final boolean hasRingingCall = !mPhone.getRingingCall().isIdle();
667 if (hasRingingCall) {
668 final boolean hasActiveCall = !mPhone.getForegroundCall().isIdle();
669 final boolean hasHoldingCall = !mPhone.getBackgroundCall().isIdle();
670 if (hasActiveCall && hasHoldingCall) {
671 // Both lines are in use!
672 // TODO: provide a flag to let the caller specify what
673 // policy to use if both lines are in use. (The current
674 // behavior is hardwired to "answer incoming, end ongoing",
675 // which is how the CALL button is specced to behave.)
676 PhoneUtils.answerAndEndActive(mCM, mCM.getFirstActiveRingingCall());
677 return;
678 } else {
679 // answerCall() will automatically hold the current active
680 // call, if there is one.
681 PhoneUtils.answerCall(mCM.getFirstActiveRingingCall());
682 return;
683 }
684 } else {
685 // No call was ringing.
686 return;
687 }
688 }
689
690 public void silenceRinger() {
691 if (DBG) log("silenceRinger...");
692 // TODO: find a more appropriate permission to check here.
693 // (That can probably wait till the big TelephonyManager API overhaul.
694 // For now, protect this call with the MODIFY_PHONE_STATE permission.)
695 enforceModifyPermission();
696 sendRequestAsync(CMD_SILENCE_RINGER);
697 }
698
699 /**
700 * Internal implemenation of silenceRinger().
701 * This should only be called from the main thread of the Phone app.
702 * @see #silenceRinger
703 */
704 private void silenceRingerInternal() {
705 if ((mCM.getState() == PhoneConstants.State.RINGING)
706 && mApp.notifier.isRinging()) {
707 // Ringer is actually playing, so silence it.
708 if (DBG) log("silenceRingerInternal: silencing...");
709 mApp.notifier.silenceRinger();
710 }
711 }
712
713 public boolean isOffhook() {
714 return (mCM.getState() == PhoneConstants.State.OFFHOOK);
715 }
716
717 public boolean isRinging() {
718 return (mCM.getState() == PhoneConstants.State.RINGING);
719 }
720
721 public boolean isIdle() {
722 return (mCM.getState() == PhoneConstants.State.IDLE);
723 }
724
725 public boolean isSimPinEnabled() {
726 enforceReadPermission();
727 return (PhoneGlobals.getInstance().isSimPinEnabled());
728 }
729
730 public boolean supplyPin(String pin) {
Wink Saville9de0f752013-10-22 19:04:03 -0700731 int [] resultArray = supplyPinReportResult(pin);
732 return (resultArray[0] == PhoneConstants.PIN_RESULT_SUCCESS) ? true : false;
733 }
734
735 public boolean supplyPuk(String puk, String pin) {
736 int [] resultArray = supplyPukReportResult(puk, pin);
737 return (resultArray[0] == PhoneConstants.PIN_RESULT_SUCCESS) ? true : false;
738 }
739
740 /** {@hide} */
741 public int[] supplyPinReportResult(String pin) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700742 enforceModifyPermission();
743 final UnlockSim checkSimPin = new UnlockSim(mPhone.getIccCard());
744 checkSimPin.start();
745 return checkSimPin.unlockSim(null, pin);
746 }
747
Wink Saville9de0f752013-10-22 19:04:03 -0700748 /** {@hide} */
749 public int[] supplyPukReportResult(String puk, String pin) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700750 enforceModifyPermission();
751 final UnlockSim checkSimPuk = new UnlockSim(mPhone.getIccCard());
752 checkSimPuk.start();
753 return checkSimPuk.unlockSim(puk, pin);
754 }
755
756 /**
Wink Saville9de0f752013-10-22 19:04:03 -0700757 * Helper thread to turn async call to SimCard#supplyPin into
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700758 * a synchronous one.
759 */
760 private static class UnlockSim extends Thread {
761
762 private final IccCard mSimCard;
763
764 private boolean mDone = false;
Wink Saville9de0f752013-10-22 19:04:03 -0700765 private int mResult = PhoneConstants.PIN_GENERAL_FAILURE;
766 private int mRetryCount = -1;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700767
768 // For replies from SimCard interface
769 private Handler mHandler;
770
771 // For async handler to identify request type
772 private static final int SUPPLY_PIN_COMPLETE = 100;
773
774 public UnlockSim(IccCard simCard) {
775 mSimCard = simCard;
776 }
777
778 @Override
779 public void run() {
780 Looper.prepare();
781 synchronized (UnlockSim.this) {
782 mHandler = new Handler() {
783 @Override
784 public void handleMessage(Message msg) {
785 AsyncResult ar = (AsyncResult) msg.obj;
786 switch (msg.what) {
787 case SUPPLY_PIN_COMPLETE:
788 Log.d(LOG_TAG, "SUPPLY_PIN_COMPLETE");
789 synchronized (UnlockSim.this) {
Wink Saville9de0f752013-10-22 19:04:03 -0700790 mRetryCount = msg.arg1;
791 if (ar.exception != null) {
792 if (ar.exception instanceof CommandException &&
793 ((CommandException)(ar.exception)).getCommandError()
794 == CommandException.Error.PASSWORD_INCORRECT) {
795 mResult = PhoneConstants.PIN_PASSWORD_INCORRECT;
796 } else {
797 mResult = PhoneConstants.PIN_GENERAL_FAILURE;
798 }
799 } else {
800 mResult = PhoneConstants.PIN_RESULT_SUCCESS;
801 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700802 mDone = true;
803 UnlockSim.this.notifyAll();
804 }
805 break;
806 }
807 }
808 };
809 UnlockSim.this.notifyAll();
810 }
811 Looper.loop();
812 }
813
814 /*
815 * Use PIN or PUK to unlock SIM card
816 *
817 * If PUK is null, unlock SIM card with PIN
818 *
819 * If PUK is not null, unlock SIM card with PUK and set PIN code
820 */
Wink Saville9de0f752013-10-22 19:04:03 -0700821 synchronized int[] unlockSim(String puk, String pin) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700822
823 while (mHandler == null) {
824 try {
825 wait();
826 } catch (InterruptedException e) {
827 Thread.currentThread().interrupt();
828 }
829 }
830 Message callback = Message.obtain(mHandler, SUPPLY_PIN_COMPLETE);
831
832 if (puk == null) {
833 mSimCard.supplyPin(pin, callback);
834 } else {
835 mSimCard.supplyPuk(puk, pin, callback);
836 }
837
838 while (!mDone) {
839 try {
840 Log.d(LOG_TAG, "wait for done");
841 wait();
842 } catch (InterruptedException e) {
843 // Restore the interrupted status
844 Thread.currentThread().interrupt();
845 }
846 }
847 Log.d(LOG_TAG, "done");
Wink Saville9de0f752013-10-22 19:04:03 -0700848 int[] resultArray = new int[2];
849 resultArray[0] = mResult;
850 resultArray[1] = mRetryCount;
851 return resultArray;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700852 }
853 }
854
855 public void updateServiceLocation() {
856 // No permission check needed here: this call is harmless, and it's
857 // needed for the ServiceState.requestStateUpdate() call (which is
858 // already intentionally exposed to 3rd parties.)
859 mPhone.updateServiceLocation();
860 }
861
862 public boolean isRadioOn() {
863 return mPhone.getServiceState().getVoiceRegState() != ServiceState.STATE_POWER_OFF;
864 }
865
866 public void toggleRadioOnOff() {
867 enforceModifyPermission();
868 mPhone.setRadioPower(!isRadioOn());
869 }
870 public boolean setRadio(boolean turnOn) {
871 enforceModifyPermission();
872 if ((mPhone.getServiceState().getVoiceRegState() != ServiceState.STATE_POWER_OFF) != turnOn) {
873 toggleRadioOnOff();
874 }
875 return true;
876 }
877 public boolean setRadioPower(boolean turnOn) {
878 enforceModifyPermission();
879 mPhone.setRadioPower(turnOn);
880 return true;
881 }
882
883 public boolean enableDataConnectivity() {
884 enforceModifyPermission();
885 ConnectivityManager cm =
886 (ConnectivityManager)mApp.getSystemService(Context.CONNECTIVITY_SERVICE);
887 cm.setMobileDataEnabled(true);
888 return true;
889 }
890
891 public int enableApnType(String type) {
892 enforceModifyPermission();
893 return mPhone.enableApnType(type);
894 }
895
896 public int disableApnType(String type) {
897 enforceModifyPermission();
898 return mPhone.disableApnType(type);
899 }
900
901 public boolean disableDataConnectivity() {
902 enforceModifyPermission();
903 ConnectivityManager cm =
904 (ConnectivityManager)mApp.getSystemService(Context.CONNECTIVITY_SERVICE);
905 cm.setMobileDataEnabled(false);
906 return true;
907 }
908
909 public boolean isDataConnectivityPossible() {
910 return mPhone.isDataConnectivityPossible();
911 }
912
913 public boolean handlePinMmi(String dialString) {
914 enforceModifyPermission();
915 return (Boolean) sendRequest(CMD_HANDLE_PIN_MMI, dialString);
916 }
917
918 public void cancelMissedCallsNotification() {
919 enforceModifyPermission();
920 mApp.notificationMgr.cancelMissedCallNotification();
921 }
922
923 public int getCallState() {
924 return DefaultPhoneNotifier.convertCallState(mCM.getState());
925 }
926
927 public int getDataState() {
928 return DefaultPhoneNotifier.convertDataState(mPhone.getDataConnectionState());
929 }
930
931 public int getDataActivity() {
932 return DefaultPhoneNotifier.convertDataActivityState(mPhone.getDataActivityState());
933 }
934
935 @Override
936 public Bundle getCellLocation() {
937 try {
938 mApp.enforceCallingOrSelfPermission(
939 android.Manifest.permission.ACCESS_FINE_LOCATION, null);
940 } catch (SecurityException e) {
941 // If we have ACCESS_FINE_LOCATION permission, skip the check for ACCESS_COARSE_LOCATION
942 // A failure should throw the SecurityException from ACCESS_COARSE_LOCATION since this
943 // is the weaker precondition
944 mApp.enforceCallingOrSelfPermission(
945 android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
946 }
947
Jake Hambye994d462014-02-03 13:10:13 -0800948 if (checkIfCallerIsSelfOrForegroundUser()) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700949 if (DBG_LOC) log("getCellLocation: is active user");
950 Bundle data = new Bundle();
951 mPhone.getCellLocation().fillInNotifierBundle(data);
952 return data;
953 } else {
954 if (DBG_LOC) log("getCellLocation: suppress non-active user");
955 return null;
956 }
957 }
958
959 @Override
960 public void enableLocationUpdates() {
961 mApp.enforceCallingOrSelfPermission(
962 android.Manifest.permission.CONTROL_LOCATION_UPDATES, null);
963 mPhone.enableLocationUpdates();
964 }
965
966 @Override
967 public void disableLocationUpdates() {
968 mApp.enforceCallingOrSelfPermission(
969 android.Manifest.permission.CONTROL_LOCATION_UPDATES, null);
970 mPhone.disableLocationUpdates();
971 }
972
973 @Override
974 @SuppressWarnings("unchecked")
975 public List<NeighboringCellInfo> getNeighboringCellInfo(String callingPackage) {
976 try {
977 mApp.enforceCallingOrSelfPermission(
978 android.Manifest.permission.ACCESS_FINE_LOCATION, null);
979 } catch (SecurityException e) {
980 // If we have ACCESS_FINE_LOCATION permission, skip the check
981 // for ACCESS_COARSE_LOCATION
982 // A failure should throw the SecurityException from
983 // ACCESS_COARSE_LOCATION since this is the weaker precondition
984 mApp.enforceCallingOrSelfPermission(
985 android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
986 }
987
988 if (mAppOps.noteOp(AppOpsManager.OP_NEIGHBORING_CELLS, Binder.getCallingUid(),
989 callingPackage) != AppOpsManager.MODE_ALLOWED) {
990 return null;
991 }
Jake Hambye994d462014-02-03 13:10:13 -0800992 if (checkIfCallerIsSelfOrForegroundUser()) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700993 if (DBG_LOC) log("getNeighboringCellInfo: is active user");
994
995 ArrayList<NeighboringCellInfo> cells = null;
996
997 try {
998 cells = (ArrayList<NeighboringCellInfo>) sendRequest(
999 CMD_HANDLE_NEIGHBORING_CELL, null);
1000 } catch (RuntimeException e) {
Shishir Agrawal566b7612013-10-28 14:41:00 -07001001 loge("getNeighboringCellInfo " + e);
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001002 }
1003 return cells;
1004 } else {
1005 if (DBG_LOC) log("getNeighboringCellInfo: suppress non-active user");
1006 return null;
1007 }
1008 }
1009
1010
1011 @Override
1012 public List<CellInfo> getAllCellInfo() {
1013 try {
1014 mApp.enforceCallingOrSelfPermission(
1015 android.Manifest.permission.ACCESS_FINE_LOCATION, null);
1016 } catch (SecurityException e) {
1017 // If we have ACCESS_FINE_LOCATION permission, skip the check for ACCESS_COARSE_LOCATION
1018 // A failure should throw the SecurityException from ACCESS_COARSE_LOCATION since this
1019 // is the weaker precondition
1020 mApp.enforceCallingOrSelfPermission(
1021 android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
1022 }
1023
Jake Hambye994d462014-02-03 13:10:13 -08001024 if (checkIfCallerIsSelfOrForegroundUser()) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001025 if (DBG_LOC) log("getAllCellInfo: is active user");
1026 return mPhone.getAllCellInfo();
1027 } else {
1028 if (DBG_LOC) log("getAllCellInfo: suppress non-active user");
1029 return null;
1030 }
1031 }
1032
Sailesh Nepalbd76e4e2013-10-27 13:59:44 -07001033 @Override
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001034 public void setCellInfoListRate(int rateInMillis) {
1035 mPhone.setCellInfoListRate(rateInMillis);
1036 }
1037
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001038 //
1039 // Internal helper methods.
1040 //
1041
Jake Hambye994d462014-02-03 13:10:13 -08001042 private static boolean checkIfCallerIsSelfOrForegroundUser() {
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001043 boolean ok;
1044
1045 boolean self = Binder.getCallingUid() == Process.myUid();
1046 if (!self) {
1047 // Get the caller's user id then clear the calling identity
1048 // which will be restored in the finally clause.
1049 int callingUser = UserHandle.getCallingUserId();
1050 long ident = Binder.clearCallingIdentity();
1051
1052 try {
1053 // With calling identity cleared the current user is the foreground user.
1054 int foregroundUser = ActivityManager.getCurrentUser();
1055 ok = (foregroundUser == callingUser);
1056 if (DBG_LOC) {
1057 log("checkIfCallerIsSelfOrForegoundUser: foregroundUser=" + foregroundUser
1058 + " callingUser=" + callingUser + " ok=" + ok);
1059 }
1060 } catch (Exception ex) {
1061 if (DBG_LOC) loge("checkIfCallerIsSelfOrForegoundUser: Exception ex=" + ex);
1062 ok = false;
1063 } finally {
1064 Binder.restoreCallingIdentity(ident);
1065 }
1066 } else {
1067 if (DBG_LOC) log("checkIfCallerIsSelfOrForegoundUser: is self");
1068 ok = true;
1069 }
1070 if (DBG_LOC) log("checkIfCallerIsSelfOrForegoundUser: ret=" + ok);
1071 return ok;
1072 }
1073
1074 /**
1075 * Make sure the caller has the READ_PHONE_STATE permission.
1076 *
1077 * @throws SecurityException if the caller does not have the required permission
1078 */
1079 private void enforceReadPermission() {
1080 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_STATE, null);
1081 }
1082
1083 /**
1084 * Make sure the caller has the MODIFY_PHONE_STATE permission.
1085 *
1086 * @throws SecurityException if the caller does not have the required permission
1087 */
1088 private void enforceModifyPermission() {
1089 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE, null);
1090 }
1091
1092 /**
1093 * Make sure the caller has the CALL_PHONE permission.
1094 *
1095 * @throws SecurityException if the caller does not have the required permission
1096 */
1097 private void enforceCallPermission() {
1098 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.CALL_PHONE, null);
1099 }
1100
Shishir Agrawal566b7612013-10-28 14:41:00 -07001101 /**
Gabriel Peal36ebb0d2014-03-20 09:20:43 -07001102 * Make sure the caller has the READ_PRIVILEGED_PHONE_STATE permission.
1103 *
1104 * @throws SecurityException if the caller does not have the required permission
1105 */
1106 private void enforcePrivilegedPhoneStatePermission() {
1107 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
1108 null);
1109 }
1110
1111 /**
Shishir Agrawal566b7612013-10-28 14:41:00 -07001112 * Make sure the caller has SIM_COMMUNICATION permission.
1113 *
1114 * @throws SecurityException if the caller does not have the required permission.
1115 */
1116 private void enforceSimCommunicationPermission() {
1117 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.SIM_COMMUNICATION, null);
1118 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001119
1120 private String createTelUrl(String number) {
1121 if (TextUtils.isEmpty(number)) {
1122 return null;
1123 }
1124
Jake Hambye994d462014-02-03 13:10:13 -08001125 return "tel:" + number;
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001126 }
1127
Ihab Awadf9e92732013-12-05 18:02:52 -08001128 private static void log(String msg) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001129 Log.d(LOG_TAG, "[PhoneIntfMgr] " + msg);
1130 }
1131
Ihab Awadf9e92732013-12-05 18:02:52 -08001132 private static void loge(String msg) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001133 Log.e(LOG_TAG, "[PhoneIntfMgr] " + msg);
1134 }
1135
1136 public int getActivePhoneType() {
1137 return mPhone.getPhoneType();
1138 }
1139
1140 /**
1141 * Returns the CDMA ERI icon index to display
1142 */
1143 public int getCdmaEriIconIndex() {
1144 return mPhone.getCdmaEriIconIndex();
1145 }
1146
1147 /**
1148 * Returns the CDMA ERI icon mode,
1149 * 0 - ON
1150 * 1 - FLASHING
1151 */
1152 public int getCdmaEriIconMode() {
1153 return mPhone.getCdmaEriIconMode();
1154 }
1155
1156 /**
1157 * Returns the CDMA ERI text,
1158 */
1159 public String getCdmaEriText() {
1160 return mPhone.getCdmaEriText();
1161 }
1162
1163 /**
1164 * Returns true if CDMA provisioning needs to run.
1165 */
1166 public boolean needsOtaServiceProvisioning() {
1167 return mPhone.needsOtaServiceProvisioning();
1168 }
1169
1170 /**
1171 * Returns the unread count of voicemails
1172 */
1173 public int getVoiceMessageCount() {
1174 return mPhone.getVoiceMessageCount();
1175 }
1176
1177 /**
1178 * Returns the data network type
1179 *
1180 * @Deprecated to be removed Q3 2013 use {@link #getDataNetworkType}.
1181 */
1182 @Override
1183 public int getNetworkType() {
1184 return mPhone.getServiceState().getDataNetworkType();
1185 }
1186
1187 /**
1188 * Returns the data network type
1189 */
1190 @Override
1191 public int getDataNetworkType() {
1192 return mPhone.getServiceState().getDataNetworkType();
1193 }
1194
1195 /**
1196 * Returns the data network type
1197 */
1198 @Override
1199 public int getVoiceNetworkType() {
1200 return mPhone.getServiceState().getVoiceNetworkType();
1201 }
1202
1203 /**
1204 * @return true if a ICC card is present
1205 */
1206 public boolean hasIccCard() {
1207 return mPhone.getIccCard().hasIccCard();
1208 }
1209
1210 /**
1211 * Return if the current radio is LTE on CDMA. This
1212 * is a tri-state return value as for a period of time
1213 * the mode may be unknown.
1214 *
1215 * @return {@link Phone#LTE_ON_CDMA_UNKNOWN}, {@link Phone#LTE_ON_CDMA_FALSE}
Jake Hambye994d462014-02-03 13:10:13 -08001216 * or {@link Phone#LTE_ON_CDMA_TRUE}
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001217 */
1218 public int getLteOnCdmaMode() {
1219 return mPhone.getLteOnCdmaMode();
1220 }
Ihab Awadf2177b72013-11-25 13:33:23 -08001221
1222 /**
1223 * @see android.telephony.TelephonyManager.WifiCallingChoices
1224 */
1225 public int getWhenToMakeWifiCalls() {
Sailesh Nepald1e68152013-12-12 19:08:02 -08001226 return Settings.System.getInt(mPhone.getContext().getContentResolver(),
1227 Settings.System.WHEN_TO_MAKE_WIFI_CALLS, getWhenToMakeWifiCallsDefaultPreference());
Ihab Awadf2177b72013-11-25 13:33:23 -08001228 }
1229
1230 /**
1231 * @see android.telephony.TelephonyManager.WifiCallingChoices
1232 */
1233 public void setWhenToMakeWifiCalls(int preference) {
Sailesh Nepald1e68152013-12-12 19:08:02 -08001234 if (DBG) log("setWhenToMakeWifiCallsStr, storing setting = " + preference);
1235 Settings.System.putInt(mPhone.getContext().getContentResolver(),
1236 Settings.System.WHEN_TO_MAKE_WIFI_CALLS, preference);
Ihab Awadf9e92732013-12-05 18:02:52 -08001237 }
1238
Sailesh Nepald1e68152013-12-12 19:08:02 -08001239 private static int getWhenToMakeWifiCallsDefaultPreference() {
1240 // TODO(sail): Use a build property to choose this value.
Evan Charlton9829e882013-12-19 15:30:38 -08001241 return TelephonyManager.WifiCallingChoices.ALWAYS_USE;
Ihab Awadf2177b72013-11-25 13:33:23 -08001242 }
Shishir Agrawal69f68122013-12-16 17:25:49 -08001243
Shishir Agrawal566b7612013-10-28 14:41:00 -07001244 @Override
1245 public int iccOpenLogicalChannel(String AID) {
1246 enforceSimCommunicationPermission();
1247
1248 if (DBG) log("iccOpenLogicalChannel: " + AID);
1249 Integer channel = (Integer)sendRequest(CMD_OPEN_CHANNEL, AID);
1250 if (DBG) log("iccOpenLogicalChannel: " + channel);
Jake Hambye994d462014-02-03 13:10:13 -08001251 return channel;
Shishir Agrawal566b7612013-10-28 14:41:00 -07001252 }
1253
1254 @Override
1255 public boolean iccCloseLogicalChannel(int channel) {
1256 enforceSimCommunicationPermission();
1257
1258 if (DBG) log("iccCloseLogicalChannel: " + channel);
1259 if (channel < 0) {
1260 return false;
1261 }
Jake Hambye994d462014-02-03 13:10:13 -08001262 Boolean success = (Boolean)sendRequest(CMD_CLOSE_CHANNEL, channel);
Shishir Agrawal566b7612013-10-28 14:41:00 -07001263 if (DBG) log("iccCloseLogicalChannel: " + success);
1264 return success;
1265 }
1266
1267 @Override
1268 public String iccTransmitApduLogicalChannel(int channel, int cla,
1269 int command, int p1, int p2, int p3, String data) {
1270 enforceSimCommunicationPermission();
1271
1272 if (DBG) {
1273 log("iccTransmitApduLogicalChannel: chnl=" + channel + " cla=" + cla +
1274 " cmd=" + command + " p1=" + p1 + " p2=" + p2 + " p3=" + p3 +
1275 " data=" + data);
1276 }
1277
1278 if (channel < 0) {
1279 return "";
1280 }
1281
1282 IccIoResult response = (IccIoResult)sendRequest(CMD_TRANSMIT_APDU,
1283 new IccAPDUArgument(channel, cla, command, p1, p2, p3, data));
1284 if (DBG) log("iccTransmitApduLogicalChannel: " + response);
1285
1286 // If the payload is null, there was an error. Indicate that by returning
1287 // an empty string.
1288 if (response.payload == null) {
1289 return "";
1290 }
1291
1292 // Append the returned status code to the end of the response payload.
1293 String s = Integer.toHexString(
1294 (response.sw1 << 8) + response.sw2 + 0x10000).substring(1);
1295 s = IccUtils.bytesToHexString(response.payload) + s;
1296 return s;
1297 }
Jake Hambye994d462014-02-03 13:10:13 -08001298
Evan Charltonc66da362014-05-16 14:06:40 -07001299 @Override
1300 public String sendEnvelopeWithStatus(String content) {
1301 enforceSimCommunicationPermission();
1302
1303 IccIoResult response = (IccIoResult)sendRequest(CMD_SEND_ENVELOPE, content);
1304 if (response.payload == null) {
1305 return "";
1306 }
1307
1308 // Append the returned status code to the end of the response payload.
1309 String s = Integer.toHexString(
1310 (response.sw1 << 8) + response.sw2 + 0x10000).substring(1);
1311 s = IccUtils.bytesToHexString(response.payload) + s;
1312 return s;
1313 }
1314
Jake Hambye994d462014-02-03 13:10:13 -08001315 /**
1316 * Read one of the NV items defined in {@link com.android.internal.telephony.RadioNVItems}
1317 * and {@code ril_nv_items.h}. Used for device configuration by some CDMA operators.
1318 *
1319 * @param itemID the ID of the item to read
1320 * @return the NV item as a String, or null on error.
1321 */
1322 @Override
1323 public String nvReadItem(int itemID) {
1324 enforceModifyPermission();
1325 if (DBG) log("nvReadItem: item " + itemID);
1326 String value = (String) sendRequest(CMD_NV_READ_ITEM, itemID);
1327 if (DBG) log("nvReadItem: item " + itemID + " is \"" + value + '"');
1328 return value;
1329 }
1330
1331 /**
1332 * Write one of the NV items defined in {@link com.android.internal.telephony.RadioNVItems}
1333 * and {@code ril_nv_items.h}. Used for device configuration by some CDMA operators.
1334 *
1335 * @param itemID the ID of the item to read
1336 * @param itemValue the value to write, as a String
1337 * @return true on success; false on any failure
1338 */
1339 @Override
1340 public boolean nvWriteItem(int itemID, String itemValue) {
1341 enforceModifyPermission();
1342 if (DBG) log("nvWriteItem: item " + itemID + " value \"" + itemValue + '"');
1343 Boolean success = (Boolean) sendRequest(CMD_NV_WRITE_ITEM,
1344 new Pair<Integer, String>(itemID, itemValue));
1345 if (DBG) log("nvWriteItem: item " + itemID + ' ' + (success ? "ok" : "fail"));
1346 return success;
1347 }
1348
1349 /**
1350 * Update the CDMA Preferred Roaming List (PRL) in the radio NV storage.
1351 * Used for device configuration by some CDMA operators.
1352 *
1353 * @param preferredRoamingList byte array containing the new PRL
1354 * @return true on success; false on any failure
1355 */
1356 @Override
1357 public boolean nvWriteCdmaPrl(byte[] preferredRoamingList) {
1358 enforceModifyPermission();
1359 if (DBG) log("nvWriteCdmaPrl: value: " + HexDump.toHexString(preferredRoamingList));
1360 Boolean success = (Boolean) sendRequest(CMD_NV_WRITE_CDMA_PRL, preferredRoamingList);
1361 if (DBG) log("nvWriteCdmaPrl: " + (success ? "ok" : "fail"));
1362 return success;
1363 }
1364
1365 /**
1366 * Perform the specified type of NV config reset.
1367 * Used for device configuration by some CDMA operators.
1368 *
1369 * @param resetType the type of reset to perform (1 == factory reset; 2 == NV-only reset)
1370 * @return true on success; false on any failure
1371 */
1372 @Override
1373 public boolean nvResetConfig(int resetType) {
1374 enforceModifyPermission();
1375 if (DBG) log("nvResetConfig: type " + resetType);
1376 Boolean success = (Boolean) sendRequest(CMD_NV_RESET_CONFIG, resetType);
1377 if (DBG) log("nvResetConfig: type " + resetType + ' ' + (success ? "ok" : "fail"));
1378 return success;
1379 }
Jake Hamby7c27be32014-03-03 13:25:59 -08001380
1381 /**
1382 * Get the preferred network type.
1383 * Used for device configuration by some CDMA operators.
1384 *
1385 * @return the preferred network type, defined in RILConstants.java.
1386 */
1387 @Override
1388 public int getPreferredNetworkType() {
1389 enforceModifyPermission();
1390 if (DBG) log("getPreferredNetworkType");
1391 int[] result = (int[]) sendRequest(CMD_GET_PREFERRED_NETWORK_TYPE, null);
1392 int networkType = (result != null ? result[0] : -1);
1393 if (DBG) log("getPreferredNetworkType: " + networkType);
1394 return networkType;
1395 }
1396
1397 /**
1398 * Set the preferred network type.
1399 * Used for device configuration by some CDMA operators.
1400 *
1401 * @param networkType the preferred network type, defined in RILConstants.java.
1402 * @return true on success; false on any failure.
1403 */
1404 @Override
1405 public boolean setPreferredNetworkType(int networkType) {
1406 enforceModifyPermission();
1407 if (DBG) log("setPreferredNetworkType: type " + networkType);
1408 Boolean success = (Boolean) sendRequest(CMD_SET_PREFERRED_NETWORK_TYPE, networkType);
1409 if (DBG) log("setPreferredNetworkType: " + (success ? "ok" : "fail"));
1410 return success;
1411 }
Gabriel Peal36ebb0d2014-03-20 09:20:43 -07001412
1413 @Override
1414 public void toggleHold() {
1415 enforceModifyPermission();
1416
1417 try {
1418 PhoneUtils.switchHoldingAndActive(mCM.getFirstActiveBgCall());
1419 } catch (Exception e) {
1420 Log.e(LOG_TAG, "Error during toggleHold().", e);
1421 }
1422 }
1423
1424 @Override
1425 public void merge() {
1426 enforceModifyPermission();
1427
1428 try {
1429 if (PhoneUtils.okToMergeCalls(mCM)) {
1430 PhoneUtils.mergeCalls(mCM);
1431 }
1432 } catch (Exception e) {
1433 Log.e(LOG_TAG, "Error during merge().", e);
1434 }
1435 }
1436
1437 @Override
1438 public void swap() {
1439 enforceModifyPermission();
1440
1441 try {
1442 PhoneUtils.swap();
1443 } catch (Exception e) {
1444 Log.e(LOG_TAG, "Error during swap().", e);
1445 }
1446 }
1447
1448 @Override
1449 public void mute(boolean onOff) {
1450 enforceModifyPermission();
1451
1452 try {
1453 PhoneUtils.setMute(onOff);
1454 } catch (Exception e) {
1455 Log.e(LOG_TAG, "Error during mute().", e);
1456 }
1457 }
1458
1459 @Override
1460 public void playDtmfTone(char digit, boolean timedShortTone) {
1461 enforceModifyPermission();
1462
1463 synchronized (mDtmfStopHandler) {
1464 try {
1465 mDtmfTonePlayer.playDtmfTone(digit, timedShortTone);
1466 } catch (Exception e) {
1467 Log.e(LOG_TAG, "Error playing DTMF tone.", e);
1468 }
1469
1470 if (mDtmfStopRunnable != null) {
1471 mDtmfStopHandler.removeCallbacks(mDtmfStopRunnable);
1472 }
1473 mDtmfStopRunnable = new Runnable() {
1474 @Override
1475 public void run() {
1476 synchronized (mDtmfStopHandler) {
1477 if (mDtmfStopRunnable == this) {
1478 mDtmfTonePlayer.stopDtmfTone();
1479 mDtmfStopRunnable = null;
1480 }
1481 }
1482 }
1483 };
1484 mDtmfStopHandler.postDelayed(mDtmfStopRunnable, 5000);
1485 }
1486 }
1487
1488 @Override
1489 public void stopDtmfTone() {
1490 enforceModifyPermission();
1491
1492 synchronized (mDtmfStopHandler) {
1493 try {
1494 mDtmfTonePlayer.stopDtmfTone();
1495 } catch (Exception e) {
1496 Log.e(LOG_TAG, "Error stopping DTMF tone.", e);
1497 }
1498
1499 if (mDtmfStopRunnable != null) {
1500 mDtmfStopHandler.removeCallbacks(mDtmfStopRunnable);
1501 mDtmfStopRunnable = null;
1502 }
1503 }
1504 }
1505
1506 @Override
1507 public void addListener(ITelephonyListener listener) {
1508 enforcePrivilegedPhoneStatePermission();
1509
1510 if (listener == null) {
1511 throw new IllegalArgumentException("Listener must not be null.");
1512 }
1513
1514 synchronized (mListeners) {
1515 IBinder listenerBinder = listener.asBinder();
1516 for (ITelephonyListener l : mListeners) {
1517 if (l.asBinder().equals(listenerBinder)) {
1518 Log.w(LOG_TAG, "Listener already registered. Ignoring.");
1519 return;
1520 }
1521 }
1522 mListeners.add(listener);
1523 mDeathRecipients.put(listener.asBinder(),
1524 new TelephonyListenerDeathRecipient(listener.asBinder()));
1525
1526 // update the new listener so they get the full call state immediately
1527 for (Call call : mCallModeler.getFullList()) {
1528 try {
1529 notifyListenerOfCallLocked(call, listener);
1530 } catch (RemoteException e) {
1531 Log.e(LOG_TAG, "Error updating new listener. Ignoring.");
1532 removeListenerInternal(listener);
1533 }
1534 }
1535 }
1536 }
1537
1538 @Override
1539 public void removeListener(ITelephonyListener listener) {
1540 enforcePrivilegedPhoneStatePermission();
1541
1542 if (listener == null) {
1543 throw new IllegalArgumentException("Listener must not be null.");
1544 }
1545
1546 removeListenerInternal(listener);
1547 }
1548
1549 private void removeListenerInternal(ITelephonyListener listener) {
1550 IBinder listenerBinder = listener.asBinder();
1551
1552 synchronized (mListeners) {
1553 for (Iterator<ITelephonyListener> it = mListeners.iterator(); it.hasNext(); ) {
1554 ITelephonyListener nextListener = it.next();
1555 if (nextListener.asBinder().equals(listenerBinder)) {
1556 TelephonyListenerDeathRecipient dr = mDeathRecipients.get(listener.asBinder());
1557 if (dr != null) {
1558 dr.unlinkDeathRecipient();
1559 }
1560 it.remove();
1561 }
1562 }
1563 }
1564 }
1565
1566 /** CallModeler.Listener implementation **/
1567
1568 @Override
1569 public void onDisconnect(Call call) {
1570 notifyListenersOfCall(call);
1571 }
1572
1573 @Override
1574 public void onIncoming(Call call) {
1575 notifyListenersOfCall(call);
1576 }
1577
1578 @Override
1579 public void onUpdate(List<Call> calls) {
1580 for (Call call : calls) {
1581 notifyListenersOfCall(call);
1582 }
1583 }
1584
1585 @Override
1586 public void onPostDialAction(
1587 Connection.PostDialState state, int callId, String remainingChars, char c) { }
1588
1589 private void notifyListenersOfCall(Call call) {
1590 synchronized (mListeners) {
1591 for (Iterator<ITelephonyListener> it = mListeners.iterator(); it.hasNext(); ) {
1592 ITelephonyListener listener = it.next();
1593 try {
1594 notifyListenerOfCallLocked(call, listener);
1595 } catch (RemoteException e) {
1596 TelephonyListenerDeathRecipient deathRecipient =
1597 mDeathRecipients.get(listener.asBinder());
1598 if (deathRecipient != null) {
1599 deathRecipient.unlinkDeathRecipient();
1600 }
1601 it.remove();
1602 }
1603 }
1604 }
1605 }
1606
1607 private void notifyListenerOfCallLocked(final Call call,final ITelephonyListener listener)
1608 throws RemoteException {
1609 if (Binder.isProxy(listener)) {
1610 listener.onUpdate(call.getCallId(), call.getState(), call.getNumber());
1611 } else {
1612 mMainThreadHandler.post(new Runnable() {
1613
1614 @Override
1615 public void run() {
1616 try {
1617 listener.onUpdate(call.getCallId(), call.getState(), call.getNumber());
1618 } catch (RemoteException e) {
1619 Log.wtf(LOG_TAG, "Local binder call failed with RemoteException.", e);
1620 }
1621 }
1622 });
1623 }
1624
1625 }
1626
1627 private class TelephonyListenerDeathRecipient implements Binder.DeathRecipient {
1628 private final IBinder mBinder;
1629
1630 public TelephonyListenerDeathRecipient(IBinder listener) {
1631 mBinder = listener;
1632 try {
1633 mBinder.linkToDeath(this, 0);
1634 } catch (RemoteException e) {
1635 unlinkDeathRecipient();
1636 }
1637 }
1638
1639 @Override
1640 public void binderDied() {
1641 synchronized (mListeners) {
1642 if (mListeners.contains(mBinder)) {
1643 mListeners.remove(mBinder);
1644 Log.w(LOG_TAG, "ITelephonyListener died. Removing.");
1645 } else {
1646 Log.w(LOG_TAG, "TelephonyListener binder died but the listener " +
1647 "is not registered.");
1648 }
1649 }
1650 }
1651
1652 public void unlinkDeathRecipient() {
1653 mBinder.unlinkToDeath(this, 0);
1654 }
1655 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001656}