blob: 2d0597d7dd3341d705e326db8a997cfa4102ca5d [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;
Shishir Agrawal566b7612013-10-28 14:41:00 -070094
Santos Cordon7d4ddf62013-07-10 11:58:08 -070095
96 /** The singleton instance. */
97 private static PhoneInterfaceManager sInstance;
98
99 PhoneGlobals mApp;
100 Phone mPhone;
101 CallManager mCM;
102 AppOpsManager mAppOps;
103 MainThreadHandler mMainThreadHandler;
Santos Cordon406c0342013-08-28 00:07:47 -0700104 CallHandlerServiceProxy mCallHandlerService;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700105
106 /**
Shishir Agrawal566b7612013-10-28 14:41:00 -0700107 * A request object to use for transmitting data to an ICC.
108 */
109 private static final class IccAPDUArgument {
110 public int channel, cla, command, p1, p2, p3;
111 public String data;
112
113 public IccAPDUArgument(int channel, int cla, int command,
114 int p1, int p2, int p3, String data) {
115 this.channel = channel;
116 this.cla = cla;
117 this.command = command;
118 this.p1 = p1;
119 this.p2 = p2;
120 this.p3 = p3;
121 this.data = data;
122 }
123 }
124
125 /**
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700126 * A request object for use with {@link MainThreadHandler}. Requesters should wait() on the
127 * request after sending. The main thread will notify the request when it is complete.
128 */
129 private static final class MainThreadRequest {
130 /** The argument to use for the request */
131 public Object argument;
132 /** The result of the request that is run on the main thread */
133 public Object result;
134
135 public MainThreadRequest(Object argument) {
136 this.argument = argument;
137 }
138 }
139
Sailesh Nepalcc0375f2013-11-13 09:15:18 -0800140 private static final class IncomingThirdPartyCallArgs {
141 public final ComponentName component;
142 public final String callId;
143 public final String callerDisplayName;
144
145 public IncomingThirdPartyCallArgs(ComponentName component, String callId,
146 String callerDisplayName) {
147 this.component = component;
148 this.callId = callId;
149 this.callerDisplayName = callerDisplayName;
150 }
151 }
152
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700153 /**
154 * A handler that processes messages on the main thread in the phone process. Since many
155 * of the Phone calls are not thread safe this is needed to shuttle the requests from the
156 * inbound binder threads to the main thread in the phone process. The Binder thread
157 * may provide a {@link MainThreadRequest} object in the msg.obj field that they are waiting
158 * on, which will be notified when the operation completes and will contain the result of the
159 * request.
160 *
161 * <p>If a MainThreadRequest object is provided in the msg.obj field,
162 * note that request.result must be set to something non-null for the calling thread to
163 * unblock.
164 */
165 private final class MainThreadHandler extends Handler {
166 @Override
167 public void handleMessage(Message msg) {
168 MainThreadRequest request;
169 Message onCompleted;
170 AsyncResult ar;
171
172 switch (msg.what) {
173 case CMD_HANDLE_PIN_MMI:
174 request = (MainThreadRequest) msg.obj;
Jake Hambye994d462014-02-03 13:10:13 -0800175 request.result = mPhone.handlePinMmi((String) request.argument);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700176 // Wake up the requesting thread
177 synchronized (request) {
178 request.notifyAll();
179 }
180 break;
181
182 case CMD_HANDLE_NEIGHBORING_CELL:
183 request = (MainThreadRequest) msg.obj;
184 onCompleted = obtainMessage(EVENT_NEIGHBORING_CELL_DONE,
185 request);
186 mPhone.getNeighboringCids(onCompleted);
187 break;
188
189 case EVENT_NEIGHBORING_CELL_DONE:
190 ar = (AsyncResult) msg.obj;
191 request = (MainThreadRequest) ar.userObj;
192 if (ar.exception == null && ar.result != null) {
193 request.result = ar.result;
194 } else {
195 // create an empty list to notify the waiting thread
Jake Hambye994d462014-02-03 13:10:13 -0800196 request.result = new ArrayList<NeighboringCellInfo>(0);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700197 }
198 // Wake up the requesting thread
199 synchronized (request) {
200 request.notifyAll();
201 }
202 break;
203
204 case CMD_ANSWER_RINGING_CALL:
205 answerRingingCallInternal();
206 break;
207
208 case CMD_SILENCE_RINGER:
209 silenceRingerInternal();
210 break;
211
212 case CMD_END_CALL:
213 request = (MainThreadRequest) msg.obj;
Jake Hambye994d462014-02-03 13:10:13 -0800214 boolean hungUp;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700215 int phoneType = mPhone.getPhoneType();
216 if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
217 // CDMA: If the user presses the Power button we treat it as
218 // ending the complete call session
219 hungUp = PhoneUtils.hangupRingingAndActive(mPhone);
220 } else if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
221 // GSM: End the call as per the Phone state
222 hungUp = PhoneUtils.hangup(mCM);
223 } else {
224 throw new IllegalStateException("Unexpected phone type: " + phoneType);
225 }
226 if (DBG) log("CMD_END_CALL: " + (hungUp ? "hung up!" : "no call to hang up"));
227 request.result = hungUp;
228 // Wake up the requesting thread
229 synchronized (request) {
230 request.notifyAll();
231 }
232 break;
233
Sailesh Nepalbd76e4e2013-10-27 13:59:44 -0700234 case CMD_NEW_INCOMING_THIRD_PARTY_CALL: {
235 request = (MainThreadRequest) msg.obj;
Sailesh Nepalcc0375f2013-11-13 09:15:18 -0800236 IncomingThirdPartyCallArgs args = (IncomingThirdPartyCallArgs) request.argument;
Sailesh Nepalbfb68322013-11-07 14:07:41 -0800237 ThirdPartyPhone thirdPartyPhone = (ThirdPartyPhone)
Sailesh Nepalcc0375f2013-11-13 09:15:18 -0800238 PhoneUtils.getThirdPartyPhoneFromComponent(mCM, args.component);
239 thirdPartyPhone.takeIncomingCall(args.callId, args.callerDisplayName);
Sailesh Nepalbd76e4e2013-10-27 13:59:44 -0700240 break;
241 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700242
Shishir Agrawal566b7612013-10-28 14:41:00 -0700243 case CMD_TRANSMIT_APDU:
244 request = (MainThreadRequest) msg.obj;
245 IccAPDUArgument argument = (IccAPDUArgument) request.argument;
246 onCompleted = obtainMessage(EVENT_TRANSMIT_APDU_DONE, request);
247 UiccController.getInstance().getUiccCard().iccTransmitApduLogicalChannel(
248 argument.channel, argument.cla, argument.command,
249 argument.p1, argument.p2, argument.p3, argument.data,
250 onCompleted);
251 break;
252
253 case EVENT_TRANSMIT_APDU_DONE:
254 ar = (AsyncResult) msg.obj;
255 request = (MainThreadRequest) ar.userObj;
256 if (ar.exception == null && ar.result != null) {
257 request.result = ar.result;
258 } else {
259 request.result = new IccIoResult(0x6F, 0, (byte[])null);
260 if (ar.result == null) {
261 loge("iccTransmitApduLogicalChannel: Empty response");
Jake Hambye994d462014-02-03 13:10:13 -0800262 } else if (ar.exception instanceof CommandException) {
Shishir Agrawal566b7612013-10-28 14:41:00 -0700263 loge("iccTransmitApduLogicalChannel: CommandException: " +
Jake Hambye994d462014-02-03 13:10:13 -0800264 ar.exception);
Shishir Agrawal566b7612013-10-28 14:41:00 -0700265 } else {
266 loge("iccTransmitApduLogicalChannel: Unknown exception");
267 }
268 }
269 synchronized (request) {
270 request.notifyAll();
271 }
272 break;
273
274 case CMD_OPEN_CHANNEL:
275 request = (MainThreadRequest) msg.obj;
276 onCompleted = obtainMessage(EVENT_OPEN_CHANNEL_DONE, request);
277 UiccController.getInstance().getUiccCard().iccOpenLogicalChannel(
278 (String)request.argument, onCompleted);
279 break;
280
281 case EVENT_OPEN_CHANNEL_DONE:
282 ar = (AsyncResult) msg.obj;
283 request = (MainThreadRequest) ar.userObj;
284 if (ar.exception == null && ar.result != null) {
Jake Hambye994d462014-02-03 13:10:13 -0800285 request.result = ((int[]) ar.result)[0];
Shishir Agrawal566b7612013-10-28 14:41:00 -0700286 } else {
Jake Hambye994d462014-02-03 13:10:13 -0800287 request.result = -1;
Shishir Agrawal566b7612013-10-28 14:41:00 -0700288 if (ar.result == null) {
289 loge("iccOpenLogicalChannel: Empty response");
Jake Hambye994d462014-02-03 13:10:13 -0800290 } else if (ar.exception instanceof CommandException) {
Shishir Agrawal566b7612013-10-28 14:41:00 -0700291 loge("iccOpenLogicalChannel: CommandException: " +
Jake Hambye994d462014-02-03 13:10:13 -0800292 ar.exception);
Shishir Agrawal566b7612013-10-28 14:41:00 -0700293 } else {
294 loge("iccOpenLogicalChannel: Unknown exception");
295 }
296 }
297 synchronized (request) {
298 request.notifyAll();
299 }
300 break;
301
302 case CMD_CLOSE_CHANNEL:
303 request = (MainThreadRequest) msg.obj;
304 onCompleted = obtainMessage(EVENT_CLOSE_CHANNEL_DONE,
305 request);
306 UiccController.getInstance().getUiccCard().iccCloseLogicalChannel(
Jake Hambye994d462014-02-03 13:10:13 -0800307 (Integer) request.argument,
Shishir Agrawal566b7612013-10-28 14:41:00 -0700308 onCompleted);
309 break;
310
311 case EVENT_CLOSE_CHANNEL_DONE:
Jake Hambye994d462014-02-03 13:10:13 -0800312 handleNullReturnEvent(msg, "iccCloseLogicalChannel");
313 break;
314
315 case CMD_NV_READ_ITEM:
316 request = (MainThreadRequest) msg.obj;
317 onCompleted = obtainMessage(EVENT_NV_READ_ITEM_DONE, request);
318 mPhone.nvReadItem((Integer) request.argument, onCompleted);
319 break;
320
321 case EVENT_NV_READ_ITEM_DONE:
Shishir Agrawal566b7612013-10-28 14:41:00 -0700322 ar = (AsyncResult) msg.obj;
323 request = (MainThreadRequest) ar.userObj;
Jake Hambye994d462014-02-03 13:10:13 -0800324 if (ar.exception == null && ar.result != null) {
325 request.result = ar.result; // String
Shishir Agrawal566b7612013-10-28 14:41:00 -0700326 } else {
Jake Hambye994d462014-02-03 13:10:13 -0800327 request.result = "";
328 if (ar.result == null) {
329 loge("nvReadItem: Empty response");
330 } else if (ar.exception instanceof CommandException) {
331 loge("nvReadItem: CommandException: " +
332 ar.exception);
Shishir Agrawal566b7612013-10-28 14:41:00 -0700333 } else {
Jake Hambye994d462014-02-03 13:10:13 -0800334 loge("nvReadItem: Unknown exception");
Shishir Agrawal566b7612013-10-28 14:41:00 -0700335 }
336 }
337 synchronized (request) {
338 request.notifyAll();
339 }
340 break;
341
Jake Hambye994d462014-02-03 13:10:13 -0800342 case CMD_NV_WRITE_ITEM:
343 request = (MainThreadRequest) msg.obj;
344 onCompleted = obtainMessage(EVENT_NV_WRITE_ITEM_DONE, request);
345 Pair<Integer, String> idValue = (Pair<Integer, String>) request.argument;
346 mPhone.nvWriteItem(idValue.first, idValue.second, onCompleted);
347 break;
348
349 case EVENT_NV_WRITE_ITEM_DONE:
350 handleNullReturnEvent(msg, "nvWriteItem");
351 break;
352
353 case CMD_NV_WRITE_CDMA_PRL:
354 request = (MainThreadRequest) msg.obj;
355 onCompleted = obtainMessage(EVENT_NV_WRITE_CDMA_PRL_DONE, request);
356 mPhone.nvWriteCdmaPrl((byte[]) request.argument, onCompleted);
357 break;
358
359 case EVENT_NV_WRITE_CDMA_PRL_DONE:
360 handleNullReturnEvent(msg, "nvWriteCdmaPrl");
361 break;
362
363 case CMD_NV_RESET_CONFIG:
364 request = (MainThreadRequest) msg.obj;
365 onCompleted = obtainMessage(EVENT_NV_RESET_CONFIG_DONE, request);
366 mPhone.nvResetConfig((Integer) request.argument, onCompleted);
367 break;
368
369 case EVENT_NV_RESET_CONFIG_DONE:
370 handleNullReturnEvent(msg, "nvResetConfig");
371 break;
372
373 case CMD_SET_RADIO_MODE:
374 request = (MainThreadRequest) msg.obj;
375 onCompleted = obtainMessage(EVENT_SET_RADIO_MODE_DONE, request);
376 mPhone.setRadioMode((Integer) request.argument, onCompleted);
377 break;
378
379 case EVENT_SET_RADIO_MODE_DONE:
380 handleNullReturnEvent(msg, "setRadioMode");
381 break;
382
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700383 default:
384 Log.w(LOG_TAG, "MainThreadHandler: unexpected message code: " + msg.what);
385 break;
386 }
387 }
Jake Hambye994d462014-02-03 13:10:13 -0800388
389 private void handleNullReturnEvent(Message msg, String command) {
390 AsyncResult ar = (AsyncResult) msg.obj;
391 MainThreadRequest request = (MainThreadRequest) ar.userObj;
392 if (ar.exception == null) {
393 request.result = true;
394 } else {
395 request.result = false;
396 if (ar.exception instanceof CommandException) {
397 loge(command + ": CommandException: " + ar.exception);
398 } else {
399 loge(command + ": Unknown exception");
400 }
401 }
402 synchronized (request) {
403 request.notifyAll();
404 }
405 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700406 }
407
408 /**
409 * Posts the specified command to be executed on the main thread,
410 * waits for the request to complete, and returns the result.
411 * @see #sendRequestAsync
412 */
413 private Object sendRequest(int command, Object argument) {
414 if (Looper.myLooper() == mMainThreadHandler.getLooper()) {
415 throw new RuntimeException("This method will deadlock if called from the main thread.");
416 }
417
418 MainThreadRequest request = new MainThreadRequest(argument);
419 Message msg = mMainThreadHandler.obtainMessage(command, request);
420 msg.sendToTarget();
421
422 // Wait for the request to complete
423 synchronized (request) {
424 while (request.result == null) {
425 try {
426 request.wait();
427 } catch (InterruptedException e) {
428 // Do nothing, go back and wait until the request is complete
429 }
430 }
431 }
432 return request.result;
433 }
434
435 /**
436 * Asynchronous ("fire and forget") version of sendRequest():
437 * Posts the specified command to be executed on the main thread, and
438 * returns immediately.
439 * @see #sendRequest
440 */
441 private void sendRequestAsync(int command) {
442 mMainThreadHandler.sendEmptyMessage(command);
443 }
444
445 /**
Sailesh Nepalbd76e4e2013-10-27 13:59:44 -0700446 * Same as {@link #sendRequestAsync(int)} except it takes an argument.
447 * @see {@link #sendRequest(int,Object)}
448 */
449 private void sendRequestAsync(int command, Object argument) {
450 MainThreadRequest request = new MainThreadRequest(argument);
451 Message msg = mMainThreadHandler.obtainMessage(command, request);
452 msg.sendToTarget();
453 }
454
455 /**
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700456 * Initialize the singleton PhoneInterfaceManager instance.
457 * This is only done once, at startup, from PhoneApp.onCreate().
458 */
Santos Cordon406c0342013-08-28 00:07:47 -0700459 /* package */ static PhoneInterfaceManager init(PhoneGlobals app, Phone phone,
460 CallHandlerServiceProxy callHandlerService) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700461 synchronized (PhoneInterfaceManager.class) {
462 if (sInstance == null) {
Santos Cordon406c0342013-08-28 00:07:47 -0700463 sInstance = new PhoneInterfaceManager(app, phone, callHandlerService);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700464 } else {
465 Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance);
466 }
467 return sInstance;
468 }
469 }
470
471 /** Private constructor; @see init() */
Santos Cordon406c0342013-08-28 00:07:47 -0700472 private PhoneInterfaceManager(PhoneGlobals app, Phone phone,
473 CallHandlerServiceProxy callHandlerService) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700474 mApp = app;
475 mPhone = phone;
476 mCM = PhoneGlobals.getInstance().mCM;
477 mAppOps = (AppOpsManager)app.getSystemService(Context.APP_OPS_SERVICE);
478 mMainThreadHandler = new MainThreadHandler();
Santos Cordon406c0342013-08-28 00:07:47 -0700479 mCallHandlerService = callHandlerService;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700480 publish();
481 }
482
483 private void publish() {
484 if (DBG) log("publish: " + this);
485
486 ServiceManager.addService("phone", this);
487 }
488
489 //
490 // Implementation of the ITelephony interface.
491 //
492
493 public void dial(String number) {
494 if (DBG) log("dial: " + number);
495 // No permission check needed here: This is just a wrapper around the
496 // ACTION_DIAL intent, which is available to any app since it puts up
497 // the UI before it does anything.
498
499 String url = createTelUrl(number);
500 if (url == null) {
501 return;
502 }
503
504 // PENDING: should we just silently fail if phone is offhook or ringing?
505 PhoneConstants.State state = mCM.getState();
506 if (state != PhoneConstants.State.OFFHOOK && state != PhoneConstants.State.RINGING) {
507 Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse(url));
508 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
509 mApp.startActivity(intent);
510 }
511 }
512
513 public void call(String callingPackage, String number) {
514 if (DBG) log("call: " + number);
515
516 // This is just a wrapper around the ACTION_CALL intent, but we still
517 // need to do a permission check since we're calling startActivity()
518 // from the context of the phone app.
519 enforceCallPermission();
520
521 if (mAppOps.noteOp(AppOpsManager.OP_CALL_PHONE, Binder.getCallingUid(), callingPackage)
522 != AppOpsManager.MODE_ALLOWED) {
523 return;
524 }
525
526 String url = createTelUrl(number);
527 if (url == null) {
528 return;
529 }
530
531 Intent intent = new Intent(Intent.ACTION_CALL, Uri.parse(url));
532 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
533 mApp.startActivity(intent);
534 }
535
536 private boolean showCallScreenInternal(boolean specifyInitialDialpadState,
Makoto Onukibcf20992013-09-12 17:59:30 -0700537 boolean showDialpad) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700538 if (!PhoneGlobals.sVoiceCapable) {
539 // Never allow the InCallScreen to appear on data-only devices.
540 return false;
541 }
542 if (isIdle()) {
543 return false;
544 }
545 // If the phone isn't idle then go to the in-call screen
546 long callingId = Binder.clearCallingIdentity();
Santos Cordon406c0342013-08-28 00:07:47 -0700547
Makoto Onukibcf20992013-09-12 17:59:30 -0700548 mCallHandlerService.bringToForeground(showDialpad);
Santos Cordon406c0342013-08-28 00:07:47 -0700549
550 Binder.restoreCallingIdentity(callingId);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700551 return true;
552 }
553
554 // Show the in-call screen without specifying the initial dialpad state.
555 public boolean showCallScreen() {
556 return showCallScreenInternal(false, false);
557 }
558
559 // The variation of showCallScreen() that specifies the initial dialpad state.
560 // (Ideally this would be called showCallScreen() too, just with a different
561 // signature, but AIDL doesn't allow that.)
562 public boolean showCallScreenWithDialpad(boolean showDialpad) {
563 return showCallScreenInternal(true, showDialpad);
564 }
565
566 /**
567 * End a call based on call state
568 * @return true is a call was ended
569 */
570 public boolean endCall() {
571 enforceCallPermission();
572 return (Boolean) sendRequest(CMD_END_CALL, null);
573 }
574
575 public void answerRingingCall() {
576 if (DBG) log("answerRingingCall...");
577 // TODO: there should eventually be a separate "ANSWER_PHONE" permission,
578 // but that can probably wait till the big TelephonyManager API overhaul.
579 // For now, protect this call with the MODIFY_PHONE_STATE permission.
580 enforceModifyPermission();
581 sendRequestAsync(CMD_ANSWER_RINGING_CALL);
582 }
583
584 /**
585 * Make the actual telephony calls to implement answerRingingCall().
586 * This should only be called from the main thread of the Phone app.
587 * @see #answerRingingCall
588 *
589 * TODO: it would be nice to return true if we answered the call, or
590 * false if there wasn't actually a ringing incoming call, or some
591 * other error occurred. (In other words, pass back the return value
592 * from PhoneUtils.answerCall() or PhoneUtils.answerAndEndActive().)
593 * But that would require calling this method via sendRequest() rather
594 * than sendRequestAsync(), and right now we don't actually *need* that
595 * return value, so let's just return void for now.
596 */
597 private void answerRingingCallInternal() {
598 final boolean hasRingingCall = !mPhone.getRingingCall().isIdle();
599 if (hasRingingCall) {
600 final boolean hasActiveCall = !mPhone.getForegroundCall().isIdle();
601 final boolean hasHoldingCall = !mPhone.getBackgroundCall().isIdle();
602 if (hasActiveCall && hasHoldingCall) {
603 // Both lines are in use!
604 // TODO: provide a flag to let the caller specify what
605 // policy to use if both lines are in use. (The current
606 // behavior is hardwired to "answer incoming, end ongoing",
607 // which is how the CALL button is specced to behave.)
608 PhoneUtils.answerAndEndActive(mCM, mCM.getFirstActiveRingingCall());
609 return;
610 } else {
611 // answerCall() will automatically hold the current active
612 // call, if there is one.
613 PhoneUtils.answerCall(mCM.getFirstActiveRingingCall());
614 return;
615 }
616 } else {
617 // No call was ringing.
618 return;
619 }
620 }
621
622 public void silenceRinger() {
623 if (DBG) log("silenceRinger...");
624 // TODO: find a more appropriate permission to check here.
625 // (That can probably wait till the big TelephonyManager API overhaul.
626 // For now, protect this call with the MODIFY_PHONE_STATE permission.)
627 enforceModifyPermission();
628 sendRequestAsync(CMD_SILENCE_RINGER);
629 }
630
631 /**
632 * Internal implemenation of silenceRinger().
633 * This should only be called from the main thread of the Phone app.
634 * @see #silenceRinger
635 */
636 private void silenceRingerInternal() {
637 if ((mCM.getState() == PhoneConstants.State.RINGING)
638 && mApp.notifier.isRinging()) {
639 // Ringer is actually playing, so silence it.
640 if (DBG) log("silenceRingerInternal: silencing...");
641 mApp.notifier.silenceRinger();
642 }
643 }
644
645 public boolean isOffhook() {
646 return (mCM.getState() == PhoneConstants.State.OFFHOOK);
647 }
648
649 public boolean isRinging() {
650 return (mCM.getState() == PhoneConstants.State.RINGING);
651 }
652
653 public boolean isIdle() {
654 return (mCM.getState() == PhoneConstants.State.IDLE);
655 }
656
657 public boolean isSimPinEnabled() {
658 enforceReadPermission();
659 return (PhoneGlobals.getInstance().isSimPinEnabled());
660 }
661
662 public boolean supplyPin(String pin) {
Wink Saville9de0f752013-10-22 19:04:03 -0700663 int [] resultArray = supplyPinReportResult(pin);
664 return (resultArray[0] == PhoneConstants.PIN_RESULT_SUCCESS) ? true : false;
665 }
666
667 public boolean supplyPuk(String puk, String pin) {
668 int [] resultArray = supplyPukReportResult(puk, pin);
669 return (resultArray[0] == PhoneConstants.PIN_RESULT_SUCCESS) ? true : false;
670 }
671
672 /** {@hide} */
673 public int[] supplyPinReportResult(String pin) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700674 enforceModifyPermission();
675 final UnlockSim checkSimPin = new UnlockSim(mPhone.getIccCard());
676 checkSimPin.start();
677 return checkSimPin.unlockSim(null, pin);
678 }
679
Wink Saville9de0f752013-10-22 19:04:03 -0700680 /** {@hide} */
681 public int[] supplyPukReportResult(String puk, String pin) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700682 enforceModifyPermission();
683 final UnlockSim checkSimPuk = new UnlockSim(mPhone.getIccCard());
684 checkSimPuk.start();
685 return checkSimPuk.unlockSim(puk, pin);
686 }
687
688 /**
Wink Saville9de0f752013-10-22 19:04:03 -0700689 * Helper thread to turn async call to SimCard#supplyPin into
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700690 * a synchronous one.
691 */
692 private static class UnlockSim extends Thread {
693
694 private final IccCard mSimCard;
695
696 private boolean mDone = false;
Wink Saville9de0f752013-10-22 19:04:03 -0700697 private int mResult = PhoneConstants.PIN_GENERAL_FAILURE;
698 private int mRetryCount = -1;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700699
700 // For replies from SimCard interface
701 private Handler mHandler;
702
703 // For async handler to identify request type
704 private static final int SUPPLY_PIN_COMPLETE = 100;
705
706 public UnlockSim(IccCard simCard) {
707 mSimCard = simCard;
708 }
709
710 @Override
711 public void run() {
712 Looper.prepare();
713 synchronized (UnlockSim.this) {
714 mHandler = new Handler() {
715 @Override
716 public void handleMessage(Message msg) {
717 AsyncResult ar = (AsyncResult) msg.obj;
718 switch (msg.what) {
719 case SUPPLY_PIN_COMPLETE:
720 Log.d(LOG_TAG, "SUPPLY_PIN_COMPLETE");
721 synchronized (UnlockSim.this) {
Wink Saville9de0f752013-10-22 19:04:03 -0700722 mRetryCount = msg.arg1;
723 if (ar.exception != null) {
724 if (ar.exception instanceof CommandException &&
725 ((CommandException)(ar.exception)).getCommandError()
726 == CommandException.Error.PASSWORD_INCORRECT) {
727 mResult = PhoneConstants.PIN_PASSWORD_INCORRECT;
728 } else {
729 mResult = PhoneConstants.PIN_GENERAL_FAILURE;
730 }
731 } else {
732 mResult = PhoneConstants.PIN_RESULT_SUCCESS;
733 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700734 mDone = true;
735 UnlockSim.this.notifyAll();
736 }
737 break;
738 }
739 }
740 };
741 UnlockSim.this.notifyAll();
742 }
743 Looper.loop();
744 }
745
746 /*
747 * Use PIN or PUK to unlock SIM card
748 *
749 * If PUK is null, unlock SIM card with PIN
750 *
751 * If PUK is not null, unlock SIM card with PUK and set PIN code
752 */
Wink Saville9de0f752013-10-22 19:04:03 -0700753 synchronized int[] unlockSim(String puk, String pin) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700754
755 while (mHandler == null) {
756 try {
757 wait();
758 } catch (InterruptedException e) {
759 Thread.currentThread().interrupt();
760 }
761 }
762 Message callback = Message.obtain(mHandler, SUPPLY_PIN_COMPLETE);
763
764 if (puk == null) {
765 mSimCard.supplyPin(pin, callback);
766 } else {
767 mSimCard.supplyPuk(puk, pin, callback);
768 }
769
770 while (!mDone) {
771 try {
772 Log.d(LOG_TAG, "wait for done");
773 wait();
774 } catch (InterruptedException e) {
775 // Restore the interrupted status
776 Thread.currentThread().interrupt();
777 }
778 }
779 Log.d(LOG_TAG, "done");
Wink Saville9de0f752013-10-22 19:04:03 -0700780 int[] resultArray = new int[2];
781 resultArray[0] = mResult;
782 resultArray[1] = mRetryCount;
783 return resultArray;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700784 }
785 }
786
787 public void updateServiceLocation() {
788 // No permission check needed here: this call is harmless, and it's
789 // needed for the ServiceState.requestStateUpdate() call (which is
790 // already intentionally exposed to 3rd parties.)
791 mPhone.updateServiceLocation();
792 }
793
794 public boolean isRadioOn() {
795 return mPhone.getServiceState().getVoiceRegState() != ServiceState.STATE_POWER_OFF;
796 }
797
798 public void toggleRadioOnOff() {
799 enforceModifyPermission();
800 mPhone.setRadioPower(!isRadioOn());
801 }
802 public boolean setRadio(boolean turnOn) {
803 enforceModifyPermission();
804 if ((mPhone.getServiceState().getVoiceRegState() != ServiceState.STATE_POWER_OFF) != turnOn) {
805 toggleRadioOnOff();
806 }
807 return true;
808 }
809 public boolean setRadioPower(boolean turnOn) {
810 enforceModifyPermission();
811 mPhone.setRadioPower(turnOn);
812 return true;
813 }
814
815 public boolean enableDataConnectivity() {
816 enforceModifyPermission();
817 ConnectivityManager cm =
818 (ConnectivityManager)mApp.getSystemService(Context.CONNECTIVITY_SERVICE);
819 cm.setMobileDataEnabled(true);
820 return true;
821 }
822
823 public int enableApnType(String type) {
824 enforceModifyPermission();
825 return mPhone.enableApnType(type);
826 }
827
828 public int disableApnType(String type) {
829 enforceModifyPermission();
830 return mPhone.disableApnType(type);
831 }
832
833 public boolean disableDataConnectivity() {
834 enforceModifyPermission();
835 ConnectivityManager cm =
836 (ConnectivityManager)mApp.getSystemService(Context.CONNECTIVITY_SERVICE);
837 cm.setMobileDataEnabled(false);
838 return true;
839 }
840
841 public boolean isDataConnectivityPossible() {
842 return mPhone.isDataConnectivityPossible();
843 }
844
845 public boolean handlePinMmi(String dialString) {
846 enforceModifyPermission();
847 return (Boolean) sendRequest(CMD_HANDLE_PIN_MMI, dialString);
848 }
849
850 public void cancelMissedCallsNotification() {
851 enforceModifyPermission();
852 mApp.notificationMgr.cancelMissedCallNotification();
853 }
854
855 public int getCallState() {
856 return DefaultPhoneNotifier.convertCallState(mCM.getState());
857 }
858
859 public int getDataState() {
860 return DefaultPhoneNotifier.convertDataState(mPhone.getDataConnectionState());
861 }
862
863 public int getDataActivity() {
864 return DefaultPhoneNotifier.convertDataActivityState(mPhone.getDataActivityState());
865 }
866
867 @Override
868 public Bundle getCellLocation() {
869 try {
870 mApp.enforceCallingOrSelfPermission(
871 android.Manifest.permission.ACCESS_FINE_LOCATION, null);
872 } catch (SecurityException e) {
873 // If we have ACCESS_FINE_LOCATION permission, skip the check for ACCESS_COARSE_LOCATION
874 // A failure should throw the SecurityException from ACCESS_COARSE_LOCATION since this
875 // is the weaker precondition
876 mApp.enforceCallingOrSelfPermission(
877 android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
878 }
879
Jake Hambye994d462014-02-03 13:10:13 -0800880 if (checkIfCallerIsSelfOrForegroundUser()) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700881 if (DBG_LOC) log("getCellLocation: is active user");
882 Bundle data = new Bundle();
883 mPhone.getCellLocation().fillInNotifierBundle(data);
884 return data;
885 } else {
886 if (DBG_LOC) log("getCellLocation: suppress non-active user");
887 return null;
888 }
889 }
890
891 @Override
892 public void enableLocationUpdates() {
893 mApp.enforceCallingOrSelfPermission(
894 android.Manifest.permission.CONTROL_LOCATION_UPDATES, null);
895 mPhone.enableLocationUpdates();
896 }
897
898 @Override
899 public void disableLocationUpdates() {
900 mApp.enforceCallingOrSelfPermission(
901 android.Manifest.permission.CONTROL_LOCATION_UPDATES, null);
902 mPhone.disableLocationUpdates();
903 }
904
905 @Override
906 @SuppressWarnings("unchecked")
907 public List<NeighboringCellInfo> getNeighboringCellInfo(String callingPackage) {
908 try {
909 mApp.enforceCallingOrSelfPermission(
910 android.Manifest.permission.ACCESS_FINE_LOCATION, null);
911 } catch (SecurityException e) {
912 // If we have ACCESS_FINE_LOCATION permission, skip the check
913 // for ACCESS_COARSE_LOCATION
914 // A failure should throw the SecurityException from
915 // ACCESS_COARSE_LOCATION since this is the weaker precondition
916 mApp.enforceCallingOrSelfPermission(
917 android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
918 }
919
920 if (mAppOps.noteOp(AppOpsManager.OP_NEIGHBORING_CELLS, Binder.getCallingUid(),
921 callingPackage) != AppOpsManager.MODE_ALLOWED) {
922 return null;
923 }
Jake Hambye994d462014-02-03 13:10:13 -0800924 if (checkIfCallerIsSelfOrForegroundUser()) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700925 if (DBG_LOC) log("getNeighboringCellInfo: is active user");
926
927 ArrayList<NeighboringCellInfo> cells = null;
928
929 try {
930 cells = (ArrayList<NeighboringCellInfo>) sendRequest(
931 CMD_HANDLE_NEIGHBORING_CELL, null);
932 } catch (RuntimeException e) {
Shishir Agrawal566b7612013-10-28 14:41:00 -0700933 loge("getNeighboringCellInfo " + e);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700934 }
935 return cells;
936 } else {
937 if (DBG_LOC) log("getNeighboringCellInfo: suppress non-active user");
938 return null;
939 }
940 }
941
942
943 @Override
944 public List<CellInfo> getAllCellInfo() {
945 try {
946 mApp.enforceCallingOrSelfPermission(
947 android.Manifest.permission.ACCESS_FINE_LOCATION, null);
948 } catch (SecurityException e) {
949 // If we have ACCESS_FINE_LOCATION permission, skip the check for ACCESS_COARSE_LOCATION
950 // A failure should throw the SecurityException from ACCESS_COARSE_LOCATION since this
951 // is the weaker precondition
952 mApp.enforceCallingOrSelfPermission(
953 android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
954 }
955
Jake Hambye994d462014-02-03 13:10:13 -0800956 if (checkIfCallerIsSelfOrForegroundUser()) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700957 if (DBG_LOC) log("getAllCellInfo: is active user");
958 return mPhone.getAllCellInfo();
959 } else {
960 if (DBG_LOC) log("getAllCellInfo: suppress non-active user");
961 return null;
962 }
963 }
964
Sailesh Nepalbd76e4e2013-10-27 13:59:44 -0700965 @Override
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700966 public void setCellInfoListRate(int rateInMillis) {
967 mPhone.setCellInfoListRate(rateInMillis);
968 }
969
Sailesh Nepalbd76e4e2013-10-27 13:59:44 -0700970 @Override
Sailesh Nepalcc0375f2013-11-13 09:15:18 -0800971 public void newIncomingThirdPartyCall(ComponentName component, String callId,
972 String callerDisplayName) {
Sailesh Nepalbd76e4e2013-10-27 13:59:44 -0700973 // TODO(sail): Enforce that the component belongs to the calling package.
974 if (DBG) {
975 log("newIncomingThirdPartyCall: component: " + component + " callId: " + callId);
976 }
977 enforceCallPermission();
Sailesh Nepalcc0375f2013-11-13 09:15:18 -0800978 sendRequestAsync(CMD_NEW_INCOMING_THIRD_PARTY_CALL, new IncomingThirdPartyCallArgs(
979 component, callId, callerDisplayName));
Sailesh Nepalbd76e4e2013-10-27 13:59:44 -0700980 }
981
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700982 //
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 /**
1046 * Make sure the caller has SIM_COMMUNICATION permission.
1047 *
1048 * @throws SecurityException if the caller does not have the required permission.
1049 */
1050 private void enforceSimCommunicationPermission() {
1051 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.SIM_COMMUNICATION, null);
1052 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001053
1054 private String createTelUrl(String number) {
1055 if (TextUtils.isEmpty(number)) {
1056 return null;
1057 }
1058
Jake Hambye994d462014-02-03 13:10:13 -08001059 return "tel:" + number;
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001060 }
1061
Ihab Awadf9e92732013-12-05 18:02:52 -08001062 private static void log(String msg) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001063 Log.d(LOG_TAG, "[PhoneIntfMgr] " + msg);
1064 }
1065
Ihab Awadf9e92732013-12-05 18:02:52 -08001066 private static void loge(String msg) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001067 Log.e(LOG_TAG, "[PhoneIntfMgr] " + msg);
1068 }
1069
1070 public int getActivePhoneType() {
1071 return mPhone.getPhoneType();
1072 }
1073
1074 /**
1075 * Returns the CDMA ERI icon index to display
1076 */
1077 public int getCdmaEriIconIndex() {
1078 return mPhone.getCdmaEriIconIndex();
1079 }
1080
1081 /**
1082 * Returns the CDMA ERI icon mode,
1083 * 0 - ON
1084 * 1 - FLASHING
1085 */
1086 public int getCdmaEriIconMode() {
1087 return mPhone.getCdmaEriIconMode();
1088 }
1089
1090 /**
1091 * Returns the CDMA ERI text,
1092 */
1093 public String getCdmaEriText() {
1094 return mPhone.getCdmaEriText();
1095 }
1096
1097 /**
1098 * Returns true if CDMA provisioning needs to run.
1099 */
1100 public boolean needsOtaServiceProvisioning() {
1101 return mPhone.needsOtaServiceProvisioning();
1102 }
1103
1104 /**
1105 * Returns the unread count of voicemails
1106 */
1107 public int getVoiceMessageCount() {
1108 return mPhone.getVoiceMessageCount();
1109 }
1110
1111 /**
1112 * Returns the data network type
1113 *
1114 * @Deprecated to be removed Q3 2013 use {@link #getDataNetworkType}.
1115 */
1116 @Override
1117 public int getNetworkType() {
1118 return mPhone.getServiceState().getDataNetworkType();
1119 }
1120
1121 /**
1122 * Returns the data network type
1123 */
1124 @Override
1125 public int getDataNetworkType() {
1126 return mPhone.getServiceState().getDataNetworkType();
1127 }
1128
1129 /**
1130 * Returns the data network type
1131 */
1132 @Override
1133 public int getVoiceNetworkType() {
1134 return mPhone.getServiceState().getVoiceNetworkType();
1135 }
1136
1137 /**
1138 * @return true if a ICC card is present
1139 */
1140 public boolean hasIccCard() {
1141 return mPhone.getIccCard().hasIccCard();
1142 }
1143
1144 /**
1145 * Return if the current radio is LTE on CDMA. This
1146 * is a tri-state return value as for a period of time
1147 * the mode may be unknown.
1148 *
1149 * @return {@link Phone#LTE_ON_CDMA_UNKNOWN}, {@link Phone#LTE_ON_CDMA_FALSE}
Jake Hambye994d462014-02-03 13:10:13 -08001150 * or {@link Phone#LTE_ON_CDMA_TRUE}
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001151 */
1152 public int getLteOnCdmaMode() {
1153 return mPhone.getLteOnCdmaMode();
1154 }
Ihab Awadf2177b72013-11-25 13:33:23 -08001155
1156 /**
1157 * @see android.telephony.TelephonyManager.WifiCallingChoices
1158 */
1159 public int getWhenToMakeWifiCalls() {
Sailesh Nepald1e68152013-12-12 19:08:02 -08001160 return Settings.System.getInt(mPhone.getContext().getContentResolver(),
1161 Settings.System.WHEN_TO_MAKE_WIFI_CALLS, getWhenToMakeWifiCallsDefaultPreference());
Ihab Awadf2177b72013-11-25 13:33:23 -08001162 }
1163
1164 /**
1165 * @see android.telephony.TelephonyManager.WifiCallingChoices
1166 */
1167 public void setWhenToMakeWifiCalls(int preference) {
Sailesh Nepald1e68152013-12-12 19:08:02 -08001168 if (DBG) log("setWhenToMakeWifiCallsStr, storing setting = " + preference);
1169 Settings.System.putInt(mPhone.getContext().getContentResolver(),
1170 Settings.System.WHEN_TO_MAKE_WIFI_CALLS, preference);
Ihab Awadf9e92732013-12-05 18:02:52 -08001171 }
1172
Sailesh Nepald1e68152013-12-12 19:08:02 -08001173 private static int getWhenToMakeWifiCallsDefaultPreference() {
1174 // TODO(sail): Use a build property to choose this value.
Evan Charlton9829e882013-12-19 15:30:38 -08001175 return TelephonyManager.WifiCallingChoices.ALWAYS_USE;
Ihab Awadf2177b72013-11-25 13:33:23 -08001176 }
Shishir Agrawal69f68122013-12-16 17:25:49 -08001177
Shishir Agrawal566b7612013-10-28 14:41:00 -07001178 @Override
1179 public int iccOpenLogicalChannel(String AID) {
1180 enforceSimCommunicationPermission();
1181
1182 if (DBG) log("iccOpenLogicalChannel: " + AID);
1183 Integer channel = (Integer)sendRequest(CMD_OPEN_CHANNEL, AID);
1184 if (DBG) log("iccOpenLogicalChannel: " + channel);
Jake Hambye994d462014-02-03 13:10:13 -08001185 return channel;
Shishir Agrawal566b7612013-10-28 14:41:00 -07001186 }
1187
1188 @Override
1189 public boolean iccCloseLogicalChannel(int channel) {
1190 enforceSimCommunicationPermission();
1191
1192 if (DBG) log("iccCloseLogicalChannel: " + channel);
1193 if (channel < 0) {
1194 return false;
1195 }
Jake Hambye994d462014-02-03 13:10:13 -08001196 Boolean success = (Boolean)sendRequest(CMD_CLOSE_CHANNEL, channel);
Shishir Agrawal566b7612013-10-28 14:41:00 -07001197 if (DBG) log("iccCloseLogicalChannel: " + success);
1198 return success;
1199 }
1200
1201 @Override
1202 public String iccTransmitApduLogicalChannel(int channel, int cla,
1203 int command, int p1, int p2, int p3, String data) {
1204 enforceSimCommunicationPermission();
1205
1206 if (DBG) {
1207 log("iccTransmitApduLogicalChannel: chnl=" + channel + " cla=" + cla +
1208 " cmd=" + command + " p1=" + p1 + " p2=" + p2 + " p3=" + p3 +
1209 " data=" + data);
1210 }
1211
1212 if (channel < 0) {
1213 return "";
1214 }
1215
1216 IccIoResult response = (IccIoResult)sendRequest(CMD_TRANSMIT_APDU,
1217 new IccAPDUArgument(channel, cla, command, p1, p2, p3, data));
1218 if (DBG) log("iccTransmitApduLogicalChannel: " + response);
1219
1220 // If the payload is null, there was an error. Indicate that by returning
1221 // an empty string.
1222 if (response.payload == null) {
1223 return "";
1224 }
1225
1226 // Append the returned status code to the end of the response payload.
1227 String s = Integer.toHexString(
1228 (response.sw1 << 8) + response.sw2 + 0x10000).substring(1);
1229 s = IccUtils.bytesToHexString(response.payload) + s;
1230 return s;
1231 }
Jake Hambye994d462014-02-03 13:10:13 -08001232
1233 /**
1234 * Read one of the NV items defined in {@link com.android.internal.telephony.RadioNVItems}
1235 * and {@code ril_nv_items.h}. Used for device configuration by some CDMA operators.
1236 *
1237 * @param itemID the ID of the item to read
1238 * @return the NV item as a String, or null on error.
1239 */
1240 @Override
1241 public String nvReadItem(int itemID) {
1242 enforceModifyPermission();
1243 if (DBG) log("nvReadItem: item " + itemID);
1244 String value = (String) sendRequest(CMD_NV_READ_ITEM, itemID);
1245 if (DBG) log("nvReadItem: item " + itemID + " is \"" + value + '"');
1246 return value;
1247 }
1248
1249 /**
1250 * Write one of the NV items defined in {@link com.android.internal.telephony.RadioNVItems}
1251 * and {@code ril_nv_items.h}. Used for device configuration by some CDMA operators.
1252 *
1253 * @param itemID the ID of the item to read
1254 * @param itemValue the value to write, as a String
1255 * @return true on success; false on any failure
1256 */
1257 @Override
1258 public boolean nvWriteItem(int itemID, String itemValue) {
1259 enforceModifyPermission();
1260 if (DBG) log("nvWriteItem: item " + itemID + " value \"" + itemValue + '"');
1261 Boolean success = (Boolean) sendRequest(CMD_NV_WRITE_ITEM,
1262 new Pair<Integer, String>(itemID, itemValue));
1263 if (DBG) log("nvWriteItem: item " + itemID + ' ' + (success ? "ok" : "fail"));
1264 return success;
1265 }
1266
1267 /**
1268 * Update the CDMA Preferred Roaming List (PRL) in the radio NV storage.
1269 * Used for device configuration by some CDMA operators.
1270 *
1271 * @param preferredRoamingList byte array containing the new PRL
1272 * @return true on success; false on any failure
1273 */
1274 @Override
1275 public boolean nvWriteCdmaPrl(byte[] preferredRoamingList) {
1276 enforceModifyPermission();
1277 if (DBG) log("nvWriteCdmaPrl: value: " + HexDump.toHexString(preferredRoamingList));
1278 Boolean success = (Boolean) sendRequest(CMD_NV_WRITE_CDMA_PRL, preferredRoamingList);
1279 if (DBG) log("nvWriteCdmaPrl: " + (success ? "ok" : "fail"));
1280 return success;
1281 }
1282
1283 /**
1284 * Perform the specified type of NV config reset.
1285 * Used for device configuration by some CDMA operators.
1286 *
1287 * @param resetType the type of reset to perform (1 == factory reset; 2 == NV-only reset)
1288 * @return true on success; false on any failure
1289 */
1290 @Override
1291 public boolean nvResetConfig(int resetType) {
1292 enforceModifyPermission();
1293 if (DBG) log("nvResetConfig: type " + resetType);
1294 Boolean success = (Boolean) sendRequest(CMD_NV_RESET_CONFIG, resetType);
1295 if (DBG) log("nvResetConfig: type " + resetType + ' ' + (success ? "ok" : "fail"));
1296 return success;
1297 }
1298
1299 /**
1300 * Change the radio to the specified mode.
1301 * Used for device configuration by some operators.
1302 *
1303 * @param radioMode is 0 for offline mode, 1 for online mode, 2 for low-power mode,
1304 * or 3 to reset the radio.
1305 * @return true on success; false on any failure
1306 */
1307 @Override
1308 public boolean setRadioMode(int radioMode) {
1309 enforceModifyPermission();
1310 if (DBG) log("setRadioMode: mode " + radioMode);
1311 Boolean success = (Boolean) sendRequest(CMD_SET_RADIO_MODE, radioMode);
1312 if (DBG) log("setRadioMode: mode " + radioMode + ' ' + (success ? "ok" : "fail"));
1313 return success;
1314 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001315}