blob: d8b0d301f8e1c96c8234cac7657adb84bf02e9d7 [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;
30import android.os.Looper;
31import android.os.Message;
32import android.os.Process;
33import android.os.ServiceManager;
34import android.os.UserHandle;
Ihab Awadf2177b72013-11-25 13:33:23 -080035import android.provider.Settings;
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;
Ihab Awadf2177b72013-11-25 13:33:23 -080039import android.telephony.TelephonyManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070040import android.text.TextUtils;
41import android.util.Log;
Jake Hambye994d462014-02-03 13:10:13 -080042import android.util.Pair;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070043
Shishir Agrawal566b7612013-10-28 14:41:00 -070044import com.android.internal.telephony.CallManager;
45import com.android.internal.telephony.CommandException;
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;
Sailesh Nepalbd76e4e2013-10-27 13:59:44 -070048import com.android.internal.telephony.IThirdPartyCallProvider;
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;
Sailesh Nepalbd76e4e2013-10-27 13:59:44 -070052import com.android.internal.telephony.thirdpartyphone.ThirdPartyPhone;
Shishir Agrawal566b7612013-10-28 14:41:00 -070053import com.android.internal.telephony.uicc.IccIoResult;
54import com.android.internal.telephony.uicc.IccUtils;
55import com.android.internal.telephony.uicc.UiccController;
Jake Hambye994d462014-02-03 13:10:13 -080056import com.android.internal.util.HexDump;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070057
Santos Cordon7d4ddf62013-07-10 11:58:08 -070058import java.util.ArrayList;
Jake Hambye994d462014-02-03 13:10:13 -080059import java.util.List;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070060
61/**
62 * Implementation of the ITelephony interface.
63 */
64public class PhoneInterfaceManager extends ITelephony.Stub {
65 private static final String LOG_TAG = "PhoneInterfaceManager";
66 private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
67 private static final boolean DBG_LOC = false;
68
69 // Message codes used with mMainThreadHandler
70 private static final int CMD_HANDLE_PIN_MMI = 1;
71 private static final int CMD_HANDLE_NEIGHBORING_CELL = 2;
72 private static final int EVENT_NEIGHBORING_CELL_DONE = 3;
73 private static final int CMD_ANSWER_RINGING_CALL = 4;
74 private static final int CMD_END_CALL = 5; // not used yet
75 private static final int CMD_SILENCE_RINGER = 6;
Shishir Agrawal566b7612013-10-28 14:41:00 -070076 private static final int CMD_TRANSMIT_APDU = 7;
77 private static final int EVENT_TRANSMIT_APDU_DONE = 8;
78 private static final int CMD_OPEN_CHANNEL = 9;
79 private static final int EVENT_OPEN_CHANNEL_DONE = 10;
80 private static final int CMD_CLOSE_CHANNEL = 11;
81 private static final int EVENT_CLOSE_CHANNEL_DONE = 12;
Evan Charlton62e3eac2014-02-05 09:17:29 -080082 // TODO: Remove this.
83 private static final int CMD_NEW_INCOMING_THIRD_PARTY_CALL = 100;
Jake Hambye994d462014-02-03 13:10:13 -080084 private static final int CMD_NV_READ_ITEM = 13;
85 private static final int EVENT_NV_READ_ITEM_DONE = 14;
86 private static final int CMD_NV_WRITE_ITEM = 15;
87 private static final int EVENT_NV_WRITE_ITEM_DONE = 16;
88 private static final int CMD_NV_WRITE_CDMA_PRL = 17;
89 private static final int EVENT_NV_WRITE_CDMA_PRL_DONE = 18;
90 private static final int CMD_NV_RESET_CONFIG = 19;
91 private static final int EVENT_NV_RESET_CONFIG_DONE = 20;
92 private static final int CMD_SET_RADIO_MODE = 21;
93 private static final int EVENT_SET_RADIO_MODE_DONE = 22;
Derek Tan4d5e5c12014-02-04 11:54:58 -080094 private static final int CMD_SEND_ENVELOPE = 23;
95 private static final int EVENT_SEND_ENVELOPE_DONE = 24;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070096
97 /** The singleton instance. */
98 private static PhoneInterfaceManager sInstance;
99
100 PhoneGlobals mApp;
101 Phone mPhone;
102 CallManager mCM;
103 AppOpsManager mAppOps;
104 MainThreadHandler mMainThreadHandler;
Santos Cordon406c0342013-08-28 00:07:47 -0700105 CallHandlerServiceProxy mCallHandlerService;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700106
107 /**
Shishir Agrawal566b7612013-10-28 14:41:00 -0700108 * A request object to use for transmitting data to an ICC.
109 */
110 private static final class IccAPDUArgument {
111 public int channel, cla, command, p1, p2, p3;
112 public String data;
113
114 public IccAPDUArgument(int channel, int cla, int command,
115 int p1, int p2, int p3, String data) {
116 this.channel = channel;
117 this.cla = cla;
118 this.command = command;
119 this.p1 = p1;
120 this.p2 = p2;
121 this.p3 = p3;
122 this.data = data;
123 }
124 }
125
126 /**
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700127 * A request object for use with {@link MainThreadHandler}. Requesters should wait() on the
128 * request after sending. The main thread will notify the request when it is complete.
129 */
130 private static final class MainThreadRequest {
131 /** The argument to use for the request */
132 public Object argument;
133 /** The result of the request that is run on the main thread */
134 public Object result;
135
136 public MainThreadRequest(Object argument) {
137 this.argument = argument;
138 }
139 }
140
Sailesh Nepalcc0375f2013-11-13 09:15:18 -0800141 private static final class IncomingThirdPartyCallArgs {
142 public final ComponentName component;
143 public final String callId;
144 public final String callerDisplayName;
145
146 public IncomingThirdPartyCallArgs(ComponentName component, String callId,
147 String callerDisplayName) {
148 this.component = component;
149 this.callId = callId;
150 this.callerDisplayName = callerDisplayName;
151 }
152 }
153
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700154 /**
155 * A handler that processes messages on the main thread in the phone process. Since many
156 * of the Phone calls are not thread safe this is needed to shuttle the requests from the
157 * inbound binder threads to the main thread in the phone process. The Binder thread
158 * may provide a {@link MainThreadRequest} object in the msg.obj field that they are waiting
159 * on, which will be notified when the operation completes and will contain the result of the
160 * request.
161 *
162 * <p>If a MainThreadRequest object is provided in the msg.obj field,
163 * note that request.result must be set to something non-null for the calling thread to
164 * unblock.
165 */
166 private final class MainThreadHandler extends Handler {
167 @Override
168 public void handleMessage(Message msg) {
169 MainThreadRequest request;
170 Message onCompleted;
171 AsyncResult ar;
172
173 switch (msg.what) {
174 case CMD_HANDLE_PIN_MMI:
175 request = (MainThreadRequest) msg.obj;
Jake Hambye994d462014-02-03 13:10:13 -0800176 request.result = mPhone.handlePinMmi((String) request.argument);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700177 // Wake up the requesting thread
178 synchronized (request) {
179 request.notifyAll();
180 }
181 break;
182
183 case CMD_HANDLE_NEIGHBORING_CELL:
184 request = (MainThreadRequest) msg.obj;
185 onCompleted = obtainMessage(EVENT_NEIGHBORING_CELL_DONE,
186 request);
187 mPhone.getNeighboringCids(onCompleted);
188 break;
189
190 case EVENT_NEIGHBORING_CELL_DONE:
191 ar = (AsyncResult) msg.obj;
192 request = (MainThreadRequest) ar.userObj;
193 if (ar.exception == null && ar.result != null) {
194 request.result = ar.result;
195 } else {
196 // create an empty list to notify the waiting thread
Jake Hambye994d462014-02-03 13:10:13 -0800197 request.result = new ArrayList<NeighboringCellInfo>(0);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700198 }
199 // Wake up the requesting thread
200 synchronized (request) {
201 request.notifyAll();
202 }
203 break;
204
205 case CMD_ANSWER_RINGING_CALL:
206 answerRingingCallInternal();
207 break;
208
209 case CMD_SILENCE_RINGER:
210 silenceRingerInternal();
211 break;
212
213 case CMD_END_CALL:
214 request = (MainThreadRequest) msg.obj;
Jake Hambye994d462014-02-03 13:10:13 -0800215 boolean hungUp;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700216 int phoneType = mPhone.getPhoneType();
217 if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
218 // CDMA: If the user presses the Power button we treat it as
219 // ending the complete call session
220 hungUp = PhoneUtils.hangupRingingAndActive(mPhone);
221 } else if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
222 // GSM: End the call as per the Phone state
223 hungUp = PhoneUtils.hangup(mCM);
224 } else {
225 throw new IllegalStateException("Unexpected phone type: " + phoneType);
226 }
227 if (DBG) log("CMD_END_CALL: " + (hungUp ? "hung up!" : "no call to hang up"));
228 request.result = hungUp;
229 // Wake up the requesting thread
230 synchronized (request) {
231 request.notifyAll();
232 }
233 break;
234
Sailesh Nepalbd76e4e2013-10-27 13:59:44 -0700235 case CMD_NEW_INCOMING_THIRD_PARTY_CALL: {
236 request = (MainThreadRequest) msg.obj;
Sailesh Nepalcc0375f2013-11-13 09:15:18 -0800237 IncomingThirdPartyCallArgs args = (IncomingThirdPartyCallArgs) request.argument;
Sailesh Nepalbfb68322013-11-07 14:07:41 -0800238 ThirdPartyPhone thirdPartyPhone = (ThirdPartyPhone)
Sailesh Nepalcc0375f2013-11-13 09:15:18 -0800239 PhoneUtils.getThirdPartyPhoneFromComponent(mCM, args.component);
240 thirdPartyPhone.takeIncomingCall(args.callId, args.callerDisplayName);
Sailesh Nepalbd76e4e2013-10-27 13:59:44 -0700241 break;
242 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700243
Shishir Agrawal566b7612013-10-28 14:41:00 -0700244 case CMD_TRANSMIT_APDU:
245 request = (MainThreadRequest) msg.obj;
246 IccAPDUArgument argument = (IccAPDUArgument) request.argument;
247 onCompleted = obtainMessage(EVENT_TRANSMIT_APDU_DONE, request);
248 UiccController.getInstance().getUiccCard().iccTransmitApduLogicalChannel(
249 argument.channel, argument.cla, argument.command,
250 argument.p1, argument.p2, argument.p3, argument.data,
251 onCompleted);
252 break;
253
254 case EVENT_TRANSMIT_APDU_DONE:
255 ar = (AsyncResult) msg.obj;
256 request = (MainThreadRequest) ar.userObj;
257 if (ar.exception == null && ar.result != null) {
258 request.result = ar.result;
259 } else {
260 request.result = new IccIoResult(0x6F, 0, (byte[])null);
261 if (ar.result == null) {
262 loge("iccTransmitApduLogicalChannel: Empty response");
Jake Hambye994d462014-02-03 13:10:13 -0800263 } else if (ar.exception instanceof CommandException) {
Shishir Agrawal566b7612013-10-28 14:41:00 -0700264 loge("iccTransmitApduLogicalChannel: CommandException: " +
Jake Hambye994d462014-02-03 13:10:13 -0800265 ar.exception);
Shishir Agrawal566b7612013-10-28 14:41:00 -0700266 } else {
267 loge("iccTransmitApduLogicalChannel: Unknown exception");
268 }
269 }
270 synchronized (request) {
271 request.notifyAll();
272 }
273 break;
274
Derek Tan4d5e5c12014-02-04 11:54:58 -0800275 case CMD_SEND_ENVELOPE:
276 request = (MainThreadRequest) msg.obj;
277 onCompleted = obtainMessage(EVENT_SEND_ENVELOPE_DONE, request);
278 UiccController.getInstance().getUiccCard().sendEnvelope(
279 (String)request.argument, onCompleted);
280 break;
281
282 case EVENT_SEND_ENVELOPE_DONE:
283 ar = (AsyncResult) msg.obj;
284 request = (MainThreadRequest) ar.userObj;
285 if (ar.exception == null) {
286 request.result = (ar.result == null) ? "" : ar.result;
287 } else {
288 loge("sendEnvelope: Unknown exception " + ar.exception);
289 }
290 synchronized (request) {
291 request.notifyAll();
292 }
293 break;
294
Shishir Agrawal566b7612013-10-28 14:41:00 -0700295 case CMD_OPEN_CHANNEL:
296 request = (MainThreadRequest) msg.obj;
297 onCompleted = obtainMessage(EVENT_OPEN_CHANNEL_DONE, request);
298 UiccController.getInstance().getUiccCard().iccOpenLogicalChannel(
299 (String)request.argument, onCompleted);
300 break;
301
302 case EVENT_OPEN_CHANNEL_DONE:
303 ar = (AsyncResult) msg.obj;
304 request = (MainThreadRequest) ar.userObj;
305 if (ar.exception == null && ar.result != null) {
Jake Hambye994d462014-02-03 13:10:13 -0800306 request.result = ((int[]) ar.result)[0];
Shishir Agrawal566b7612013-10-28 14:41:00 -0700307 } else {
Jake Hambye994d462014-02-03 13:10:13 -0800308 request.result = -1;
Shishir Agrawal566b7612013-10-28 14:41:00 -0700309 if (ar.result == null) {
310 loge("iccOpenLogicalChannel: Empty response");
Jake Hambye994d462014-02-03 13:10:13 -0800311 } else if (ar.exception instanceof CommandException) {
Shishir Agrawal566b7612013-10-28 14:41:00 -0700312 loge("iccOpenLogicalChannel: CommandException: " +
Jake Hambye994d462014-02-03 13:10:13 -0800313 ar.exception);
Shishir Agrawal566b7612013-10-28 14:41:00 -0700314 } else {
315 loge("iccOpenLogicalChannel: Unknown exception");
316 }
317 }
318 synchronized (request) {
319 request.notifyAll();
320 }
321 break;
322
323 case CMD_CLOSE_CHANNEL:
324 request = (MainThreadRequest) msg.obj;
325 onCompleted = obtainMessage(EVENT_CLOSE_CHANNEL_DONE,
326 request);
327 UiccController.getInstance().getUiccCard().iccCloseLogicalChannel(
Jake Hambye994d462014-02-03 13:10:13 -0800328 (Integer) request.argument,
Shishir Agrawal566b7612013-10-28 14:41:00 -0700329 onCompleted);
330 break;
331
332 case EVENT_CLOSE_CHANNEL_DONE:
Jake Hambye994d462014-02-03 13:10:13 -0800333 handleNullReturnEvent(msg, "iccCloseLogicalChannel");
334 break;
335
336 case CMD_NV_READ_ITEM:
337 request = (MainThreadRequest) msg.obj;
338 onCompleted = obtainMessage(EVENT_NV_READ_ITEM_DONE, request);
339 mPhone.nvReadItem((Integer) request.argument, onCompleted);
340 break;
341
342 case EVENT_NV_READ_ITEM_DONE:
Shishir Agrawal566b7612013-10-28 14:41:00 -0700343 ar = (AsyncResult) msg.obj;
344 request = (MainThreadRequest) ar.userObj;
Jake Hambye994d462014-02-03 13:10:13 -0800345 if (ar.exception == null && ar.result != null) {
346 request.result = ar.result; // String
Shishir Agrawal566b7612013-10-28 14:41:00 -0700347 } else {
Jake Hambye994d462014-02-03 13:10:13 -0800348 request.result = "";
349 if (ar.result == null) {
350 loge("nvReadItem: Empty response");
351 } else if (ar.exception instanceof CommandException) {
352 loge("nvReadItem: CommandException: " +
353 ar.exception);
Shishir Agrawal566b7612013-10-28 14:41:00 -0700354 } else {
Jake Hambye994d462014-02-03 13:10:13 -0800355 loge("nvReadItem: Unknown exception");
Shishir Agrawal566b7612013-10-28 14:41:00 -0700356 }
357 }
358 synchronized (request) {
359 request.notifyAll();
360 }
361 break;
362
Jake Hambye994d462014-02-03 13:10:13 -0800363 case CMD_NV_WRITE_ITEM:
364 request = (MainThreadRequest) msg.obj;
365 onCompleted = obtainMessage(EVENT_NV_WRITE_ITEM_DONE, request);
366 Pair<Integer, String> idValue = (Pair<Integer, String>) request.argument;
367 mPhone.nvWriteItem(idValue.first, idValue.second, onCompleted);
368 break;
369
370 case EVENT_NV_WRITE_ITEM_DONE:
371 handleNullReturnEvent(msg, "nvWriteItem");
372 break;
373
374 case CMD_NV_WRITE_CDMA_PRL:
375 request = (MainThreadRequest) msg.obj;
376 onCompleted = obtainMessage(EVENT_NV_WRITE_CDMA_PRL_DONE, request);
377 mPhone.nvWriteCdmaPrl((byte[]) request.argument, onCompleted);
378 break;
379
380 case EVENT_NV_WRITE_CDMA_PRL_DONE:
381 handleNullReturnEvent(msg, "nvWriteCdmaPrl");
382 break;
383
384 case CMD_NV_RESET_CONFIG:
385 request = (MainThreadRequest) msg.obj;
386 onCompleted = obtainMessage(EVENT_NV_RESET_CONFIG_DONE, request);
387 mPhone.nvResetConfig((Integer) request.argument, onCompleted);
388 break;
389
390 case EVENT_NV_RESET_CONFIG_DONE:
391 handleNullReturnEvent(msg, "nvResetConfig");
392 break;
393
394 case CMD_SET_RADIO_MODE:
395 request = (MainThreadRequest) msg.obj;
396 onCompleted = obtainMessage(EVENT_SET_RADIO_MODE_DONE, request);
397 mPhone.setRadioMode((Integer) request.argument, onCompleted);
398 break;
399
400 case EVENT_SET_RADIO_MODE_DONE:
401 handleNullReturnEvent(msg, "setRadioMode");
402 break;
403
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700404 default:
405 Log.w(LOG_TAG, "MainThreadHandler: unexpected message code: " + msg.what);
406 break;
407 }
408 }
Jake Hambye994d462014-02-03 13:10:13 -0800409
410 private void handleNullReturnEvent(Message msg, String command) {
411 AsyncResult ar = (AsyncResult) msg.obj;
412 MainThreadRequest request = (MainThreadRequest) ar.userObj;
413 if (ar.exception == null) {
414 request.result = true;
415 } else {
416 request.result = false;
417 if (ar.exception instanceof CommandException) {
418 loge(command + ": CommandException: " + ar.exception);
419 } else {
420 loge(command + ": Unknown exception");
421 }
422 }
423 synchronized (request) {
424 request.notifyAll();
425 }
426 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700427 }
428
429 /**
430 * Posts the specified command to be executed on the main thread,
431 * waits for the request to complete, and returns the result.
432 * @see #sendRequestAsync
433 */
434 private Object sendRequest(int command, Object argument) {
435 if (Looper.myLooper() == mMainThreadHandler.getLooper()) {
436 throw new RuntimeException("This method will deadlock if called from the main thread.");
437 }
438
439 MainThreadRequest request = new MainThreadRequest(argument);
440 Message msg = mMainThreadHandler.obtainMessage(command, request);
441 msg.sendToTarget();
442
443 // Wait for the request to complete
444 synchronized (request) {
445 while (request.result == null) {
446 try {
447 request.wait();
448 } catch (InterruptedException e) {
449 // Do nothing, go back and wait until the request is complete
450 }
451 }
452 }
453 return request.result;
454 }
455
456 /**
457 * Asynchronous ("fire and forget") version of sendRequest():
458 * Posts the specified command to be executed on the main thread, and
459 * returns immediately.
460 * @see #sendRequest
461 */
462 private void sendRequestAsync(int command) {
463 mMainThreadHandler.sendEmptyMessage(command);
464 }
465
466 /**
Sailesh Nepalbd76e4e2013-10-27 13:59:44 -0700467 * Same as {@link #sendRequestAsync(int)} except it takes an argument.
468 * @see {@link #sendRequest(int,Object)}
469 */
470 private void sendRequestAsync(int command, Object argument) {
471 MainThreadRequest request = new MainThreadRequest(argument);
472 Message msg = mMainThreadHandler.obtainMessage(command, request);
473 msg.sendToTarget();
474 }
475
476 /**
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700477 * Initialize the singleton PhoneInterfaceManager instance.
478 * This is only done once, at startup, from PhoneApp.onCreate().
479 */
Santos Cordon406c0342013-08-28 00:07:47 -0700480 /* package */ static PhoneInterfaceManager init(PhoneGlobals app, Phone phone,
481 CallHandlerServiceProxy callHandlerService) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700482 synchronized (PhoneInterfaceManager.class) {
483 if (sInstance == null) {
Santos Cordon406c0342013-08-28 00:07:47 -0700484 sInstance = new PhoneInterfaceManager(app, phone, callHandlerService);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700485 } else {
486 Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance);
487 }
488 return sInstance;
489 }
490 }
491
492 /** Private constructor; @see init() */
Santos Cordon406c0342013-08-28 00:07:47 -0700493 private PhoneInterfaceManager(PhoneGlobals app, Phone phone,
494 CallHandlerServiceProxy callHandlerService) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700495 mApp = app;
496 mPhone = phone;
497 mCM = PhoneGlobals.getInstance().mCM;
498 mAppOps = (AppOpsManager)app.getSystemService(Context.APP_OPS_SERVICE);
499 mMainThreadHandler = new MainThreadHandler();
Santos Cordon406c0342013-08-28 00:07:47 -0700500 mCallHandlerService = callHandlerService;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700501 publish();
502 }
503
504 private void publish() {
505 if (DBG) log("publish: " + this);
506
507 ServiceManager.addService("phone", this);
508 }
509
510 //
511 // Implementation of the ITelephony interface.
512 //
513
514 public void dial(String number) {
515 if (DBG) log("dial: " + number);
516 // No permission check needed here: This is just a wrapper around the
517 // ACTION_DIAL intent, which is available to any app since it puts up
518 // the UI before it does anything.
519
520 String url = createTelUrl(number);
521 if (url == null) {
522 return;
523 }
524
525 // PENDING: should we just silently fail if phone is offhook or ringing?
526 PhoneConstants.State state = mCM.getState();
527 if (state != PhoneConstants.State.OFFHOOK && state != PhoneConstants.State.RINGING) {
528 Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse(url));
529 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
530 mApp.startActivity(intent);
531 }
532 }
533
534 public void call(String callingPackage, String number) {
535 if (DBG) log("call: " + number);
536
537 // This is just a wrapper around the ACTION_CALL intent, but we still
538 // need to do a permission check since we're calling startActivity()
539 // from the context of the phone app.
540 enforceCallPermission();
541
542 if (mAppOps.noteOp(AppOpsManager.OP_CALL_PHONE, Binder.getCallingUid(), callingPackage)
543 != AppOpsManager.MODE_ALLOWED) {
544 return;
545 }
546
547 String url = createTelUrl(number);
548 if (url == null) {
549 return;
550 }
551
552 Intent intent = new Intent(Intent.ACTION_CALL, Uri.parse(url));
553 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
554 mApp.startActivity(intent);
555 }
556
557 private boolean showCallScreenInternal(boolean specifyInitialDialpadState,
Makoto Onukibcf20992013-09-12 17:59:30 -0700558 boolean showDialpad) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700559 if (!PhoneGlobals.sVoiceCapable) {
560 // Never allow the InCallScreen to appear on data-only devices.
561 return false;
562 }
563 if (isIdle()) {
564 return false;
565 }
566 // If the phone isn't idle then go to the in-call screen
567 long callingId = Binder.clearCallingIdentity();
Santos Cordon406c0342013-08-28 00:07:47 -0700568
Makoto Onukibcf20992013-09-12 17:59:30 -0700569 mCallHandlerService.bringToForeground(showDialpad);
Santos Cordon406c0342013-08-28 00:07:47 -0700570
571 Binder.restoreCallingIdentity(callingId);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700572 return true;
573 }
574
575 // Show the in-call screen without specifying the initial dialpad state.
576 public boolean showCallScreen() {
577 return showCallScreenInternal(false, false);
578 }
579
580 // The variation of showCallScreen() that specifies the initial dialpad state.
581 // (Ideally this would be called showCallScreen() too, just with a different
582 // signature, but AIDL doesn't allow that.)
583 public boolean showCallScreenWithDialpad(boolean showDialpad) {
584 return showCallScreenInternal(true, showDialpad);
585 }
586
587 /**
588 * End a call based on call state
589 * @return true is a call was ended
590 */
591 public boolean endCall() {
592 enforceCallPermission();
593 return (Boolean) sendRequest(CMD_END_CALL, null);
594 }
595
596 public void answerRingingCall() {
597 if (DBG) log("answerRingingCall...");
598 // TODO: there should eventually be a separate "ANSWER_PHONE" permission,
599 // but that can probably wait till the big TelephonyManager API overhaul.
600 // For now, protect this call with the MODIFY_PHONE_STATE permission.
601 enforceModifyPermission();
602 sendRequestAsync(CMD_ANSWER_RINGING_CALL);
603 }
604
605 /**
606 * Make the actual telephony calls to implement answerRingingCall().
607 * This should only be called from the main thread of the Phone app.
608 * @see #answerRingingCall
609 *
610 * TODO: it would be nice to return true if we answered the call, or
611 * false if there wasn't actually a ringing incoming call, or some
612 * other error occurred. (In other words, pass back the return value
613 * from PhoneUtils.answerCall() or PhoneUtils.answerAndEndActive().)
614 * But that would require calling this method via sendRequest() rather
615 * than sendRequestAsync(), and right now we don't actually *need* that
616 * return value, so let's just return void for now.
617 */
618 private void answerRingingCallInternal() {
619 final boolean hasRingingCall = !mPhone.getRingingCall().isIdle();
620 if (hasRingingCall) {
621 final boolean hasActiveCall = !mPhone.getForegroundCall().isIdle();
622 final boolean hasHoldingCall = !mPhone.getBackgroundCall().isIdle();
623 if (hasActiveCall && hasHoldingCall) {
624 // Both lines are in use!
625 // TODO: provide a flag to let the caller specify what
626 // policy to use if both lines are in use. (The current
627 // behavior is hardwired to "answer incoming, end ongoing",
628 // which is how the CALL button is specced to behave.)
629 PhoneUtils.answerAndEndActive(mCM, mCM.getFirstActiveRingingCall());
630 return;
631 } else {
632 // answerCall() will automatically hold the current active
633 // call, if there is one.
634 PhoneUtils.answerCall(mCM.getFirstActiveRingingCall());
635 return;
636 }
637 } else {
638 // No call was ringing.
639 return;
640 }
641 }
642
643 public void silenceRinger() {
644 if (DBG) log("silenceRinger...");
645 // TODO: find a more appropriate permission to check here.
646 // (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_SILENCE_RINGER);
650 }
651
652 /**
653 * Internal implemenation of silenceRinger().
654 * This should only be called from the main thread of the Phone app.
655 * @see #silenceRinger
656 */
657 private void silenceRingerInternal() {
658 if ((mCM.getState() == PhoneConstants.State.RINGING)
659 && mApp.notifier.isRinging()) {
660 // Ringer is actually playing, so silence it.
661 if (DBG) log("silenceRingerInternal: silencing...");
662 mApp.notifier.silenceRinger();
663 }
664 }
665
666 public boolean isOffhook() {
667 return (mCM.getState() == PhoneConstants.State.OFFHOOK);
668 }
669
670 public boolean isRinging() {
671 return (mCM.getState() == PhoneConstants.State.RINGING);
672 }
673
674 public boolean isIdle() {
675 return (mCM.getState() == PhoneConstants.State.IDLE);
676 }
677
678 public boolean isSimPinEnabled() {
679 enforceReadPermission();
680 return (PhoneGlobals.getInstance().isSimPinEnabled());
681 }
682
683 public boolean supplyPin(String pin) {
Wink Saville9de0f752013-10-22 19:04:03 -0700684 int [] resultArray = supplyPinReportResult(pin);
685 return (resultArray[0] == PhoneConstants.PIN_RESULT_SUCCESS) ? true : false;
686 }
687
688 public boolean supplyPuk(String puk, String pin) {
689 int [] resultArray = supplyPukReportResult(puk, pin);
690 return (resultArray[0] == PhoneConstants.PIN_RESULT_SUCCESS) ? true : false;
691 }
692
693 /** {@hide} */
694 public int[] supplyPinReportResult(String pin) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700695 enforceModifyPermission();
696 final UnlockSim checkSimPin = new UnlockSim(mPhone.getIccCard());
697 checkSimPin.start();
698 return checkSimPin.unlockSim(null, pin);
699 }
700
Wink Saville9de0f752013-10-22 19:04:03 -0700701 /** {@hide} */
702 public int[] supplyPukReportResult(String puk, String pin) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700703 enforceModifyPermission();
704 final UnlockSim checkSimPuk = new UnlockSim(mPhone.getIccCard());
705 checkSimPuk.start();
706 return checkSimPuk.unlockSim(puk, pin);
707 }
708
709 /**
Wink Saville9de0f752013-10-22 19:04:03 -0700710 * Helper thread to turn async call to SimCard#supplyPin into
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700711 * a synchronous one.
712 */
713 private static class UnlockSim extends Thread {
714
715 private final IccCard mSimCard;
716
717 private boolean mDone = false;
Wink Saville9de0f752013-10-22 19:04:03 -0700718 private int mResult = PhoneConstants.PIN_GENERAL_FAILURE;
719 private int mRetryCount = -1;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700720
721 // For replies from SimCard interface
722 private Handler mHandler;
723
724 // For async handler to identify request type
725 private static final int SUPPLY_PIN_COMPLETE = 100;
726
727 public UnlockSim(IccCard simCard) {
728 mSimCard = simCard;
729 }
730
731 @Override
732 public void run() {
733 Looper.prepare();
734 synchronized (UnlockSim.this) {
735 mHandler = new Handler() {
736 @Override
737 public void handleMessage(Message msg) {
738 AsyncResult ar = (AsyncResult) msg.obj;
739 switch (msg.what) {
740 case SUPPLY_PIN_COMPLETE:
741 Log.d(LOG_TAG, "SUPPLY_PIN_COMPLETE");
742 synchronized (UnlockSim.this) {
Wink Saville9de0f752013-10-22 19:04:03 -0700743 mRetryCount = msg.arg1;
744 if (ar.exception != null) {
745 if (ar.exception instanceof CommandException &&
746 ((CommandException)(ar.exception)).getCommandError()
747 == CommandException.Error.PASSWORD_INCORRECT) {
748 mResult = PhoneConstants.PIN_PASSWORD_INCORRECT;
749 } else {
750 mResult = PhoneConstants.PIN_GENERAL_FAILURE;
751 }
752 } else {
753 mResult = PhoneConstants.PIN_RESULT_SUCCESS;
754 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700755 mDone = true;
756 UnlockSim.this.notifyAll();
757 }
758 break;
759 }
760 }
761 };
762 UnlockSim.this.notifyAll();
763 }
764 Looper.loop();
765 }
766
767 /*
768 * Use PIN or PUK to unlock SIM card
769 *
770 * If PUK is null, unlock SIM card with PIN
771 *
772 * If PUK is not null, unlock SIM card with PUK and set PIN code
773 */
Wink Saville9de0f752013-10-22 19:04:03 -0700774 synchronized int[] unlockSim(String puk, String pin) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700775
776 while (mHandler == null) {
777 try {
778 wait();
779 } catch (InterruptedException e) {
780 Thread.currentThread().interrupt();
781 }
782 }
783 Message callback = Message.obtain(mHandler, SUPPLY_PIN_COMPLETE);
784
785 if (puk == null) {
786 mSimCard.supplyPin(pin, callback);
787 } else {
788 mSimCard.supplyPuk(puk, pin, callback);
789 }
790
791 while (!mDone) {
792 try {
793 Log.d(LOG_TAG, "wait for done");
794 wait();
795 } catch (InterruptedException e) {
796 // Restore the interrupted status
797 Thread.currentThread().interrupt();
798 }
799 }
800 Log.d(LOG_TAG, "done");
Wink Saville9de0f752013-10-22 19:04:03 -0700801 int[] resultArray = new int[2];
802 resultArray[0] = mResult;
803 resultArray[1] = mRetryCount;
804 return resultArray;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700805 }
806 }
807
808 public void updateServiceLocation() {
809 // No permission check needed here: this call is harmless, and it's
810 // needed for the ServiceState.requestStateUpdate() call (which is
811 // already intentionally exposed to 3rd parties.)
812 mPhone.updateServiceLocation();
813 }
814
815 public boolean isRadioOn() {
816 return mPhone.getServiceState().getVoiceRegState() != ServiceState.STATE_POWER_OFF;
817 }
818
819 public void toggleRadioOnOff() {
820 enforceModifyPermission();
821 mPhone.setRadioPower(!isRadioOn());
822 }
823 public boolean setRadio(boolean turnOn) {
824 enforceModifyPermission();
825 if ((mPhone.getServiceState().getVoiceRegState() != ServiceState.STATE_POWER_OFF) != turnOn) {
826 toggleRadioOnOff();
827 }
828 return true;
829 }
830 public boolean setRadioPower(boolean turnOn) {
831 enforceModifyPermission();
832 mPhone.setRadioPower(turnOn);
833 return true;
834 }
835
836 public boolean enableDataConnectivity() {
837 enforceModifyPermission();
838 ConnectivityManager cm =
839 (ConnectivityManager)mApp.getSystemService(Context.CONNECTIVITY_SERVICE);
840 cm.setMobileDataEnabled(true);
841 return true;
842 }
843
844 public int enableApnType(String type) {
845 enforceModifyPermission();
846 return mPhone.enableApnType(type);
847 }
848
849 public int disableApnType(String type) {
850 enforceModifyPermission();
851 return mPhone.disableApnType(type);
852 }
853
854 public boolean disableDataConnectivity() {
855 enforceModifyPermission();
856 ConnectivityManager cm =
857 (ConnectivityManager)mApp.getSystemService(Context.CONNECTIVITY_SERVICE);
858 cm.setMobileDataEnabled(false);
859 return true;
860 }
861
862 public boolean isDataConnectivityPossible() {
863 return mPhone.isDataConnectivityPossible();
864 }
865
866 public boolean handlePinMmi(String dialString) {
867 enforceModifyPermission();
868 return (Boolean) sendRequest(CMD_HANDLE_PIN_MMI, dialString);
869 }
870
871 public void cancelMissedCallsNotification() {
872 enforceModifyPermission();
873 mApp.notificationMgr.cancelMissedCallNotification();
874 }
875
876 public int getCallState() {
877 return DefaultPhoneNotifier.convertCallState(mCM.getState());
878 }
879
880 public int getDataState() {
881 return DefaultPhoneNotifier.convertDataState(mPhone.getDataConnectionState());
882 }
883
884 public int getDataActivity() {
885 return DefaultPhoneNotifier.convertDataActivityState(mPhone.getDataActivityState());
886 }
887
888 @Override
889 public Bundle getCellLocation() {
890 try {
891 mApp.enforceCallingOrSelfPermission(
892 android.Manifest.permission.ACCESS_FINE_LOCATION, null);
893 } catch (SecurityException e) {
894 // If we have ACCESS_FINE_LOCATION permission, skip the check for ACCESS_COARSE_LOCATION
895 // A failure should throw the SecurityException from ACCESS_COARSE_LOCATION since this
896 // is the weaker precondition
897 mApp.enforceCallingOrSelfPermission(
898 android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
899 }
900
Jake Hambye994d462014-02-03 13:10:13 -0800901 if (checkIfCallerIsSelfOrForegroundUser()) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700902 if (DBG_LOC) log("getCellLocation: is active user");
903 Bundle data = new Bundle();
904 mPhone.getCellLocation().fillInNotifierBundle(data);
905 return data;
906 } else {
907 if (DBG_LOC) log("getCellLocation: suppress non-active user");
908 return null;
909 }
910 }
911
912 @Override
913 public void enableLocationUpdates() {
914 mApp.enforceCallingOrSelfPermission(
915 android.Manifest.permission.CONTROL_LOCATION_UPDATES, null);
916 mPhone.enableLocationUpdates();
917 }
918
919 @Override
920 public void disableLocationUpdates() {
921 mApp.enforceCallingOrSelfPermission(
922 android.Manifest.permission.CONTROL_LOCATION_UPDATES, null);
923 mPhone.disableLocationUpdates();
924 }
925
926 @Override
927 @SuppressWarnings("unchecked")
928 public List<NeighboringCellInfo> getNeighboringCellInfo(String callingPackage) {
929 try {
930 mApp.enforceCallingOrSelfPermission(
931 android.Manifest.permission.ACCESS_FINE_LOCATION, null);
932 } catch (SecurityException e) {
933 // If we have ACCESS_FINE_LOCATION permission, skip the check
934 // for ACCESS_COARSE_LOCATION
935 // A failure should throw the SecurityException from
936 // ACCESS_COARSE_LOCATION since this is the weaker precondition
937 mApp.enforceCallingOrSelfPermission(
938 android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
939 }
940
941 if (mAppOps.noteOp(AppOpsManager.OP_NEIGHBORING_CELLS, Binder.getCallingUid(),
942 callingPackage) != AppOpsManager.MODE_ALLOWED) {
943 return null;
944 }
Jake Hambye994d462014-02-03 13:10:13 -0800945 if (checkIfCallerIsSelfOrForegroundUser()) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700946 if (DBG_LOC) log("getNeighboringCellInfo: is active user");
947
948 ArrayList<NeighboringCellInfo> cells = null;
949
950 try {
951 cells = (ArrayList<NeighboringCellInfo>) sendRequest(
952 CMD_HANDLE_NEIGHBORING_CELL, null);
953 } catch (RuntimeException e) {
Shishir Agrawal566b7612013-10-28 14:41:00 -0700954 loge("getNeighboringCellInfo " + e);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700955 }
956 return cells;
957 } else {
958 if (DBG_LOC) log("getNeighboringCellInfo: suppress non-active user");
959 return null;
960 }
961 }
962
963
964 @Override
965 public List<CellInfo> getAllCellInfo() {
966 try {
967 mApp.enforceCallingOrSelfPermission(
968 android.Manifest.permission.ACCESS_FINE_LOCATION, null);
969 } catch (SecurityException e) {
970 // If we have ACCESS_FINE_LOCATION permission, skip the check for ACCESS_COARSE_LOCATION
971 // A failure should throw the SecurityException from ACCESS_COARSE_LOCATION since this
972 // is the weaker precondition
973 mApp.enforceCallingOrSelfPermission(
974 android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
975 }
976
Jake Hambye994d462014-02-03 13:10:13 -0800977 if (checkIfCallerIsSelfOrForegroundUser()) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700978 if (DBG_LOC) log("getAllCellInfo: is active user");
979 return mPhone.getAllCellInfo();
980 } else {
981 if (DBG_LOC) log("getAllCellInfo: suppress non-active user");
982 return null;
983 }
984 }
985
Sailesh Nepalbd76e4e2013-10-27 13:59:44 -0700986 @Override
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700987 public void setCellInfoListRate(int rateInMillis) {
988 mPhone.setCellInfoListRate(rateInMillis);
989 }
990
Sailesh Nepalbd76e4e2013-10-27 13:59:44 -0700991 @Override
Sailesh Nepalcc0375f2013-11-13 09:15:18 -0800992 public void newIncomingThirdPartyCall(ComponentName component, String callId,
993 String callerDisplayName) {
Sailesh Nepalbd76e4e2013-10-27 13:59:44 -0700994 // TODO(sail): Enforce that the component belongs to the calling package.
995 if (DBG) {
996 log("newIncomingThirdPartyCall: component: " + component + " callId: " + callId);
997 }
998 enforceCallPermission();
Sailesh Nepalcc0375f2013-11-13 09:15:18 -0800999 sendRequestAsync(CMD_NEW_INCOMING_THIRD_PARTY_CALL, new IncomingThirdPartyCallArgs(
1000 component, callId, callerDisplayName));
Sailesh Nepalbd76e4e2013-10-27 13:59:44 -07001001 }
1002
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001003 //
1004 // Internal helper methods.
1005 //
1006
Jake Hambye994d462014-02-03 13:10:13 -08001007 private static boolean checkIfCallerIsSelfOrForegroundUser() {
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001008 boolean ok;
1009
1010 boolean self = Binder.getCallingUid() == Process.myUid();
1011 if (!self) {
1012 // Get the caller's user id then clear the calling identity
1013 // which will be restored in the finally clause.
1014 int callingUser = UserHandle.getCallingUserId();
1015 long ident = Binder.clearCallingIdentity();
1016
1017 try {
1018 // With calling identity cleared the current user is the foreground user.
1019 int foregroundUser = ActivityManager.getCurrentUser();
1020 ok = (foregroundUser == callingUser);
1021 if (DBG_LOC) {
1022 log("checkIfCallerIsSelfOrForegoundUser: foregroundUser=" + foregroundUser
1023 + " callingUser=" + callingUser + " ok=" + ok);
1024 }
1025 } catch (Exception ex) {
1026 if (DBG_LOC) loge("checkIfCallerIsSelfOrForegoundUser: Exception ex=" + ex);
1027 ok = false;
1028 } finally {
1029 Binder.restoreCallingIdentity(ident);
1030 }
1031 } else {
1032 if (DBG_LOC) log("checkIfCallerIsSelfOrForegoundUser: is self");
1033 ok = true;
1034 }
1035 if (DBG_LOC) log("checkIfCallerIsSelfOrForegoundUser: ret=" + ok);
1036 return ok;
1037 }
1038
1039 /**
1040 * Make sure the caller has the READ_PHONE_STATE permission.
1041 *
1042 * @throws SecurityException if the caller does not have the required permission
1043 */
1044 private void enforceReadPermission() {
1045 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_STATE, null);
1046 }
1047
1048 /**
1049 * Make sure the caller has the MODIFY_PHONE_STATE permission.
1050 *
1051 * @throws SecurityException if the caller does not have the required permission
1052 */
1053 private void enforceModifyPermission() {
1054 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE, null);
1055 }
1056
1057 /**
1058 * Make sure the caller has the CALL_PHONE permission.
1059 *
1060 * @throws SecurityException if the caller does not have the required permission
1061 */
1062 private void enforceCallPermission() {
1063 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.CALL_PHONE, null);
1064 }
1065
Shishir Agrawal566b7612013-10-28 14:41:00 -07001066 /**
1067 * Make sure the caller has SIM_COMMUNICATION permission.
1068 *
1069 * @throws SecurityException if the caller does not have the required permission.
1070 */
1071 private void enforceSimCommunicationPermission() {
1072 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.SIM_COMMUNICATION, null);
1073 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001074
1075 private String createTelUrl(String number) {
1076 if (TextUtils.isEmpty(number)) {
1077 return null;
1078 }
1079
Jake Hambye994d462014-02-03 13:10:13 -08001080 return "tel:" + number;
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001081 }
1082
Ihab Awadf9e92732013-12-05 18:02:52 -08001083 private static void log(String msg) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001084 Log.d(LOG_TAG, "[PhoneIntfMgr] " + msg);
1085 }
1086
Ihab Awadf9e92732013-12-05 18:02:52 -08001087 private static void loge(String msg) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001088 Log.e(LOG_TAG, "[PhoneIntfMgr] " + msg);
1089 }
1090
1091 public int getActivePhoneType() {
1092 return mPhone.getPhoneType();
1093 }
1094
1095 /**
1096 * Returns the CDMA ERI icon index to display
1097 */
1098 public int getCdmaEriIconIndex() {
1099 return mPhone.getCdmaEriIconIndex();
1100 }
1101
1102 /**
1103 * Returns the CDMA ERI icon mode,
1104 * 0 - ON
1105 * 1 - FLASHING
1106 */
1107 public int getCdmaEriIconMode() {
1108 return mPhone.getCdmaEriIconMode();
1109 }
1110
1111 /**
1112 * Returns the CDMA ERI text,
1113 */
1114 public String getCdmaEriText() {
1115 return mPhone.getCdmaEriText();
1116 }
1117
1118 /**
1119 * Returns true if CDMA provisioning needs to run.
1120 */
1121 public boolean needsOtaServiceProvisioning() {
1122 return mPhone.needsOtaServiceProvisioning();
1123 }
1124
1125 /**
1126 * Returns the unread count of voicemails
1127 */
1128 public int getVoiceMessageCount() {
1129 return mPhone.getVoiceMessageCount();
1130 }
1131
1132 /**
1133 * Returns the data network type
1134 *
1135 * @Deprecated to be removed Q3 2013 use {@link #getDataNetworkType}.
1136 */
1137 @Override
1138 public int getNetworkType() {
1139 return mPhone.getServiceState().getDataNetworkType();
1140 }
1141
1142 /**
1143 * Returns the data network type
1144 */
1145 @Override
1146 public int getDataNetworkType() {
1147 return mPhone.getServiceState().getDataNetworkType();
1148 }
1149
1150 /**
1151 * Returns the data network type
1152 */
1153 @Override
1154 public int getVoiceNetworkType() {
1155 return mPhone.getServiceState().getVoiceNetworkType();
1156 }
1157
1158 /**
1159 * @return true if a ICC card is present
1160 */
1161 public boolean hasIccCard() {
1162 return mPhone.getIccCard().hasIccCard();
1163 }
1164
1165 /**
1166 * Return if the current radio is LTE on CDMA. This
1167 * is a tri-state return value as for a period of time
1168 * the mode may be unknown.
1169 *
1170 * @return {@link Phone#LTE_ON_CDMA_UNKNOWN}, {@link Phone#LTE_ON_CDMA_FALSE}
Jake Hambye994d462014-02-03 13:10:13 -08001171 * or {@link Phone#LTE_ON_CDMA_TRUE}
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001172 */
1173 public int getLteOnCdmaMode() {
1174 return mPhone.getLteOnCdmaMode();
1175 }
Ihab Awadf2177b72013-11-25 13:33:23 -08001176
1177 /**
1178 * @see android.telephony.TelephonyManager.WifiCallingChoices
1179 */
1180 public int getWhenToMakeWifiCalls() {
Sailesh Nepald1e68152013-12-12 19:08:02 -08001181 return Settings.System.getInt(mPhone.getContext().getContentResolver(),
1182 Settings.System.WHEN_TO_MAKE_WIFI_CALLS, getWhenToMakeWifiCallsDefaultPreference());
Ihab Awadf2177b72013-11-25 13:33:23 -08001183 }
1184
1185 /**
1186 * @see android.telephony.TelephonyManager.WifiCallingChoices
1187 */
1188 public void setWhenToMakeWifiCalls(int preference) {
Sailesh Nepald1e68152013-12-12 19:08:02 -08001189 if (DBG) log("setWhenToMakeWifiCallsStr, storing setting = " + preference);
1190 Settings.System.putInt(mPhone.getContext().getContentResolver(),
1191 Settings.System.WHEN_TO_MAKE_WIFI_CALLS, preference);
Ihab Awadf9e92732013-12-05 18:02:52 -08001192 }
1193
Sailesh Nepald1e68152013-12-12 19:08:02 -08001194 private static int getWhenToMakeWifiCallsDefaultPreference() {
1195 // TODO(sail): Use a build property to choose this value.
Evan Charlton9829e882013-12-19 15:30:38 -08001196 return TelephonyManager.WifiCallingChoices.ALWAYS_USE;
Ihab Awadf2177b72013-11-25 13:33:23 -08001197 }
Shishir Agrawal69f68122013-12-16 17:25:49 -08001198
Shishir Agrawal566b7612013-10-28 14:41:00 -07001199 @Override
1200 public int iccOpenLogicalChannel(String AID) {
1201 enforceSimCommunicationPermission();
1202
1203 if (DBG) log("iccOpenLogicalChannel: " + AID);
1204 Integer channel = (Integer)sendRequest(CMD_OPEN_CHANNEL, AID);
1205 if (DBG) log("iccOpenLogicalChannel: " + channel);
Jake Hambye994d462014-02-03 13:10:13 -08001206 return channel;
Shishir Agrawal566b7612013-10-28 14:41:00 -07001207 }
1208
1209 @Override
1210 public boolean iccCloseLogicalChannel(int channel) {
1211 enforceSimCommunicationPermission();
1212
1213 if (DBG) log("iccCloseLogicalChannel: " + channel);
1214 if (channel < 0) {
1215 return false;
1216 }
Jake Hambye994d462014-02-03 13:10:13 -08001217 Boolean success = (Boolean)sendRequest(CMD_CLOSE_CHANNEL, channel);
Shishir Agrawal566b7612013-10-28 14:41:00 -07001218 if (DBG) log("iccCloseLogicalChannel: " + success);
1219 return success;
1220 }
1221
1222 @Override
1223 public String iccTransmitApduLogicalChannel(int channel, int cla,
1224 int command, int p1, int p2, int p3, String data) {
1225 enforceSimCommunicationPermission();
1226
1227 if (DBG) {
1228 log("iccTransmitApduLogicalChannel: chnl=" + channel + " cla=" + cla +
1229 " cmd=" + command + " p1=" + p1 + " p2=" + p2 + " p3=" + p3 +
1230 " data=" + data);
1231 }
1232
1233 if (channel < 0) {
1234 return "";
1235 }
1236
1237 IccIoResult response = (IccIoResult)sendRequest(CMD_TRANSMIT_APDU,
1238 new IccAPDUArgument(channel, cla, command, p1, p2, p3, data));
1239 if (DBG) log("iccTransmitApduLogicalChannel: " + response);
1240
1241 // If the payload is null, there was an error. Indicate that by returning
1242 // an empty string.
1243 if (response.payload == null) {
1244 return "";
1245 }
1246
1247 // Append the returned status code to the end of the response payload.
1248 String s = Integer.toHexString(
1249 (response.sw1 << 8) + response.sw2 + 0x10000).substring(1);
1250 s = IccUtils.bytesToHexString(response.payload) + s;
1251 return s;
1252 }
Jake Hambye994d462014-02-03 13:10:13 -08001253
Derek Tan4d5e5c12014-02-04 11:54:58 -08001254 @Override
1255 public String sendEnvelope(String content) {
1256 enforceSimCommunicationPermission();
1257
1258 String response = (String)sendRequest(CMD_SEND_ENVELOPE, content);
1259
1260 return response;
1261 }
1262
Jake Hambye994d462014-02-03 13:10:13 -08001263 /**
1264 * Read one of the NV items defined in {@link com.android.internal.telephony.RadioNVItems}
1265 * and {@code ril_nv_items.h}. Used for device configuration by some CDMA operators.
1266 *
1267 * @param itemID the ID of the item to read
1268 * @return the NV item as a String, or null on error.
1269 */
1270 @Override
1271 public String nvReadItem(int itemID) {
1272 enforceModifyPermission();
1273 if (DBG) log("nvReadItem: item " + itemID);
1274 String value = (String) sendRequest(CMD_NV_READ_ITEM, itemID);
1275 if (DBG) log("nvReadItem: item " + itemID + " is \"" + value + '"');
1276 return value;
1277 }
1278
1279 /**
1280 * Write one of the NV items defined in {@link com.android.internal.telephony.RadioNVItems}
1281 * and {@code ril_nv_items.h}. Used for device configuration by some CDMA operators.
1282 *
1283 * @param itemID the ID of the item to read
1284 * @param itemValue the value to write, as a String
1285 * @return true on success; false on any failure
1286 */
1287 @Override
1288 public boolean nvWriteItem(int itemID, String itemValue) {
1289 enforceModifyPermission();
1290 if (DBG) log("nvWriteItem: item " + itemID + " value \"" + itemValue + '"');
1291 Boolean success = (Boolean) sendRequest(CMD_NV_WRITE_ITEM,
1292 new Pair<Integer, String>(itemID, itemValue));
1293 if (DBG) log("nvWriteItem: item " + itemID + ' ' + (success ? "ok" : "fail"));
1294 return success;
1295 }
1296
1297 /**
1298 * Update the CDMA Preferred Roaming List (PRL) in the radio NV storage.
1299 * Used for device configuration by some CDMA operators.
1300 *
1301 * @param preferredRoamingList byte array containing the new PRL
1302 * @return true on success; false on any failure
1303 */
1304 @Override
1305 public boolean nvWriteCdmaPrl(byte[] preferredRoamingList) {
1306 enforceModifyPermission();
1307 if (DBG) log("nvWriteCdmaPrl: value: " + HexDump.toHexString(preferredRoamingList));
1308 Boolean success = (Boolean) sendRequest(CMD_NV_WRITE_CDMA_PRL, preferredRoamingList);
1309 if (DBG) log("nvWriteCdmaPrl: " + (success ? "ok" : "fail"));
1310 return success;
1311 }
1312
1313 /**
1314 * Perform the specified type of NV config reset.
1315 * Used for device configuration by some CDMA operators.
1316 *
1317 * @param resetType the type of reset to perform (1 == factory reset; 2 == NV-only reset)
1318 * @return true on success; false on any failure
1319 */
1320 @Override
1321 public boolean nvResetConfig(int resetType) {
1322 enforceModifyPermission();
1323 if (DBG) log("nvResetConfig: type " + resetType);
1324 Boolean success = (Boolean) sendRequest(CMD_NV_RESET_CONFIG, resetType);
1325 if (DBG) log("nvResetConfig: type " + resetType + ' ' + (success ? "ok" : "fail"));
1326 return success;
1327 }
1328
1329 /**
1330 * Change the radio to the specified mode.
1331 * Used for device configuration by some operators.
1332 *
1333 * @param radioMode is 0 for offline mode, 1 for online mode, 2 for low-power mode,
1334 * or 3 to reset the radio.
1335 * @return true on success; false on any failure
1336 */
1337 @Override
1338 public boolean setRadioMode(int radioMode) {
1339 enforceModifyPermission();
1340 if (DBG) log("setRadioMode: mode " + radioMode);
1341 Boolean success = (Boolean) sendRequest(CMD_SET_RADIO_MODE, radioMode);
1342 if (DBG) log("setRadioMode: mode " + radioMode + ' ' + (success ? "ok" : "fail"));
1343 return success;
1344 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001345}