blob: 115c8301fda80779360b55d20a22b1363c141a0f [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;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070021import android.content.Context;
22import android.content.Intent;
23import android.net.ConnectivityManager;
24import android.net.Uri;
25import android.os.AsyncResult;
26import android.os.Binder;
27import android.os.Bundle;
28import android.os.Handler;
Gabriel Peal36ebb0d2014-03-20 09:20:43 -070029import android.os.IBinder;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070030import android.os.Looper;
31import android.os.Message;
32import android.os.Process;
Gabriel Peal36ebb0d2014-03-20 09:20:43 -070033import android.os.RemoteException;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070034import android.os.ServiceManager;
35import android.os.UserHandle;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070036import android.telephony.CellInfo;
Jake Hambye994d462014-02-03 13:10:13 -080037import android.telephony.NeighboringCellInfo;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070038import android.telephony.ServiceState;
39import android.text.TextUtils;
40import android.util.Log;
Jake Hambye994d462014-02-03 13:10:13 -080041import android.util.Pair;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070042
Shishir Agrawal566b7612013-10-28 14:41:00 -070043import com.android.internal.telephony.CallManager;
44import com.android.internal.telephony.CommandException;
Gabriel Peal36ebb0d2014-03-20 09:20:43 -070045import com.android.internal.telephony.Connection;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070046import com.android.internal.telephony.DefaultPhoneNotifier;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070047import com.android.internal.telephony.ITelephony;
Gabriel Peal36ebb0d2014-03-20 09:20:43 -070048import com.android.internal.telephony.ITelephonyListener;
Jake Hambye994d462014-02-03 13:10:13 -080049import com.android.internal.telephony.IccCard;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070050import com.android.internal.telephony.Phone;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070051import com.android.internal.telephony.PhoneConstants;
Shishir Agrawal566b7612013-10-28 14:41:00 -070052import com.android.internal.telephony.uicc.IccIoResult;
53import com.android.internal.telephony.uicc.IccUtils;
54import com.android.internal.telephony.uicc.UiccController;
Jake Hambye994d462014-02-03 13:10:13 -080055import com.android.internal.util.HexDump;
Gabriel Peal36ebb0d2014-03-20 09:20:43 -070056import com.android.services.telephony.common.Call;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070057
Santos Cordon7d4ddf62013-07-10 11:58:08 -070058import java.util.ArrayList;
Gabriel Peal36ebb0d2014-03-20 09:20:43 -070059import java.util.HashMap;
60import java.util.Iterator;
Jake Hambye994d462014-02-03 13:10:13 -080061import java.util.List;
Gabriel Peal36ebb0d2014-03-20 09:20:43 -070062import java.util.Map;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070063
64/**
65 * Implementation of the ITelephony interface.
66 */
Gabriel Peal36ebb0d2014-03-20 09:20:43 -070067public class PhoneInterfaceManager extends ITelephony.Stub implements CallModeler.Listener {
Santos Cordon7d4ddf62013-07-10 11:58:08 -070068 private static final String LOG_TAG = "PhoneInterfaceManager";
69 private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
70 private static final boolean DBG_LOC = false;
71
72 // Message codes used with mMainThreadHandler
73 private static final int CMD_HANDLE_PIN_MMI = 1;
74 private static final int CMD_HANDLE_NEIGHBORING_CELL = 2;
75 private static final int EVENT_NEIGHBORING_CELL_DONE = 3;
76 private static final int CMD_ANSWER_RINGING_CALL = 4;
77 private static final int CMD_END_CALL = 5; // not used yet
78 private static final int CMD_SILENCE_RINGER = 6;
Shishir Agrawal566b7612013-10-28 14:41:00 -070079 private static final int CMD_TRANSMIT_APDU = 7;
80 private static final int EVENT_TRANSMIT_APDU_DONE = 8;
81 private static final int CMD_OPEN_CHANNEL = 9;
82 private static final int EVENT_OPEN_CHANNEL_DONE = 10;
83 private static final int CMD_CLOSE_CHANNEL = 11;
84 private static final int EVENT_CLOSE_CHANNEL_DONE = 12;
Jake Hambye994d462014-02-03 13:10:13 -080085 private static final int CMD_NV_READ_ITEM = 13;
86 private static final int EVENT_NV_READ_ITEM_DONE = 14;
87 private static final int CMD_NV_WRITE_ITEM = 15;
88 private static final int EVENT_NV_WRITE_ITEM_DONE = 16;
89 private static final int CMD_NV_WRITE_CDMA_PRL = 17;
90 private static final int EVENT_NV_WRITE_CDMA_PRL_DONE = 18;
91 private static final int CMD_NV_RESET_CONFIG = 19;
92 private static final int EVENT_NV_RESET_CONFIG_DONE = 20;
Jake Hamby7c27be32014-03-03 13:25:59 -080093 private static final int CMD_GET_PREFERRED_NETWORK_TYPE = 21;
94 private static final int EVENT_GET_PREFERRED_NETWORK_TYPE_DONE = 22;
95 private static final int CMD_SET_PREFERRED_NETWORK_TYPE = 23;
96 private static final int EVENT_SET_PREFERRED_NETWORK_TYPE_DONE = 24;
Shishir Agrawal566b7612013-10-28 14:41:00 -070097
Santos Cordon7d4ddf62013-07-10 11:58:08 -070098
99 /** The singleton instance. */
100 private static PhoneInterfaceManager sInstance;
101
102 PhoneGlobals mApp;
103 Phone mPhone;
104 CallManager mCM;
105 AppOpsManager mAppOps;
106 MainThreadHandler mMainThreadHandler;
Santos Cordon406c0342013-08-28 00:07:47 -0700107 CallHandlerServiceProxy mCallHandlerService;
Gabriel Peal36ebb0d2014-03-20 09:20:43 -0700108 CallModeler mCallModeler;
109 DTMFTonePlayer mDtmfTonePlayer;
110 Handler mDtmfStopHandler = new Handler();
111 Runnable mDtmfStopRunnable;
112
113 private final List<ITelephonyListener> mListeners = new ArrayList<ITelephonyListener>();
114 private final Map<IBinder, TelephonyListenerDeathRecipient> mDeathRecipients =
115 new HashMap<IBinder, TelephonyListenerDeathRecipient>();
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700116
117 /**
Shishir Agrawal566b7612013-10-28 14:41:00 -0700118 * A request object to use for transmitting data to an ICC.
119 */
120 private static final class IccAPDUArgument {
121 public int channel, cla, command, p1, p2, p3;
122 public String data;
123
124 public IccAPDUArgument(int channel, int cla, int command,
125 int p1, int p2, int p3, String data) {
126 this.channel = channel;
127 this.cla = cla;
128 this.command = command;
129 this.p1 = p1;
130 this.p2 = p2;
131 this.p3 = p3;
132 this.data = data;
133 }
134 }
135
136 /**
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700137 * A request object for use with {@link MainThreadHandler}. Requesters should wait() on the
138 * request after sending. The main thread will notify the request when it is complete.
139 */
140 private static final class MainThreadRequest {
141 /** The argument to use for the request */
142 public Object argument;
143 /** The result of the request that is run on the main thread */
144 public Object result;
145
146 public MainThreadRequest(Object argument) {
147 this.argument = argument;
148 }
149 }
150
151 /**
152 * A handler that processes messages on the main thread in the phone process. Since many
153 * of the Phone calls are not thread safe this is needed to shuttle the requests from the
154 * inbound binder threads to the main thread in the phone process. The Binder thread
155 * may provide a {@link MainThreadRequest} object in the msg.obj field that they are waiting
156 * on, which will be notified when the operation completes and will contain the result of the
157 * request.
158 *
159 * <p>If a MainThreadRequest object is provided in the msg.obj field,
160 * note that request.result must be set to something non-null for the calling thread to
161 * unblock.
162 */
163 private final class MainThreadHandler extends Handler {
164 @Override
165 public void handleMessage(Message msg) {
166 MainThreadRequest request;
167 Message onCompleted;
168 AsyncResult ar;
169
170 switch (msg.what) {
171 case CMD_HANDLE_PIN_MMI:
172 request = (MainThreadRequest) msg.obj;
Jake Hambye994d462014-02-03 13:10:13 -0800173 request.result = mPhone.handlePinMmi((String) request.argument);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700174 // Wake up the requesting thread
175 synchronized (request) {
176 request.notifyAll();
177 }
178 break;
179
180 case CMD_HANDLE_NEIGHBORING_CELL:
181 request = (MainThreadRequest) msg.obj;
182 onCompleted = obtainMessage(EVENT_NEIGHBORING_CELL_DONE,
183 request);
184 mPhone.getNeighboringCids(onCompleted);
185 break;
186
187 case EVENT_NEIGHBORING_CELL_DONE:
188 ar = (AsyncResult) msg.obj;
189 request = (MainThreadRequest) ar.userObj;
190 if (ar.exception == null && ar.result != null) {
191 request.result = ar.result;
192 } else {
193 // create an empty list to notify the waiting thread
Jake Hambye994d462014-02-03 13:10:13 -0800194 request.result = new ArrayList<NeighboringCellInfo>(0);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700195 }
196 // Wake up the requesting thread
197 synchronized (request) {
198 request.notifyAll();
199 }
200 break;
201
202 case CMD_ANSWER_RINGING_CALL:
203 answerRingingCallInternal();
204 break;
205
206 case CMD_SILENCE_RINGER:
207 silenceRingerInternal();
208 break;
209
210 case CMD_END_CALL:
211 request = (MainThreadRequest) msg.obj;
Jake Hambye994d462014-02-03 13:10:13 -0800212 boolean hungUp;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700213 int phoneType = mPhone.getPhoneType();
214 if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
215 // CDMA: If the user presses the Power button we treat it as
216 // ending the complete call session
217 hungUp = PhoneUtils.hangupRingingAndActive(mPhone);
218 } else if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
219 // GSM: End the call as per the Phone state
220 hungUp = PhoneUtils.hangup(mCM);
221 } else {
222 throw new IllegalStateException("Unexpected phone type: " + phoneType);
223 }
224 if (DBG) log("CMD_END_CALL: " + (hungUp ? "hung up!" : "no call to hang up"));
225 request.result = hungUp;
226 // Wake up the requesting thread
227 synchronized (request) {
228 request.notifyAll();
229 }
230 break;
231
Shishir Agrawal566b7612013-10-28 14:41:00 -0700232 case CMD_TRANSMIT_APDU:
233 request = (MainThreadRequest) msg.obj;
234 IccAPDUArgument argument = (IccAPDUArgument) request.argument;
235 onCompleted = obtainMessage(EVENT_TRANSMIT_APDU_DONE, request);
236 UiccController.getInstance().getUiccCard().iccTransmitApduLogicalChannel(
237 argument.channel, argument.cla, argument.command,
238 argument.p1, argument.p2, argument.p3, argument.data,
239 onCompleted);
240 break;
241
242 case EVENT_TRANSMIT_APDU_DONE:
243 ar = (AsyncResult) msg.obj;
244 request = (MainThreadRequest) ar.userObj;
245 if (ar.exception == null && ar.result != null) {
246 request.result = ar.result;
247 } else {
248 request.result = new IccIoResult(0x6F, 0, (byte[])null);
249 if (ar.result == null) {
250 loge("iccTransmitApduLogicalChannel: Empty response");
Jake Hambye994d462014-02-03 13:10:13 -0800251 } else if (ar.exception instanceof CommandException) {
Shishir Agrawal566b7612013-10-28 14:41:00 -0700252 loge("iccTransmitApduLogicalChannel: CommandException: " +
Jake Hambye994d462014-02-03 13:10:13 -0800253 ar.exception);
Shishir Agrawal566b7612013-10-28 14:41:00 -0700254 } else {
255 loge("iccTransmitApduLogicalChannel: Unknown exception");
256 }
257 }
258 synchronized (request) {
259 request.notifyAll();
260 }
261 break;
262
263 case CMD_OPEN_CHANNEL:
264 request = (MainThreadRequest) msg.obj;
265 onCompleted = obtainMessage(EVENT_OPEN_CHANNEL_DONE, request);
266 UiccController.getInstance().getUiccCard().iccOpenLogicalChannel(
267 (String)request.argument, onCompleted);
268 break;
269
270 case EVENT_OPEN_CHANNEL_DONE:
271 ar = (AsyncResult) msg.obj;
272 request = (MainThreadRequest) ar.userObj;
273 if (ar.exception == null && ar.result != null) {
Jake Hambye994d462014-02-03 13:10:13 -0800274 request.result = ((int[]) ar.result)[0];
Shishir Agrawal566b7612013-10-28 14:41:00 -0700275 } else {
Jake Hambye994d462014-02-03 13:10:13 -0800276 request.result = -1;
Shishir Agrawal566b7612013-10-28 14:41:00 -0700277 if (ar.result == null) {
278 loge("iccOpenLogicalChannel: Empty response");
Jake Hambye994d462014-02-03 13:10:13 -0800279 } else if (ar.exception instanceof CommandException) {
Shishir Agrawal566b7612013-10-28 14:41:00 -0700280 loge("iccOpenLogicalChannel: CommandException: " +
Jake Hambye994d462014-02-03 13:10:13 -0800281 ar.exception);
Shishir Agrawal566b7612013-10-28 14:41:00 -0700282 } else {
283 loge("iccOpenLogicalChannel: Unknown exception");
284 }
285 }
286 synchronized (request) {
287 request.notifyAll();
288 }
289 break;
290
291 case CMD_CLOSE_CHANNEL:
292 request = (MainThreadRequest) msg.obj;
293 onCompleted = obtainMessage(EVENT_CLOSE_CHANNEL_DONE,
294 request);
295 UiccController.getInstance().getUiccCard().iccCloseLogicalChannel(
Jake Hambye994d462014-02-03 13:10:13 -0800296 (Integer) request.argument,
Shishir Agrawal566b7612013-10-28 14:41:00 -0700297 onCompleted);
298 break;
299
300 case EVENT_CLOSE_CHANNEL_DONE:
Jake Hambye994d462014-02-03 13:10:13 -0800301 handleNullReturnEvent(msg, "iccCloseLogicalChannel");
302 break;
303
304 case CMD_NV_READ_ITEM:
305 request = (MainThreadRequest) msg.obj;
306 onCompleted = obtainMessage(EVENT_NV_READ_ITEM_DONE, request);
307 mPhone.nvReadItem((Integer) request.argument, onCompleted);
308 break;
309
310 case EVENT_NV_READ_ITEM_DONE:
Shishir Agrawal566b7612013-10-28 14:41:00 -0700311 ar = (AsyncResult) msg.obj;
312 request = (MainThreadRequest) ar.userObj;
Jake Hambye994d462014-02-03 13:10:13 -0800313 if (ar.exception == null && ar.result != null) {
314 request.result = ar.result; // String
Shishir Agrawal566b7612013-10-28 14:41:00 -0700315 } else {
Jake Hambye994d462014-02-03 13:10:13 -0800316 request.result = "";
317 if (ar.result == null) {
318 loge("nvReadItem: Empty response");
319 } else if (ar.exception instanceof CommandException) {
320 loge("nvReadItem: CommandException: " +
321 ar.exception);
Shishir Agrawal566b7612013-10-28 14:41:00 -0700322 } else {
Jake Hambye994d462014-02-03 13:10:13 -0800323 loge("nvReadItem: Unknown exception");
Shishir Agrawal566b7612013-10-28 14:41:00 -0700324 }
325 }
326 synchronized (request) {
327 request.notifyAll();
328 }
329 break;
330
Jake Hambye994d462014-02-03 13:10:13 -0800331 case CMD_NV_WRITE_ITEM:
332 request = (MainThreadRequest) msg.obj;
333 onCompleted = obtainMessage(EVENT_NV_WRITE_ITEM_DONE, request);
334 Pair<Integer, String> idValue = (Pair<Integer, String>) request.argument;
335 mPhone.nvWriteItem(idValue.first, idValue.second, onCompleted);
336 break;
337
338 case EVENT_NV_WRITE_ITEM_DONE:
339 handleNullReturnEvent(msg, "nvWriteItem");
340 break;
341
342 case CMD_NV_WRITE_CDMA_PRL:
343 request = (MainThreadRequest) msg.obj;
344 onCompleted = obtainMessage(EVENT_NV_WRITE_CDMA_PRL_DONE, request);
345 mPhone.nvWriteCdmaPrl((byte[]) request.argument, onCompleted);
346 break;
347
348 case EVENT_NV_WRITE_CDMA_PRL_DONE:
349 handleNullReturnEvent(msg, "nvWriteCdmaPrl");
350 break;
351
352 case CMD_NV_RESET_CONFIG:
353 request = (MainThreadRequest) msg.obj;
354 onCompleted = obtainMessage(EVENT_NV_RESET_CONFIG_DONE, request);
355 mPhone.nvResetConfig((Integer) request.argument, onCompleted);
356 break;
357
358 case EVENT_NV_RESET_CONFIG_DONE:
359 handleNullReturnEvent(msg, "nvResetConfig");
360 break;
361
Jake Hamby7c27be32014-03-03 13:25:59 -0800362 case CMD_GET_PREFERRED_NETWORK_TYPE:
363 request = (MainThreadRequest) msg.obj;
364 onCompleted = obtainMessage(EVENT_GET_PREFERRED_NETWORK_TYPE_DONE, request);
365 mPhone.getPreferredNetworkType(onCompleted);
366 break;
367
368 case EVENT_GET_PREFERRED_NETWORK_TYPE_DONE:
369 ar = (AsyncResult) msg.obj;
370 request = (MainThreadRequest) ar.userObj;
371 if (ar.exception == null && ar.result != null) {
372 request.result = ar.result; // Integer
373 } else {
374 request.result = -1;
375 if (ar.result == null) {
376 loge("getPreferredNetworkType: Empty response");
377 } else if (ar.exception instanceof CommandException) {
378 loge("getPreferredNetworkType: CommandException: " +
379 ar.exception);
380 } else {
381 loge("getPreferredNetworkType: Unknown exception");
382 }
383 }
384 synchronized (request) {
385 request.notifyAll();
386 }
387 break;
388
389 case CMD_SET_PREFERRED_NETWORK_TYPE:
390 request = (MainThreadRequest) msg.obj;
391 onCompleted = obtainMessage(EVENT_SET_PREFERRED_NETWORK_TYPE_DONE, request);
392 int networkType = (Integer) request.argument;
393 mPhone.setPreferredNetworkType(networkType, onCompleted);
394 break;
395
396 case EVENT_SET_PREFERRED_NETWORK_TYPE_DONE:
397 handleNullReturnEvent(msg, "setPreferredNetworkType");
398 break;
399
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700400 default:
401 Log.w(LOG_TAG, "MainThreadHandler: unexpected message code: " + msg.what);
402 break;
403 }
404 }
Jake Hambye994d462014-02-03 13:10:13 -0800405
406 private void handleNullReturnEvent(Message msg, String command) {
407 AsyncResult ar = (AsyncResult) msg.obj;
408 MainThreadRequest request = (MainThreadRequest) ar.userObj;
409 if (ar.exception == null) {
410 request.result = true;
411 } else {
412 request.result = false;
413 if (ar.exception instanceof CommandException) {
414 loge(command + ": CommandException: " + ar.exception);
415 } else {
416 loge(command + ": Unknown exception");
417 }
418 }
419 synchronized (request) {
420 request.notifyAll();
421 }
422 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700423 }
424
425 /**
426 * Posts the specified command to be executed on the main thread,
427 * waits for the request to complete, and returns the result.
428 * @see #sendRequestAsync
429 */
430 private Object sendRequest(int command, Object argument) {
431 if (Looper.myLooper() == mMainThreadHandler.getLooper()) {
432 throw new RuntimeException("This method will deadlock if called from the main thread.");
433 }
434
435 MainThreadRequest request = new MainThreadRequest(argument);
436 Message msg = mMainThreadHandler.obtainMessage(command, request);
437 msg.sendToTarget();
438
439 // Wait for the request to complete
440 synchronized (request) {
441 while (request.result == null) {
442 try {
443 request.wait();
444 } catch (InterruptedException e) {
445 // Do nothing, go back and wait until the request is complete
446 }
447 }
448 }
449 return request.result;
450 }
451
452 /**
453 * Asynchronous ("fire and forget") version of sendRequest():
454 * Posts the specified command to be executed on the main thread, and
455 * returns immediately.
456 * @see #sendRequest
457 */
458 private void sendRequestAsync(int command) {
459 mMainThreadHandler.sendEmptyMessage(command);
460 }
461
462 /**
463 * Initialize the singleton PhoneInterfaceManager instance.
464 * This is only done once, at startup, from PhoneApp.onCreate().
465 */
Santos Cordon406c0342013-08-28 00:07:47 -0700466 /* package */ static PhoneInterfaceManager init(PhoneGlobals app, Phone phone,
Gabriel Peal36ebb0d2014-03-20 09:20:43 -0700467 CallHandlerServiceProxy callHandlerService, CallModeler callModeler,
468 DTMFTonePlayer dtmfTonePlayer) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700469 synchronized (PhoneInterfaceManager.class) {
470 if (sInstance == null) {
Gabriel Peal36ebb0d2014-03-20 09:20:43 -0700471 sInstance = new PhoneInterfaceManager(app, phone, callHandlerService, callModeler,
472 dtmfTonePlayer);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700473 } else {
474 Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance);
475 }
476 return sInstance;
477 }
478 }
479
480 /** Private constructor; @see init() */
Santos Cordon406c0342013-08-28 00:07:47 -0700481 private PhoneInterfaceManager(PhoneGlobals app, Phone phone,
Gabriel Peal36ebb0d2014-03-20 09:20:43 -0700482 CallHandlerServiceProxy callHandlerService, CallModeler callModeler,
483 DTMFTonePlayer dtmfTonePlayer) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700484 mApp = app;
485 mPhone = phone;
486 mCM = PhoneGlobals.getInstance().mCM;
487 mAppOps = (AppOpsManager)app.getSystemService(Context.APP_OPS_SERVICE);
488 mMainThreadHandler = new MainThreadHandler();
Santos Cordon406c0342013-08-28 00:07:47 -0700489 mCallHandlerService = callHandlerService;
Gabriel Peal36ebb0d2014-03-20 09:20:43 -0700490 mCallModeler = callModeler;
491 mCallModeler.addListener(this);
492 mDtmfTonePlayer = dtmfTonePlayer;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700493 publish();
494 }
495
496 private void publish() {
497 if (DBG) log("publish: " + this);
498
499 ServiceManager.addService("phone", this);
500 }
501
502 //
503 // Implementation of the ITelephony interface.
504 //
505
506 public void dial(String number) {
507 if (DBG) log("dial: " + number);
508 // No permission check needed here: This is just a wrapper around the
509 // ACTION_DIAL intent, which is available to any app since it puts up
510 // the UI before it does anything.
511
512 String url = createTelUrl(number);
513 if (url == null) {
514 return;
515 }
516
517 // PENDING: should we just silently fail if phone is offhook or ringing?
518 PhoneConstants.State state = mCM.getState();
519 if (state != PhoneConstants.State.OFFHOOK && state != PhoneConstants.State.RINGING) {
520 Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse(url));
521 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
522 mApp.startActivity(intent);
523 }
524 }
525
526 public void call(String callingPackage, String number) {
527 if (DBG) log("call: " + number);
528
529 // This is just a wrapper around the ACTION_CALL intent, but we still
530 // need to do a permission check since we're calling startActivity()
531 // from the context of the phone app.
532 enforceCallPermission();
533
534 if (mAppOps.noteOp(AppOpsManager.OP_CALL_PHONE, Binder.getCallingUid(), callingPackage)
535 != AppOpsManager.MODE_ALLOWED) {
536 return;
537 }
538
539 String url = createTelUrl(number);
540 if (url == null) {
541 return;
542 }
543
544 Intent intent = new Intent(Intent.ACTION_CALL, Uri.parse(url));
545 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
546 mApp.startActivity(intent);
547 }
548
549 private boolean showCallScreenInternal(boolean specifyInitialDialpadState,
Makoto Onukibcf20992013-09-12 17:59:30 -0700550 boolean showDialpad) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700551 if (!PhoneGlobals.sVoiceCapable) {
552 // Never allow the InCallScreen to appear on data-only devices.
553 return false;
554 }
555 if (isIdle()) {
556 return false;
557 }
558 // If the phone isn't idle then go to the in-call screen
559 long callingId = Binder.clearCallingIdentity();
Santos Cordon406c0342013-08-28 00:07:47 -0700560
Makoto Onukibcf20992013-09-12 17:59:30 -0700561 mCallHandlerService.bringToForeground(showDialpad);
Santos Cordon406c0342013-08-28 00:07:47 -0700562
563 Binder.restoreCallingIdentity(callingId);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700564 return true;
565 }
566
567 // Show the in-call screen without specifying the initial dialpad state.
568 public boolean showCallScreen() {
569 return showCallScreenInternal(false, false);
570 }
571
572 // The variation of showCallScreen() that specifies the initial dialpad state.
573 // (Ideally this would be called showCallScreen() too, just with a different
574 // signature, but AIDL doesn't allow that.)
575 public boolean showCallScreenWithDialpad(boolean showDialpad) {
576 return showCallScreenInternal(true, showDialpad);
577 }
578
579 /**
580 * End a call based on call state
581 * @return true is a call was ended
582 */
583 public boolean endCall() {
584 enforceCallPermission();
585 return (Boolean) sendRequest(CMD_END_CALL, null);
586 }
587
588 public void answerRingingCall() {
589 if (DBG) log("answerRingingCall...");
590 // TODO: there should eventually be a separate "ANSWER_PHONE" permission,
591 // but that can probably wait till the big TelephonyManager API overhaul.
592 // For now, protect this call with the MODIFY_PHONE_STATE permission.
593 enforceModifyPermission();
594 sendRequestAsync(CMD_ANSWER_RINGING_CALL);
595 }
596
597 /**
598 * Make the actual telephony calls to implement answerRingingCall().
599 * This should only be called from the main thread of the Phone app.
600 * @see #answerRingingCall
601 *
602 * TODO: it would be nice to return true if we answered the call, or
603 * false if there wasn't actually a ringing incoming call, or some
604 * other error occurred. (In other words, pass back the return value
605 * from PhoneUtils.answerCall() or PhoneUtils.answerAndEndActive().)
606 * But that would require calling this method via sendRequest() rather
607 * than sendRequestAsync(), and right now we don't actually *need* that
608 * return value, so let's just return void for now.
609 */
610 private void answerRingingCallInternal() {
611 final boolean hasRingingCall = !mPhone.getRingingCall().isIdle();
612 if (hasRingingCall) {
613 final boolean hasActiveCall = !mPhone.getForegroundCall().isIdle();
614 final boolean hasHoldingCall = !mPhone.getBackgroundCall().isIdle();
615 if (hasActiveCall && hasHoldingCall) {
616 // Both lines are in use!
617 // TODO: provide a flag to let the caller specify what
618 // policy to use if both lines are in use. (The current
619 // behavior is hardwired to "answer incoming, end ongoing",
620 // which is how the CALL button is specced to behave.)
621 PhoneUtils.answerAndEndActive(mCM, mCM.getFirstActiveRingingCall());
622 return;
623 } else {
624 // answerCall() will automatically hold the current active
625 // call, if there is one.
626 PhoneUtils.answerCall(mCM.getFirstActiveRingingCall());
627 return;
628 }
629 } else {
630 // No call was ringing.
631 return;
632 }
633 }
634
635 public void silenceRinger() {
636 if (DBG) log("silenceRinger...");
637 // TODO: find a more appropriate permission to check here.
638 // (That can probably wait till the big TelephonyManager API overhaul.
639 // For now, protect this call with the MODIFY_PHONE_STATE permission.)
640 enforceModifyPermission();
641 sendRequestAsync(CMD_SILENCE_RINGER);
642 }
643
644 /**
645 * Internal implemenation of silenceRinger().
646 * This should only be called from the main thread of the Phone app.
647 * @see #silenceRinger
648 */
649 private void silenceRingerInternal() {
650 if ((mCM.getState() == PhoneConstants.State.RINGING)
651 && mApp.notifier.isRinging()) {
652 // Ringer is actually playing, so silence it.
653 if (DBG) log("silenceRingerInternal: silencing...");
654 mApp.notifier.silenceRinger();
655 }
656 }
657
658 public boolean isOffhook() {
659 return (mCM.getState() == PhoneConstants.State.OFFHOOK);
660 }
661
662 public boolean isRinging() {
663 return (mCM.getState() == PhoneConstants.State.RINGING);
664 }
665
666 public boolean isIdle() {
667 return (mCM.getState() == PhoneConstants.State.IDLE);
668 }
669
670 public boolean isSimPinEnabled() {
671 enforceReadPermission();
672 return (PhoneGlobals.getInstance().isSimPinEnabled());
673 }
674
675 public boolean supplyPin(String pin) {
Wink Saville9de0f752013-10-22 19:04:03 -0700676 int [] resultArray = supplyPinReportResult(pin);
677 return (resultArray[0] == PhoneConstants.PIN_RESULT_SUCCESS) ? true : false;
678 }
679
680 public boolean supplyPuk(String puk, String pin) {
681 int [] resultArray = supplyPukReportResult(puk, pin);
682 return (resultArray[0] == PhoneConstants.PIN_RESULT_SUCCESS) ? true : false;
683 }
684
685 /** {@hide} */
686 public int[] supplyPinReportResult(String pin) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700687 enforceModifyPermission();
688 final UnlockSim checkSimPin = new UnlockSim(mPhone.getIccCard());
689 checkSimPin.start();
690 return checkSimPin.unlockSim(null, pin);
691 }
692
Wink Saville9de0f752013-10-22 19:04:03 -0700693 /** {@hide} */
694 public int[] supplyPukReportResult(String puk, String pin) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700695 enforceModifyPermission();
696 final UnlockSim checkSimPuk = new UnlockSim(mPhone.getIccCard());
697 checkSimPuk.start();
698 return checkSimPuk.unlockSim(puk, pin);
699 }
700
701 /**
Wink Saville9de0f752013-10-22 19:04:03 -0700702 * Helper thread to turn async call to SimCard#supplyPin into
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700703 * a synchronous one.
704 */
705 private static class UnlockSim extends Thread {
706
707 private final IccCard mSimCard;
708
709 private boolean mDone = false;
Wink Saville9de0f752013-10-22 19:04:03 -0700710 private int mResult = PhoneConstants.PIN_GENERAL_FAILURE;
711 private int mRetryCount = -1;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700712
713 // For replies from SimCard interface
714 private Handler mHandler;
715
716 // For async handler to identify request type
717 private static final int SUPPLY_PIN_COMPLETE = 100;
718
719 public UnlockSim(IccCard simCard) {
720 mSimCard = simCard;
721 }
722
723 @Override
724 public void run() {
725 Looper.prepare();
726 synchronized (UnlockSim.this) {
727 mHandler = new Handler() {
728 @Override
729 public void handleMessage(Message msg) {
730 AsyncResult ar = (AsyncResult) msg.obj;
731 switch (msg.what) {
732 case SUPPLY_PIN_COMPLETE:
733 Log.d(LOG_TAG, "SUPPLY_PIN_COMPLETE");
734 synchronized (UnlockSim.this) {
Wink Saville9de0f752013-10-22 19:04:03 -0700735 mRetryCount = msg.arg1;
736 if (ar.exception != null) {
737 if (ar.exception instanceof CommandException &&
738 ((CommandException)(ar.exception)).getCommandError()
739 == CommandException.Error.PASSWORD_INCORRECT) {
740 mResult = PhoneConstants.PIN_PASSWORD_INCORRECT;
741 } else {
742 mResult = PhoneConstants.PIN_GENERAL_FAILURE;
743 }
744 } else {
745 mResult = PhoneConstants.PIN_RESULT_SUCCESS;
746 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700747 mDone = true;
748 UnlockSim.this.notifyAll();
749 }
750 break;
751 }
752 }
753 };
754 UnlockSim.this.notifyAll();
755 }
756 Looper.loop();
757 }
758
759 /*
760 * Use PIN or PUK to unlock SIM card
761 *
762 * If PUK is null, unlock SIM card with PIN
763 *
764 * If PUK is not null, unlock SIM card with PUK and set PIN code
765 */
Wink Saville9de0f752013-10-22 19:04:03 -0700766 synchronized int[] unlockSim(String puk, String pin) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700767
768 while (mHandler == null) {
769 try {
770 wait();
771 } catch (InterruptedException e) {
772 Thread.currentThread().interrupt();
773 }
774 }
775 Message callback = Message.obtain(mHandler, SUPPLY_PIN_COMPLETE);
776
777 if (puk == null) {
778 mSimCard.supplyPin(pin, callback);
779 } else {
780 mSimCard.supplyPuk(puk, pin, callback);
781 }
782
783 while (!mDone) {
784 try {
785 Log.d(LOG_TAG, "wait for done");
786 wait();
787 } catch (InterruptedException e) {
788 // Restore the interrupted status
789 Thread.currentThread().interrupt();
790 }
791 }
792 Log.d(LOG_TAG, "done");
Wink Saville9de0f752013-10-22 19:04:03 -0700793 int[] resultArray = new int[2];
794 resultArray[0] = mResult;
795 resultArray[1] = mRetryCount;
796 return resultArray;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700797 }
798 }
799
800 public void updateServiceLocation() {
801 // No permission check needed here: this call is harmless, and it's
802 // needed for the ServiceState.requestStateUpdate() call (which is
803 // already intentionally exposed to 3rd parties.)
804 mPhone.updateServiceLocation();
805 }
806
807 public boolean isRadioOn() {
808 return mPhone.getServiceState().getVoiceRegState() != ServiceState.STATE_POWER_OFF;
809 }
810
811 public void toggleRadioOnOff() {
812 enforceModifyPermission();
813 mPhone.setRadioPower(!isRadioOn());
814 }
815 public boolean setRadio(boolean turnOn) {
816 enforceModifyPermission();
817 if ((mPhone.getServiceState().getVoiceRegState() != ServiceState.STATE_POWER_OFF) != turnOn) {
818 toggleRadioOnOff();
819 }
820 return true;
821 }
822 public boolean setRadioPower(boolean turnOn) {
823 enforceModifyPermission();
824 mPhone.setRadioPower(turnOn);
825 return true;
826 }
827
828 public boolean enableDataConnectivity() {
829 enforceModifyPermission();
830 ConnectivityManager cm =
831 (ConnectivityManager)mApp.getSystemService(Context.CONNECTIVITY_SERVICE);
832 cm.setMobileDataEnabled(true);
833 return true;
834 }
835
836 public int enableApnType(String type) {
837 enforceModifyPermission();
838 return mPhone.enableApnType(type);
839 }
840
841 public int disableApnType(String type) {
842 enforceModifyPermission();
843 return mPhone.disableApnType(type);
844 }
845
846 public boolean disableDataConnectivity() {
847 enforceModifyPermission();
848 ConnectivityManager cm =
849 (ConnectivityManager)mApp.getSystemService(Context.CONNECTIVITY_SERVICE);
850 cm.setMobileDataEnabled(false);
851 return true;
852 }
853
854 public boolean isDataConnectivityPossible() {
855 return mPhone.isDataConnectivityPossible();
856 }
857
858 public boolean handlePinMmi(String dialString) {
859 enforceModifyPermission();
860 return (Boolean) sendRequest(CMD_HANDLE_PIN_MMI, dialString);
861 }
862
863 public void cancelMissedCallsNotification() {
864 enforceModifyPermission();
865 mApp.notificationMgr.cancelMissedCallNotification();
866 }
867
868 public int getCallState() {
869 return DefaultPhoneNotifier.convertCallState(mCM.getState());
870 }
871
872 public int getDataState() {
873 return DefaultPhoneNotifier.convertDataState(mPhone.getDataConnectionState());
874 }
875
876 public int getDataActivity() {
877 return DefaultPhoneNotifier.convertDataActivityState(mPhone.getDataActivityState());
878 }
879
880 @Override
881 public Bundle getCellLocation() {
882 try {
883 mApp.enforceCallingOrSelfPermission(
884 android.Manifest.permission.ACCESS_FINE_LOCATION, null);
885 } catch (SecurityException e) {
886 // If we have ACCESS_FINE_LOCATION permission, skip the check for ACCESS_COARSE_LOCATION
887 // A failure should throw the SecurityException from ACCESS_COARSE_LOCATION since this
888 // is the weaker precondition
889 mApp.enforceCallingOrSelfPermission(
890 android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
891 }
892
Jake Hambye994d462014-02-03 13:10:13 -0800893 if (checkIfCallerIsSelfOrForegroundUser()) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700894 if (DBG_LOC) log("getCellLocation: is active user");
895 Bundle data = new Bundle();
896 mPhone.getCellLocation().fillInNotifierBundle(data);
897 return data;
898 } else {
899 if (DBG_LOC) log("getCellLocation: suppress non-active user");
900 return null;
901 }
902 }
903
904 @Override
905 public void enableLocationUpdates() {
906 mApp.enforceCallingOrSelfPermission(
907 android.Manifest.permission.CONTROL_LOCATION_UPDATES, null);
908 mPhone.enableLocationUpdates();
909 }
910
911 @Override
912 public void disableLocationUpdates() {
913 mApp.enforceCallingOrSelfPermission(
914 android.Manifest.permission.CONTROL_LOCATION_UPDATES, null);
915 mPhone.disableLocationUpdates();
916 }
917
918 @Override
919 @SuppressWarnings("unchecked")
920 public List<NeighboringCellInfo> getNeighboringCellInfo(String callingPackage) {
921 try {
922 mApp.enforceCallingOrSelfPermission(
923 android.Manifest.permission.ACCESS_FINE_LOCATION, null);
924 } catch (SecurityException e) {
925 // If we have ACCESS_FINE_LOCATION permission, skip the check
926 // for ACCESS_COARSE_LOCATION
927 // A failure should throw the SecurityException from
928 // ACCESS_COARSE_LOCATION since this is the weaker precondition
929 mApp.enforceCallingOrSelfPermission(
930 android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
931 }
932
933 if (mAppOps.noteOp(AppOpsManager.OP_NEIGHBORING_CELLS, Binder.getCallingUid(),
934 callingPackage) != AppOpsManager.MODE_ALLOWED) {
935 return null;
936 }
Jake Hambye994d462014-02-03 13:10:13 -0800937 if (checkIfCallerIsSelfOrForegroundUser()) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700938 if (DBG_LOC) log("getNeighboringCellInfo: is active user");
939
940 ArrayList<NeighboringCellInfo> cells = null;
941
942 try {
943 cells = (ArrayList<NeighboringCellInfo>) sendRequest(
944 CMD_HANDLE_NEIGHBORING_CELL, null);
945 } catch (RuntimeException e) {
Shishir Agrawal566b7612013-10-28 14:41:00 -0700946 loge("getNeighboringCellInfo " + e);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700947 }
948 return cells;
949 } else {
950 if (DBG_LOC) log("getNeighboringCellInfo: suppress non-active user");
951 return null;
952 }
953 }
954
955
956 @Override
957 public List<CellInfo> getAllCellInfo() {
958 try {
959 mApp.enforceCallingOrSelfPermission(
960 android.Manifest.permission.ACCESS_FINE_LOCATION, null);
961 } catch (SecurityException e) {
962 // If we have ACCESS_FINE_LOCATION permission, skip the check for ACCESS_COARSE_LOCATION
963 // A failure should throw the SecurityException from ACCESS_COARSE_LOCATION since this
964 // is the weaker precondition
965 mApp.enforceCallingOrSelfPermission(
966 android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
967 }
968
Jake Hambye994d462014-02-03 13:10:13 -0800969 if (checkIfCallerIsSelfOrForegroundUser()) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700970 if (DBG_LOC) log("getAllCellInfo: is active user");
971 return mPhone.getAllCellInfo();
972 } else {
973 if (DBG_LOC) log("getAllCellInfo: suppress non-active user");
974 return null;
975 }
976 }
977
978 public void setCellInfoListRate(int rateInMillis) {
979 mPhone.setCellInfoListRate(rateInMillis);
980 }
981
982 //
983 // Internal helper methods.
984 //
985
Jake Hambye994d462014-02-03 13:10:13 -0800986 private static boolean checkIfCallerIsSelfOrForegroundUser() {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700987 boolean ok;
988
989 boolean self = Binder.getCallingUid() == Process.myUid();
990 if (!self) {
991 // Get the caller's user id then clear the calling identity
992 // which will be restored in the finally clause.
993 int callingUser = UserHandle.getCallingUserId();
994 long ident = Binder.clearCallingIdentity();
995
996 try {
997 // With calling identity cleared the current user is the foreground user.
998 int foregroundUser = ActivityManager.getCurrentUser();
999 ok = (foregroundUser == callingUser);
1000 if (DBG_LOC) {
1001 log("checkIfCallerIsSelfOrForegoundUser: foregroundUser=" + foregroundUser
1002 + " callingUser=" + callingUser + " ok=" + ok);
1003 }
1004 } catch (Exception ex) {
1005 if (DBG_LOC) loge("checkIfCallerIsSelfOrForegoundUser: Exception ex=" + ex);
1006 ok = false;
1007 } finally {
1008 Binder.restoreCallingIdentity(ident);
1009 }
1010 } else {
1011 if (DBG_LOC) log("checkIfCallerIsSelfOrForegoundUser: is self");
1012 ok = true;
1013 }
1014 if (DBG_LOC) log("checkIfCallerIsSelfOrForegoundUser: ret=" + ok);
1015 return ok;
1016 }
1017
1018 /**
1019 * Make sure the caller has the READ_PHONE_STATE permission.
1020 *
1021 * @throws SecurityException if the caller does not have the required permission
1022 */
1023 private void enforceReadPermission() {
1024 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_STATE, null);
1025 }
1026
1027 /**
1028 * Make sure the caller has the MODIFY_PHONE_STATE permission.
1029 *
1030 * @throws SecurityException if the caller does not have the required permission
1031 */
1032 private void enforceModifyPermission() {
1033 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE, null);
1034 }
1035
1036 /**
1037 * Make sure the caller has the CALL_PHONE permission.
1038 *
1039 * @throws SecurityException if the caller does not have the required permission
1040 */
1041 private void enforceCallPermission() {
1042 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.CALL_PHONE, null);
1043 }
1044
Shishir Agrawal566b7612013-10-28 14:41:00 -07001045 /**
Gabriel Peal36ebb0d2014-03-20 09:20:43 -07001046 * Make sure the caller has the READ_PRIVILEGED_PHONE_STATE permission.
1047 *
1048 * @throws SecurityException if the caller does not have the required permission
1049 */
1050 private void enforcePrivilegedPhoneStatePermission() {
1051 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
1052 null);
1053 }
1054
1055 /**
Shishir Agrawal566b7612013-10-28 14:41:00 -07001056 * Make sure the caller has SIM_COMMUNICATION permission.
1057 *
1058 * @throws SecurityException if the caller does not have the required permission.
1059 */
1060 private void enforceSimCommunicationPermission() {
1061 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.SIM_COMMUNICATION, null);
1062 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001063
1064 private String createTelUrl(String number) {
1065 if (TextUtils.isEmpty(number)) {
1066 return null;
1067 }
1068
Jake Hambye994d462014-02-03 13:10:13 -08001069 return "tel:" + number;
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001070 }
1071
Jake Hambye994d462014-02-03 13:10:13 -08001072 private static void log(String msg) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001073 Log.d(LOG_TAG, "[PhoneIntfMgr] " + msg);
1074 }
1075
Jake Hambye994d462014-02-03 13:10:13 -08001076 private static void loge(String msg) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001077 Log.e(LOG_TAG, "[PhoneIntfMgr] " + msg);
1078 }
1079
1080 public int getActivePhoneType() {
1081 return mPhone.getPhoneType();
1082 }
1083
1084 /**
1085 * Returns the CDMA ERI icon index to display
1086 */
1087 public int getCdmaEriIconIndex() {
1088 return mPhone.getCdmaEriIconIndex();
1089 }
1090
1091 /**
1092 * Returns the CDMA ERI icon mode,
1093 * 0 - ON
1094 * 1 - FLASHING
1095 */
1096 public int getCdmaEriIconMode() {
1097 return mPhone.getCdmaEriIconMode();
1098 }
1099
1100 /**
1101 * Returns the CDMA ERI text,
1102 */
1103 public String getCdmaEriText() {
1104 return mPhone.getCdmaEriText();
1105 }
1106
1107 /**
1108 * Returns true if CDMA provisioning needs to run.
1109 */
1110 public boolean needsOtaServiceProvisioning() {
1111 return mPhone.needsOtaServiceProvisioning();
1112 }
1113
1114 /**
1115 * Returns the unread count of voicemails
1116 */
1117 public int getVoiceMessageCount() {
1118 return mPhone.getVoiceMessageCount();
1119 }
1120
1121 /**
1122 * Returns the data network type
1123 *
1124 * @Deprecated to be removed Q3 2013 use {@link #getDataNetworkType}.
1125 */
1126 @Override
1127 public int getNetworkType() {
1128 return mPhone.getServiceState().getDataNetworkType();
1129 }
1130
1131 /**
1132 * Returns the data network type
1133 */
1134 @Override
1135 public int getDataNetworkType() {
1136 return mPhone.getServiceState().getDataNetworkType();
1137 }
1138
1139 /**
1140 * Returns the data network type
1141 */
1142 @Override
1143 public int getVoiceNetworkType() {
1144 return mPhone.getServiceState().getVoiceNetworkType();
1145 }
1146
1147 /**
1148 * @return true if a ICC card is present
1149 */
1150 public boolean hasIccCard() {
1151 return mPhone.getIccCard().hasIccCard();
1152 }
1153
1154 /**
1155 * Return if the current radio is LTE on CDMA. This
1156 * is a tri-state return value as for a period of time
1157 * the mode may be unknown.
1158 *
1159 * @return {@link Phone#LTE_ON_CDMA_UNKNOWN}, {@link Phone#LTE_ON_CDMA_FALSE}
Jake Hambye994d462014-02-03 13:10:13 -08001160 * or {@link Phone#LTE_ON_CDMA_TRUE}
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001161 */
1162 public int getLteOnCdmaMode() {
1163 return mPhone.getLteOnCdmaMode();
1164 }
Shishir Agrawal566b7612013-10-28 14:41:00 -07001165
1166 @Override
1167 public int iccOpenLogicalChannel(String AID) {
1168 enforceSimCommunicationPermission();
1169
1170 if (DBG) log("iccOpenLogicalChannel: " + AID);
1171 Integer channel = (Integer)sendRequest(CMD_OPEN_CHANNEL, AID);
1172 if (DBG) log("iccOpenLogicalChannel: " + channel);
Jake Hambye994d462014-02-03 13:10:13 -08001173 return channel;
Shishir Agrawal566b7612013-10-28 14:41:00 -07001174 }
1175
1176 @Override
1177 public boolean iccCloseLogicalChannel(int channel) {
1178 enforceSimCommunicationPermission();
1179
1180 if (DBG) log("iccCloseLogicalChannel: " + channel);
1181 if (channel < 0) {
1182 return false;
1183 }
Jake Hambye994d462014-02-03 13:10:13 -08001184 Boolean success = (Boolean)sendRequest(CMD_CLOSE_CHANNEL, channel);
Shishir Agrawal566b7612013-10-28 14:41:00 -07001185 if (DBG) log("iccCloseLogicalChannel: " + success);
1186 return success;
1187 }
1188
1189 @Override
1190 public String iccTransmitApduLogicalChannel(int channel, int cla,
1191 int command, int p1, int p2, int p3, String data) {
1192 enforceSimCommunicationPermission();
1193
1194 if (DBG) {
1195 log("iccTransmitApduLogicalChannel: chnl=" + channel + " cla=" + cla +
1196 " cmd=" + command + " p1=" + p1 + " p2=" + p2 + " p3=" + p3 +
1197 " data=" + data);
1198 }
1199
1200 if (channel < 0) {
1201 return "";
1202 }
1203
1204 IccIoResult response = (IccIoResult)sendRequest(CMD_TRANSMIT_APDU,
1205 new IccAPDUArgument(channel, cla, command, p1, p2, p3, data));
1206 if (DBG) log("iccTransmitApduLogicalChannel: " + response);
1207
1208 // If the payload is null, there was an error. Indicate that by returning
1209 // an empty string.
1210 if (response.payload == null) {
1211 return "";
1212 }
1213
1214 // Append the returned status code to the end of the response payload.
1215 String s = Integer.toHexString(
1216 (response.sw1 << 8) + response.sw2 + 0x10000).substring(1);
1217 s = IccUtils.bytesToHexString(response.payload) + s;
1218 return s;
1219 }
Jake Hambye994d462014-02-03 13:10:13 -08001220
1221 /**
1222 * Read one of the NV items defined in {@link com.android.internal.telephony.RadioNVItems}
1223 * and {@code ril_nv_items.h}. Used for device configuration by some CDMA operators.
1224 *
1225 * @param itemID the ID of the item to read
1226 * @return the NV item as a String, or null on error.
1227 */
1228 @Override
1229 public String nvReadItem(int itemID) {
1230 enforceModifyPermission();
1231 if (DBG) log("nvReadItem: item " + itemID);
1232 String value = (String) sendRequest(CMD_NV_READ_ITEM, itemID);
1233 if (DBG) log("nvReadItem: item " + itemID + " is \"" + value + '"');
1234 return value;
1235 }
1236
1237 /**
1238 * Write one of the NV items defined in {@link com.android.internal.telephony.RadioNVItems}
1239 * and {@code ril_nv_items.h}. Used for device configuration by some CDMA operators.
1240 *
1241 * @param itemID the ID of the item to read
1242 * @param itemValue the value to write, as a String
1243 * @return true on success; false on any failure
1244 */
1245 @Override
1246 public boolean nvWriteItem(int itemID, String itemValue) {
1247 enforceModifyPermission();
1248 if (DBG) log("nvWriteItem: item " + itemID + " value \"" + itemValue + '"');
1249 Boolean success = (Boolean) sendRequest(CMD_NV_WRITE_ITEM,
1250 new Pair<Integer, String>(itemID, itemValue));
1251 if (DBG) log("nvWriteItem: item " + itemID + ' ' + (success ? "ok" : "fail"));
1252 return success;
1253 }
1254
1255 /**
1256 * Update the CDMA Preferred Roaming List (PRL) in the radio NV storage.
1257 * Used for device configuration by some CDMA operators.
1258 *
1259 * @param preferredRoamingList byte array containing the new PRL
1260 * @return true on success; false on any failure
1261 */
1262 @Override
1263 public boolean nvWriteCdmaPrl(byte[] preferredRoamingList) {
1264 enforceModifyPermission();
1265 if (DBG) log("nvWriteCdmaPrl: value: " + HexDump.toHexString(preferredRoamingList));
1266 Boolean success = (Boolean) sendRequest(CMD_NV_WRITE_CDMA_PRL, preferredRoamingList);
1267 if (DBG) log("nvWriteCdmaPrl: " + (success ? "ok" : "fail"));
1268 return success;
1269 }
1270
1271 /**
1272 * Perform the specified type of NV config reset.
1273 * Used for device configuration by some CDMA operators.
1274 *
1275 * @param resetType the type of reset to perform (1 == factory reset; 2 == NV-only reset)
1276 * @return true on success; false on any failure
1277 */
1278 @Override
1279 public boolean nvResetConfig(int resetType) {
1280 enforceModifyPermission();
1281 if (DBG) log("nvResetConfig: type " + resetType);
1282 Boolean success = (Boolean) sendRequest(CMD_NV_RESET_CONFIG, resetType);
1283 if (DBG) log("nvResetConfig: type " + resetType + ' ' + (success ? "ok" : "fail"));
1284 return success;
1285 }
Jake Hamby7c27be32014-03-03 13:25:59 -08001286
1287 /**
1288 * Get the preferred network type.
1289 * Used for device configuration by some CDMA operators.
1290 *
1291 * @return the preferred network type, defined in RILConstants.java.
1292 */
1293 @Override
1294 public int getPreferredNetworkType() {
1295 enforceModifyPermission();
1296 if (DBG) log("getPreferredNetworkType");
1297 int[] result = (int[]) sendRequest(CMD_GET_PREFERRED_NETWORK_TYPE, null);
1298 int networkType = (result != null ? result[0] : -1);
1299 if (DBG) log("getPreferredNetworkType: " + networkType);
1300 return networkType;
1301 }
1302
1303 /**
1304 * Set the preferred network type.
1305 * Used for device configuration by some CDMA operators.
1306 *
1307 * @param networkType the preferred network type, defined in RILConstants.java.
1308 * @return true on success; false on any failure.
1309 */
1310 @Override
1311 public boolean setPreferredNetworkType(int networkType) {
1312 enforceModifyPermission();
1313 if (DBG) log("setPreferredNetworkType: type " + networkType);
1314 Boolean success = (Boolean) sendRequest(CMD_SET_PREFERRED_NETWORK_TYPE, networkType);
1315 if (DBG) log("setPreferredNetworkType: " + (success ? "ok" : "fail"));
1316 return success;
1317 }
Gabriel Peal36ebb0d2014-03-20 09:20:43 -07001318
1319 @Override
1320 public void toggleHold() {
1321 enforceModifyPermission();
1322
1323 try {
1324 PhoneUtils.switchHoldingAndActive(mCM.getFirstActiveBgCall());
1325 } catch (Exception e) {
1326 Log.e(LOG_TAG, "Error during toggleHold().", e);
1327 }
1328 }
1329
1330 @Override
1331 public void merge() {
1332 enforceModifyPermission();
1333
1334 try {
1335 if (PhoneUtils.okToMergeCalls(mCM)) {
1336 PhoneUtils.mergeCalls(mCM);
1337 }
1338 } catch (Exception e) {
1339 Log.e(LOG_TAG, "Error during merge().", e);
1340 }
1341 }
1342
1343 @Override
1344 public void swap() {
1345 enforceModifyPermission();
1346
1347 try {
1348 PhoneUtils.swap();
1349 } catch (Exception e) {
1350 Log.e(LOG_TAG, "Error during swap().", e);
1351 }
1352 }
1353
1354 @Override
1355 public void mute(boolean onOff) {
1356 enforceModifyPermission();
1357
1358 try {
1359 PhoneUtils.setMute(onOff);
1360 } catch (Exception e) {
1361 Log.e(LOG_TAG, "Error during mute().", e);
1362 }
1363 }
1364
1365 @Override
1366 public void playDtmfTone(char digit, boolean timedShortTone) {
1367 enforceModifyPermission();
1368
1369 synchronized (mDtmfStopHandler) {
1370 try {
1371 mDtmfTonePlayer.playDtmfTone(digit, timedShortTone);
1372 } catch (Exception e) {
1373 Log.e(LOG_TAG, "Error playing DTMF tone.", e);
1374 }
1375
1376 if (mDtmfStopRunnable != null) {
1377 mDtmfStopHandler.removeCallbacks(mDtmfStopRunnable);
1378 }
1379 mDtmfStopRunnable = new Runnable() {
1380 @Override
1381 public void run() {
1382 synchronized (mDtmfStopHandler) {
1383 if (mDtmfStopRunnable == this) {
1384 mDtmfTonePlayer.stopDtmfTone();
1385 mDtmfStopRunnable = null;
1386 }
1387 }
1388 }
1389 };
1390 mDtmfStopHandler.postDelayed(mDtmfStopRunnable, 5000);
1391 }
1392 }
1393
1394 @Override
1395 public void stopDtmfTone() {
1396 enforceModifyPermission();
1397
1398 synchronized (mDtmfStopHandler) {
1399 try {
1400 mDtmfTonePlayer.stopDtmfTone();
1401 } catch (Exception e) {
1402 Log.e(LOG_TAG, "Error stopping DTMF tone.", e);
1403 }
1404
1405 if (mDtmfStopRunnable != null) {
1406 mDtmfStopHandler.removeCallbacks(mDtmfStopRunnable);
1407 mDtmfStopRunnable = null;
1408 }
1409 }
1410 }
1411
1412 @Override
1413 public void addListener(ITelephonyListener listener) {
1414 enforcePrivilegedPhoneStatePermission();
1415
1416 if (listener == null) {
1417 throw new IllegalArgumentException("Listener must not be null.");
1418 }
1419
1420 synchronized (mListeners) {
1421 IBinder listenerBinder = listener.asBinder();
1422 for (ITelephonyListener l : mListeners) {
1423 if (l.asBinder().equals(listenerBinder)) {
1424 Log.w(LOG_TAG, "Listener already registered. Ignoring.");
1425 return;
1426 }
1427 }
1428 mListeners.add(listener);
1429 mDeathRecipients.put(listener.asBinder(),
1430 new TelephonyListenerDeathRecipient(listener.asBinder()));
1431
1432 // update the new listener so they get the full call state immediately
1433 for (Call call : mCallModeler.getFullList()) {
1434 try {
1435 notifyListenerOfCallLocked(call, listener);
1436 } catch (RemoteException e) {
1437 Log.e(LOG_TAG, "Error updating new listener. Ignoring.");
1438 removeListenerInternal(listener);
1439 }
1440 }
1441 }
1442 }
1443
1444 @Override
1445 public void removeListener(ITelephonyListener listener) {
1446 enforcePrivilegedPhoneStatePermission();
1447
1448 if (listener == null) {
1449 throw new IllegalArgumentException("Listener must not be null.");
1450 }
1451
1452 removeListenerInternal(listener);
1453 }
1454
1455 private void removeListenerInternal(ITelephonyListener listener) {
1456 IBinder listenerBinder = listener.asBinder();
1457
1458 synchronized (mListeners) {
1459 for (Iterator<ITelephonyListener> it = mListeners.iterator(); it.hasNext(); ) {
1460 ITelephonyListener nextListener = it.next();
1461 if (nextListener.asBinder().equals(listenerBinder)) {
1462 TelephonyListenerDeathRecipient dr = mDeathRecipients.get(listener.asBinder());
1463 if (dr != null) {
1464 dr.unlinkDeathRecipient();
1465 }
1466 it.remove();
1467 }
1468 }
1469 }
1470 }
1471
1472 /** CallModeler.Listener implementation **/
1473
1474 @Override
1475 public void onDisconnect(Call call) {
1476 notifyListenersOfCall(call);
1477 }
1478
1479 @Override
1480 public void onIncoming(Call call) {
1481 notifyListenersOfCall(call);
1482 }
1483
1484 @Override
1485 public void onUpdate(List<Call> calls) {
1486 for (Call call : calls) {
1487 notifyListenersOfCall(call);
1488 }
1489 }
1490
1491 @Override
1492 public void onPostDialAction(
1493 Connection.PostDialState state, int callId, String remainingChars, char c) { }
1494
1495 private void notifyListenersOfCall(Call call) {
1496 synchronized (mListeners) {
1497 for (Iterator<ITelephonyListener> it = mListeners.iterator(); it.hasNext(); ) {
1498 ITelephonyListener listener = it.next();
1499 try {
1500 notifyListenerOfCallLocked(call, listener);
1501 } catch (RemoteException e) {
1502 TelephonyListenerDeathRecipient deathRecipient =
1503 mDeathRecipients.get(listener.asBinder());
1504 if (deathRecipient != null) {
1505 deathRecipient.unlinkDeathRecipient();
1506 }
1507 it.remove();
1508 }
1509 }
1510 }
1511 }
1512
1513 private void notifyListenerOfCallLocked(final Call call,final ITelephonyListener listener)
1514 throws RemoteException {
1515 if (Binder.isProxy(listener)) {
1516 listener.onUpdate(call.getCallId(), call.getState(), call.getNumber());
1517 } else {
1518 mMainThreadHandler.post(new Runnable() {
1519
1520 @Override
1521 public void run() {
1522 try {
1523 listener.onUpdate(call.getCallId(), call.getState(), call.getNumber());
1524 } catch (RemoteException e) {
1525 Log.wtf(LOG_TAG, "Local binder call failed with RemoteException.", e);
1526 }
1527 }
1528 });
1529 }
1530
1531 }
1532
1533 private class TelephonyListenerDeathRecipient implements Binder.DeathRecipient {
1534 private final IBinder mBinder;
1535
1536 public TelephonyListenerDeathRecipient(IBinder listener) {
1537 mBinder = listener;
1538 try {
1539 mBinder.linkToDeath(this, 0);
1540 } catch (RemoteException e) {
1541 unlinkDeathRecipient();
1542 }
1543 }
1544
1545 @Override
1546 public void binderDied() {
1547 synchronized (mListeners) {
1548 if (mListeners.contains(mBinder)) {
1549 mListeners.remove(mBinder);
1550 Log.w(LOG_TAG, "ITelephonyListener died. Removing.");
1551 } else {
1552 Log.w(LOG_TAG, "TelephonyListener binder died but the listener " +
1553 "is not registered.");
1554 }
1555 }
1556 }
1557
1558 public void unlinkDeathRecipient() {
1559 mBinder.unlinkToDeath(this, 0);
1560 }
1561 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001562}