blob: e79ad3cb22192a33f98d5f864b139aac0995ba5a [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;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070024import android.net.Uri;
25import android.os.AsyncResult;
26import android.os.Binder;
27import android.os.Bundle;
28import android.os.Handler;
Gabriel Peal36ebb0d2014-03-20 09:20:43 -070029import android.os.IBinder;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070030import android.os.Looper;
31import android.os.Message;
32import android.os.Process;
Gabriel Peal36ebb0d2014-03-20 09:20:43 -070033import android.os.RemoteException;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070034import android.os.ServiceManager;
35import android.os.UserHandle;
Ihab Awadf2177b72013-11-25 13:33:23 -080036import android.provider.Settings;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070037import android.telephony.CellInfo;
Jake Hambye994d462014-02-03 13:10:13 -080038import android.telephony.NeighboringCellInfo;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070039import android.telephony.ServiceState;
Ihab Awadf2177b72013-11-25 13:33:23 -080040import android.telephony.TelephonyManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070041import android.text.TextUtils;
42import android.util.Log;
Jake Hambye994d462014-02-03 13:10:13 -080043import android.util.Pair;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070044
Shishir Agrawal566b7612013-10-28 14:41:00 -070045import com.android.internal.telephony.CallManager;
46import com.android.internal.telephony.CommandException;
Gabriel Peal36ebb0d2014-03-20 09:20:43 -070047import com.android.internal.telephony.Connection;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070048import com.android.internal.telephony.DefaultPhoneNotifier;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070049import com.android.internal.telephony.ITelephony;
Jake Hambye994d462014-02-03 13:10:13 -080050import com.android.internal.telephony.IccCard;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070051import com.android.internal.telephony.Phone;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070052import com.android.internal.telephony.PhoneConstants;
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;
Gabriel Peal36ebb0d2014-03-20 09:20:43 -070057import com.android.services.telephony.common.Call;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070058
Santos Cordon7d4ddf62013-07-10 11:58:08 -070059import java.util.ArrayList;
Gabriel Peal36ebb0d2014-03-20 09:20:43 -070060import java.util.HashMap;
61import java.util.Iterator;
Jake Hambye994d462014-02-03 13:10:13 -080062import java.util.List;
Gabriel Peal36ebb0d2014-03-20 09:20:43 -070063import java.util.Map;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070064
65/**
66 * Implementation of the ITelephony interface.
67 */
Santos Cordon117fee72014-05-16 17:56:12 -070068public class PhoneInterfaceManager extends ITelephony.Stub {
Santos Cordon7d4ddf62013-07-10 11:58:08 -070069 private static final String LOG_TAG = "PhoneInterfaceManager";
70 private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
71 private static final boolean DBG_LOC = false;
72
73 // Message codes used with mMainThreadHandler
74 private static final int CMD_HANDLE_PIN_MMI = 1;
75 private static final int CMD_HANDLE_NEIGHBORING_CELL = 2;
76 private static final int EVENT_NEIGHBORING_CELL_DONE = 3;
77 private static final int CMD_ANSWER_RINGING_CALL = 4;
78 private static final int CMD_END_CALL = 5; // not used yet
79 private static final int CMD_SILENCE_RINGER = 6;
Shishir Agrawal566b7612013-10-28 14:41:00 -070080 private static final int CMD_TRANSMIT_APDU = 7;
81 private static final int EVENT_TRANSMIT_APDU_DONE = 8;
82 private static final int CMD_OPEN_CHANNEL = 9;
83 private static final int EVENT_OPEN_CHANNEL_DONE = 10;
84 private static final int CMD_CLOSE_CHANNEL = 11;
85 private static final int EVENT_CLOSE_CHANNEL_DONE = 12;
Jake Hambye994d462014-02-03 13:10:13 -080086 private static final int CMD_NV_READ_ITEM = 13;
87 private static final int EVENT_NV_READ_ITEM_DONE = 14;
88 private static final int CMD_NV_WRITE_ITEM = 15;
89 private static final int EVENT_NV_WRITE_ITEM_DONE = 16;
90 private static final int CMD_NV_WRITE_CDMA_PRL = 17;
91 private static final int EVENT_NV_WRITE_CDMA_PRL_DONE = 18;
92 private static final int CMD_NV_RESET_CONFIG = 19;
93 private static final int EVENT_NV_RESET_CONFIG_DONE = 20;
Jake Hamby7c27be32014-03-03 13:25:59 -080094 private static final int CMD_GET_PREFERRED_NETWORK_TYPE = 21;
95 private static final int EVENT_GET_PREFERRED_NETWORK_TYPE_DONE = 22;
96 private static final int CMD_SET_PREFERRED_NETWORK_TYPE = 23;
97 private static final int EVENT_SET_PREFERRED_NETWORK_TYPE_DONE = 24;
Sailesh Nepal35b59452014-03-06 09:26:56 -080098 private static final int CMD_SEND_ENVELOPE = 25;
99 private static final int EVENT_SEND_ENVELOPE_DONE = 26;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700100
101 /** The singleton instance. */
102 private static PhoneInterfaceManager sInstance;
103
104 PhoneGlobals mApp;
105 Phone mPhone;
106 CallManager mCM;
107 AppOpsManager mAppOps;
108 MainThreadHandler mMainThreadHandler;
Santos Cordon406c0342013-08-28 00:07:47 -0700109 CallHandlerServiceProxy mCallHandlerService;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700110
111 /**
Shishir Agrawal566b7612013-10-28 14:41:00 -0700112 * A request object to use for transmitting data to an ICC.
113 */
114 private static final class IccAPDUArgument {
115 public int channel, cla, command, p1, p2, p3;
116 public String data;
117
118 public IccAPDUArgument(int channel, int cla, int command,
119 int p1, int p2, int p3, String data) {
120 this.channel = channel;
121 this.cla = cla;
122 this.command = command;
123 this.p1 = p1;
124 this.p2 = p2;
125 this.p3 = p3;
126 this.data = data;
127 }
128 }
129
130 /**
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700131 * A request object for use with {@link MainThreadHandler}. Requesters should wait() on the
132 * request after sending. The main thread will notify the request when it is complete.
133 */
134 private static final class MainThreadRequest {
135 /** The argument to use for the request */
136 public Object argument;
137 /** The result of the request that is run on the main thread */
138 public Object result;
139
140 public MainThreadRequest(Object argument) {
141 this.argument = argument;
142 }
143 }
144
Sailesh Nepalcc0375f2013-11-13 09:15:18 -0800145 private static final class IncomingThirdPartyCallArgs {
146 public final ComponentName component;
147 public final String callId;
148 public final String callerDisplayName;
149
150 public IncomingThirdPartyCallArgs(ComponentName component, String callId,
151 String callerDisplayName) {
152 this.component = component;
153 this.callId = callId;
154 this.callerDisplayName = callerDisplayName;
155 }
156 }
157
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700158 /**
159 * A handler that processes messages on the main thread in the phone process. Since many
160 * of the Phone calls are not thread safe this is needed to shuttle the requests from the
161 * inbound binder threads to the main thread in the phone process. The Binder thread
162 * may provide a {@link MainThreadRequest} object in the msg.obj field that they are waiting
163 * on, which will be notified when the operation completes and will contain the result of the
164 * request.
165 *
166 * <p>If a MainThreadRequest object is provided in the msg.obj field,
167 * note that request.result must be set to something non-null for the calling thread to
168 * unblock.
169 */
170 private final class MainThreadHandler extends Handler {
171 @Override
172 public void handleMessage(Message msg) {
173 MainThreadRequest request;
174 Message onCompleted;
175 AsyncResult ar;
176
177 switch (msg.what) {
178 case CMD_HANDLE_PIN_MMI:
179 request = (MainThreadRequest) msg.obj;
Jake Hambye994d462014-02-03 13:10:13 -0800180 request.result = mPhone.handlePinMmi((String) request.argument);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700181 // Wake up the requesting thread
182 synchronized (request) {
183 request.notifyAll();
184 }
185 break;
186
187 case CMD_HANDLE_NEIGHBORING_CELL:
188 request = (MainThreadRequest) msg.obj;
189 onCompleted = obtainMessage(EVENT_NEIGHBORING_CELL_DONE,
190 request);
191 mPhone.getNeighboringCids(onCompleted);
192 break;
193
194 case EVENT_NEIGHBORING_CELL_DONE:
195 ar = (AsyncResult) msg.obj;
196 request = (MainThreadRequest) ar.userObj;
197 if (ar.exception == null && ar.result != null) {
198 request.result = ar.result;
199 } else {
200 // create an empty list to notify the waiting thread
Jake Hambye994d462014-02-03 13:10:13 -0800201 request.result = new ArrayList<NeighboringCellInfo>(0);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700202 }
203 // Wake up the requesting thread
204 synchronized (request) {
205 request.notifyAll();
206 }
207 break;
208
209 case CMD_ANSWER_RINGING_CALL:
210 answerRingingCallInternal();
211 break;
212
213 case CMD_SILENCE_RINGER:
214 silenceRingerInternal();
215 break;
216
217 case CMD_END_CALL:
218 request = (MainThreadRequest) msg.obj;
Jake Hambye994d462014-02-03 13:10:13 -0800219 boolean hungUp;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700220 int phoneType = mPhone.getPhoneType();
221 if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
222 // CDMA: If the user presses the Power button we treat it as
223 // ending the complete call session
224 hungUp = PhoneUtils.hangupRingingAndActive(mPhone);
225 } else if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
226 // GSM: End the call as per the Phone state
227 hungUp = PhoneUtils.hangup(mCM);
228 } else {
229 throw new IllegalStateException("Unexpected phone type: " + phoneType);
230 }
231 if (DBG) log("CMD_END_CALL: " + (hungUp ? "hung up!" : "no call to hang up"));
232 request.result = hungUp;
233 // Wake up the requesting thread
234 synchronized (request) {
235 request.notifyAll();
236 }
237 break;
238
Shishir Agrawal566b7612013-10-28 14:41:00 -0700239 case CMD_TRANSMIT_APDU:
240 request = (MainThreadRequest) msg.obj;
241 IccAPDUArgument argument = (IccAPDUArgument) request.argument;
242 onCompleted = obtainMessage(EVENT_TRANSMIT_APDU_DONE, request);
243 UiccController.getInstance().getUiccCard().iccTransmitApduLogicalChannel(
244 argument.channel, argument.cla, argument.command,
245 argument.p1, argument.p2, argument.p3, argument.data,
246 onCompleted);
247 break;
248
249 case EVENT_TRANSMIT_APDU_DONE:
250 ar = (AsyncResult) msg.obj;
251 request = (MainThreadRequest) ar.userObj;
252 if (ar.exception == null && ar.result != null) {
253 request.result = ar.result;
254 } else {
255 request.result = new IccIoResult(0x6F, 0, (byte[])null);
256 if (ar.result == null) {
257 loge("iccTransmitApduLogicalChannel: Empty response");
Jake Hambye994d462014-02-03 13:10:13 -0800258 } else if (ar.exception instanceof CommandException) {
Shishir Agrawal566b7612013-10-28 14:41:00 -0700259 loge("iccTransmitApduLogicalChannel: CommandException: " +
Jake Hambye994d462014-02-03 13:10:13 -0800260 ar.exception);
Shishir Agrawal566b7612013-10-28 14:41:00 -0700261 } else {
262 loge("iccTransmitApduLogicalChannel: Unknown exception");
263 }
264 }
265 synchronized (request) {
266 request.notifyAll();
267 }
268 break;
269
Derek Tan4d5e5c12014-02-04 11:54:58 -0800270 case CMD_SEND_ENVELOPE:
271 request = (MainThreadRequest) msg.obj;
272 onCompleted = obtainMessage(EVENT_SEND_ENVELOPE_DONE, request);
Shishir Agrawal9f9877d2014-03-14 09:36:27 -0700273 UiccController.getInstance().getUiccCard().sendEnvelopeWithStatus(
Derek Tan4d5e5c12014-02-04 11:54:58 -0800274 (String)request.argument, onCompleted);
275 break;
276
277 case EVENT_SEND_ENVELOPE_DONE:
278 ar = (AsyncResult) msg.obj;
279 request = (MainThreadRequest) ar.userObj;
Shishir Agrawal9f9877d2014-03-14 09:36:27 -0700280 if (ar.exception == null && ar.result != null) {
281 request.result = ar.result;
Derek Tan4d5e5c12014-02-04 11:54:58 -0800282 } else {
Shishir Agrawal9f9877d2014-03-14 09:36:27 -0700283 request.result = new IccIoResult(0x6F, 0, (byte[])null);
284 if (ar.result == null) {
285 loge("sendEnvelopeWithStatus: Empty response");
286 } else if (ar.exception instanceof CommandException) {
287 loge("sendEnvelopeWithStatus: CommandException: " +
288 ar.exception);
289 } else {
290 loge("sendEnvelopeWithStatus: exception:" + ar.exception);
291 }
Derek Tan4d5e5c12014-02-04 11:54:58 -0800292 }
293 synchronized (request) {
294 request.notifyAll();
295 }
296 break;
297
Shishir Agrawal566b7612013-10-28 14:41:00 -0700298 case CMD_OPEN_CHANNEL:
299 request = (MainThreadRequest) msg.obj;
300 onCompleted = obtainMessage(EVENT_OPEN_CHANNEL_DONE, request);
301 UiccController.getInstance().getUiccCard().iccOpenLogicalChannel(
302 (String)request.argument, onCompleted);
303 break;
304
305 case EVENT_OPEN_CHANNEL_DONE:
306 ar = (AsyncResult) msg.obj;
307 request = (MainThreadRequest) ar.userObj;
308 if (ar.exception == null && ar.result != null) {
Jake Hambye994d462014-02-03 13:10:13 -0800309 request.result = ((int[]) ar.result)[0];
Shishir Agrawal566b7612013-10-28 14:41:00 -0700310 } else {
Jake Hambye994d462014-02-03 13:10:13 -0800311 request.result = -1;
Shishir Agrawal566b7612013-10-28 14:41:00 -0700312 if (ar.result == null) {
313 loge("iccOpenLogicalChannel: Empty response");
Jake Hambye994d462014-02-03 13:10:13 -0800314 } else if (ar.exception instanceof CommandException) {
Shishir Agrawal566b7612013-10-28 14:41:00 -0700315 loge("iccOpenLogicalChannel: CommandException: " +
Jake Hambye994d462014-02-03 13:10:13 -0800316 ar.exception);
Shishir Agrawal566b7612013-10-28 14:41:00 -0700317 } else {
318 loge("iccOpenLogicalChannel: Unknown exception");
319 }
320 }
321 synchronized (request) {
322 request.notifyAll();
323 }
324 break;
325
326 case CMD_CLOSE_CHANNEL:
327 request = (MainThreadRequest) msg.obj;
328 onCompleted = obtainMessage(EVENT_CLOSE_CHANNEL_DONE,
329 request);
330 UiccController.getInstance().getUiccCard().iccCloseLogicalChannel(
Jake Hambye994d462014-02-03 13:10:13 -0800331 (Integer) request.argument,
Shishir Agrawal566b7612013-10-28 14:41:00 -0700332 onCompleted);
333 break;
334
335 case EVENT_CLOSE_CHANNEL_DONE:
Jake Hambye994d462014-02-03 13:10:13 -0800336 handleNullReturnEvent(msg, "iccCloseLogicalChannel");
337 break;
338
339 case CMD_NV_READ_ITEM:
340 request = (MainThreadRequest) msg.obj;
341 onCompleted = obtainMessage(EVENT_NV_READ_ITEM_DONE, request);
342 mPhone.nvReadItem((Integer) request.argument, onCompleted);
343 break;
344
345 case EVENT_NV_READ_ITEM_DONE:
Shishir Agrawal566b7612013-10-28 14:41:00 -0700346 ar = (AsyncResult) msg.obj;
347 request = (MainThreadRequest) ar.userObj;
Jake Hambye994d462014-02-03 13:10:13 -0800348 if (ar.exception == null && ar.result != null) {
349 request.result = ar.result; // String
Shishir Agrawal566b7612013-10-28 14:41:00 -0700350 } else {
Jake Hambye994d462014-02-03 13:10:13 -0800351 request.result = "";
352 if (ar.result == null) {
353 loge("nvReadItem: Empty response");
354 } else if (ar.exception instanceof CommandException) {
355 loge("nvReadItem: CommandException: " +
356 ar.exception);
Shishir Agrawal566b7612013-10-28 14:41:00 -0700357 } else {
Jake Hambye994d462014-02-03 13:10:13 -0800358 loge("nvReadItem: Unknown exception");
Shishir Agrawal566b7612013-10-28 14:41:00 -0700359 }
360 }
361 synchronized (request) {
362 request.notifyAll();
363 }
364 break;
365
Jake Hambye994d462014-02-03 13:10:13 -0800366 case CMD_NV_WRITE_ITEM:
367 request = (MainThreadRequest) msg.obj;
368 onCompleted = obtainMessage(EVENT_NV_WRITE_ITEM_DONE, request);
369 Pair<Integer, String> idValue = (Pair<Integer, String>) request.argument;
370 mPhone.nvWriteItem(idValue.first, idValue.second, onCompleted);
371 break;
372
373 case EVENT_NV_WRITE_ITEM_DONE:
374 handleNullReturnEvent(msg, "nvWriteItem");
375 break;
376
377 case CMD_NV_WRITE_CDMA_PRL:
378 request = (MainThreadRequest) msg.obj;
379 onCompleted = obtainMessage(EVENT_NV_WRITE_CDMA_PRL_DONE, request);
380 mPhone.nvWriteCdmaPrl((byte[]) request.argument, onCompleted);
381 break;
382
383 case EVENT_NV_WRITE_CDMA_PRL_DONE:
384 handleNullReturnEvent(msg, "nvWriteCdmaPrl");
385 break;
386
387 case CMD_NV_RESET_CONFIG:
388 request = (MainThreadRequest) msg.obj;
389 onCompleted = obtainMessage(EVENT_NV_RESET_CONFIG_DONE, request);
390 mPhone.nvResetConfig((Integer) request.argument, onCompleted);
391 break;
392
393 case EVENT_NV_RESET_CONFIG_DONE:
394 handleNullReturnEvent(msg, "nvResetConfig");
395 break;
396
Jake Hamby7c27be32014-03-03 13:25:59 -0800397 case CMD_GET_PREFERRED_NETWORK_TYPE:
398 request = (MainThreadRequest) msg.obj;
399 onCompleted = obtainMessage(EVENT_GET_PREFERRED_NETWORK_TYPE_DONE, request);
400 mPhone.getPreferredNetworkType(onCompleted);
401 break;
402
403 case EVENT_GET_PREFERRED_NETWORK_TYPE_DONE:
404 ar = (AsyncResult) msg.obj;
405 request = (MainThreadRequest) ar.userObj;
406 if (ar.exception == null && ar.result != null) {
407 request.result = ar.result; // Integer
408 } else {
409 request.result = -1;
410 if (ar.result == null) {
411 loge("getPreferredNetworkType: Empty response");
412 } else if (ar.exception instanceof CommandException) {
413 loge("getPreferredNetworkType: CommandException: " +
414 ar.exception);
415 } else {
416 loge("getPreferredNetworkType: Unknown exception");
417 }
418 }
419 synchronized (request) {
420 request.notifyAll();
421 }
422 break;
423
424 case CMD_SET_PREFERRED_NETWORK_TYPE:
425 request = (MainThreadRequest) msg.obj;
426 onCompleted = obtainMessage(EVENT_SET_PREFERRED_NETWORK_TYPE_DONE, request);
427 int networkType = (Integer) request.argument;
428 mPhone.setPreferredNetworkType(networkType, onCompleted);
429 break;
430
431 case EVENT_SET_PREFERRED_NETWORK_TYPE_DONE:
432 handleNullReturnEvent(msg, "setPreferredNetworkType");
433 break;
434
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700435 default:
436 Log.w(LOG_TAG, "MainThreadHandler: unexpected message code: " + msg.what);
437 break;
438 }
439 }
Jake Hambye994d462014-02-03 13:10:13 -0800440
441 private void handleNullReturnEvent(Message msg, String command) {
442 AsyncResult ar = (AsyncResult) msg.obj;
443 MainThreadRequest request = (MainThreadRequest) ar.userObj;
444 if (ar.exception == null) {
445 request.result = true;
446 } else {
447 request.result = false;
448 if (ar.exception instanceof CommandException) {
449 loge(command + ": CommandException: " + ar.exception);
450 } else {
451 loge(command + ": Unknown exception");
452 }
453 }
454 synchronized (request) {
455 request.notifyAll();
456 }
457 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700458 }
459
460 /**
461 * Posts the specified command to be executed on the main thread,
462 * waits for the request to complete, and returns the result.
463 * @see #sendRequestAsync
464 */
465 private Object sendRequest(int command, Object argument) {
466 if (Looper.myLooper() == mMainThreadHandler.getLooper()) {
467 throw new RuntimeException("This method will deadlock if called from the main thread.");
468 }
469
470 MainThreadRequest request = new MainThreadRequest(argument);
471 Message msg = mMainThreadHandler.obtainMessage(command, request);
472 msg.sendToTarget();
473
474 // Wait for the request to complete
475 synchronized (request) {
476 while (request.result == null) {
477 try {
478 request.wait();
479 } catch (InterruptedException e) {
480 // Do nothing, go back and wait until the request is complete
481 }
482 }
483 }
484 return request.result;
485 }
486
487 /**
488 * Asynchronous ("fire and forget") version of sendRequest():
489 * Posts the specified command to be executed on the main thread, and
490 * returns immediately.
491 * @see #sendRequest
492 */
493 private void sendRequestAsync(int command) {
494 mMainThreadHandler.sendEmptyMessage(command);
495 }
496
497 /**
Sailesh Nepalbd76e4e2013-10-27 13:59:44 -0700498 * Same as {@link #sendRequestAsync(int)} except it takes an argument.
499 * @see {@link #sendRequest(int,Object)}
500 */
501 private void sendRequestAsync(int command, Object argument) {
502 MainThreadRequest request = new MainThreadRequest(argument);
503 Message msg = mMainThreadHandler.obtainMessage(command, request);
504 msg.sendToTarget();
505 }
506
507 /**
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700508 * Initialize the singleton PhoneInterfaceManager instance.
509 * This is only done once, at startup, from PhoneApp.onCreate().
510 */
Santos Cordon406c0342013-08-28 00:07:47 -0700511 /* package */ static PhoneInterfaceManager init(PhoneGlobals app, Phone phone,
Santos Cordon117fee72014-05-16 17:56:12 -0700512 CallHandlerServiceProxy callHandlerService) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700513 synchronized (PhoneInterfaceManager.class) {
514 if (sInstance == null) {
Santos Cordon117fee72014-05-16 17:56:12 -0700515 sInstance = new PhoneInterfaceManager(app, phone, callHandlerService);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700516 } else {
517 Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance);
518 }
519 return sInstance;
520 }
521 }
522
523 /** Private constructor; @see init() */
Santos Cordon406c0342013-08-28 00:07:47 -0700524 private PhoneInterfaceManager(PhoneGlobals app, Phone phone,
Santos Cordon117fee72014-05-16 17:56:12 -0700525 CallHandlerServiceProxy callHandlerService) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700526 mApp = app;
527 mPhone = phone;
528 mCM = PhoneGlobals.getInstance().mCM;
529 mAppOps = (AppOpsManager)app.getSystemService(Context.APP_OPS_SERVICE);
530 mMainThreadHandler = new MainThreadHandler();
Santos Cordon406c0342013-08-28 00:07:47 -0700531 mCallHandlerService = callHandlerService;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700532 publish();
533 }
534
535 private void publish() {
536 if (DBG) log("publish: " + this);
537
538 ServiceManager.addService("phone", this);
539 }
540
541 //
542 // Implementation of the ITelephony interface.
543 //
544
545 public void dial(String number) {
546 if (DBG) log("dial: " + number);
547 // No permission check needed here: This is just a wrapper around the
548 // ACTION_DIAL intent, which is available to any app since it puts up
549 // the UI before it does anything.
550
551 String url = createTelUrl(number);
552 if (url == null) {
553 return;
554 }
555
556 // PENDING: should we just silently fail if phone is offhook or ringing?
557 PhoneConstants.State state = mCM.getState();
558 if (state != PhoneConstants.State.OFFHOOK && state != PhoneConstants.State.RINGING) {
559 Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse(url));
560 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
561 mApp.startActivity(intent);
562 }
563 }
564
565 public void call(String callingPackage, String number) {
566 if (DBG) log("call: " + number);
567
568 // This is just a wrapper around the ACTION_CALL intent, but we still
569 // need to do a permission check since we're calling startActivity()
570 // from the context of the phone app.
571 enforceCallPermission();
572
573 if (mAppOps.noteOp(AppOpsManager.OP_CALL_PHONE, Binder.getCallingUid(), callingPackage)
574 != AppOpsManager.MODE_ALLOWED) {
575 return;
576 }
577
578 String url = createTelUrl(number);
579 if (url == null) {
580 return;
581 }
582
583 Intent intent = new Intent(Intent.ACTION_CALL, Uri.parse(url));
584 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
585 mApp.startActivity(intent);
586 }
587
588 private boolean showCallScreenInternal(boolean specifyInitialDialpadState,
Makoto Onukibcf20992013-09-12 17:59:30 -0700589 boolean showDialpad) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700590 if (!PhoneGlobals.sVoiceCapable) {
591 // Never allow the InCallScreen to appear on data-only devices.
592 return false;
593 }
594 if (isIdle()) {
595 return false;
596 }
597 // If the phone isn't idle then go to the in-call screen
598 long callingId = Binder.clearCallingIdentity();
Santos Cordon406c0342013-08-28 00:07:47 -0700599
Makoto Onukibcf20992013-09-12 17:59:30 -0700600 mCallHandlerService.bringToForeground(showDialpad);
Santos Cordon406c0342013-08-28 00:07:47 -0700601
602 Binder.restoreCallingIdentity(callingId);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700603 return true;
604 }
605
606 // Show the in-call screen without specifying the initial dialpad state.
607 public boolean showCallScreen() {
608 return showCallScreenInternal(false, false);
609 }
610
611 // The variation of showCallScreen() that specifies the initial dialpad state.
612 // (Ideally this would be called showCallScreen() too, just with a different
613 // signature, but AIDL doesn't allow that.)
614 public boolean showCallScreenWithDialpad(boolean showDialpad) {
615 return showCallScreenInternal(true, showDialpad);
616 }
617
618 /**
619 * End a call based on call state
620 * @return true is a call was ended
621 */
622 public boolean endCall() {
623 enforceCallPermission();
624 return (Boolean) sendRequest(CMD_END_CALL, null);
625 }
626
627 public void answerRingingCall() {
628 if (DBG) log("answerRingingCall...");
629 // TODO: there should eventually be a separate "ANSWER_PHONE" permission,
630 // but that can probably wait till the big TelephonyManager API overhaul.
631 // For now, protect this call with the MODIFY_PHONE_STATE permission.
632 enforceModifyPermission();
633 sendRequestAsync(CMD_ANSWER_RINGING_CALL);
634 }
635
636 /**
637 * Make the actual telephony calls to implement answerRingingCall().
638 * This should only be called from the main thread of the Phone app.
639 * @see #answerRingingCall
640 *
641 * TODO: it would be nice to return true if we answered the call, or
642 * false if there wasn't actually a ringing incoming call, or some
643 * other error occurred. (In other words, pass back the return value
644 * from PhoneUtils.answerCall() or PhoneUtils.answerAndEndActive().)
645 * But that would require calling this method via sendRequest() rather
646 * than sendRequestAsync(), and right now we don't actually *need* that
647 * return value, so let's just return void for now.
648 */
649 private void answerRingingCallInternal() {
650 final boolean hasRingingCall = !mPhone.getRingingCall().isIdle();
651 if (hasRingingCall) {
652 final boolean hasActiveCall = !mPhone.getForegroundCall().isIdle();
653 final boolean hasHoldingCall = !mPhone.getBackgroundCall().isIdle();
654 if (hasActiveCall && hasHoldingCall) {
655 // Both lines are in use!
656 // TODO: provide a flag to let the caller specify what
657 // policy to use if both lines are in use. (The current
658 // behavior is hardwired to "answer incoming, end ongoing",
659 // which is how the CALL button is specced to behave.)
660 PhoneUtils.answerAndEndActive(mCM, mCM.getFirstActiveRingingCall());
661 return;
662 } else {
663 // answerCall() will automatically hold the current active
664 // call, if there is one.
665 PhoneUtils.answerCall(mCM.getFirstActiveRingingCall());
666 return;
667 }
668 } else {
669 // No call was ringing.
670 return;
671 }
672 }
673
674 public void silenceRinger() {
675 if (DBG) log("silenceRinger...");
676 // TODO: find a more appropriate permission to check here.
677 // (That can probably wait till the big TelephonyManager API overhaul.
678 // For now, protect this call with the MODIFY_PHONE_STATE permission.)
679 enforceModifyPermission();
680 sendRequestAsync(CMD_SILENCE_RINGER);
681 }
682
683 /**
684 * Internal implemenation of silenceRinger().
685 * This should only be called from the main thread of the Phone app.
686 * @see #silenceRinger
687 */
688 private void silenceRingerInternal() {
689 if ((mCM.getState() == PhoneConstants.State.RINGING)
690 && mApp.notifier.isRinging()) {
691 // Ringer is actually playing, so silence it.
692 if (DBG) log("silenceRingerInternal: silencing...");
693 mApp.notifier.silenceRinger();
694 }
695 }
696
697 public boolean isOffhook() {
698 return (mCM.getState() == PhoneConstants.State.OFFHOOK);
699 }
700
701 public boolean isRinging() {
702 return (mCM.getState() == PhoneConstants.State.RINGING);
703 }
704
705 public boolean isIdle() {
706 return (mCM.getState() == PhoneConstants.State.IDLE);
707 }
708
709 public boolean isSimPinEnabled() {
710 enforceReadPermission();
711 return (PhoneGlobals.getInstance().isSimPinEnabled());
712 }
713
714 public boolean supplyPin(String pin) {
Wink Saville9de0f752013-10-22 19:04:03 -0700715 int [] resultArray = supplyPinReportResult(pin);
716 return (resultArray[0] == PhoneConstants.PIN_RESULT_SUCCESS) ? true : false;
717 }
718
719 public boolean supplyPuk(String puk, String pin) {
720 int [] resultArray = supplyPukReportResult(puk, pin);
721 return (resultArray[0] == PhoneConstants.PIN_RESULT_SUCCESS) ? true : false;
722 }
723
724 /** {@hide} */
725 public int[] supplyPinReportResult(String pin) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700726 enforceModifyPermission();
727 final UnlockSim checkSimPin = new UnlockSim(mPhone.getIccCard());
728 checkSimPin.start();
729 return checkSimPin.unlockSim(null, pin);
730 }
731
Wink Saville9de0f752013-10-22 19:04:03 -0700732 /** {@hide} */
733 public int[] supplyPukReportResult(String puk, String pin) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700734 enforceModifyPermission();
735 final UnlockSim checkSimPuk = new UnlockSim(mPhone.getIccCard());
736 checkSimPuk.start();
737 return checkSimPuk.unlockSim(puk, pin);
738 }
739
740 /**
Wink Saville9de0f752013-10-22 19:04:03 -0700741 * Helper thread to turn async call to SimCard#supplyPin into
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700742 * a synchronous one.
743 */
744 private static class UnlockSim extends Thread {
745
746 private final IccCard mSimCard;
747
748 private boolean mDone = false;
Wink Saville9de0f752013-10-22 19:04:03 -0700749 private int mResult = PhoneConstants.PIN_GENERAL_FAILURE;
750 private int mRetryCount = -1;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700751
752 // For replies from SimCard interface
753 private Handler mHandler;
754
755 // For async handler to identify request type
756 private static final int SUPPLY_PIN_COMPLETE = 100;
757
758 public UnlockSim(IccCard simCard) {
759 mSimCard = simCard;
760 }
761
762 @Override
763 public void run() {
764 Looper.prepare();
765 synchronized (UnlockSim.this) {
766 mHandler = new Handler() {
767 @Override
768 public void handleMessage(Message msg) {
769 AsyncResult ar = (AsyncResult) msg.obj;
770 switch (msg.what) {
771 case SUPPLY_PIN_COMPLETE:
772 Log.d(LOG_TAG, "SUPPLY_PIN_COMPLETE");
773 synchronized (UnlockSim.this) {
Wink Saville9de0f752013-10-22 19:04:03 -0700774 mRetryCount = msg.arg1;
775 if (ar.exception != null) {
776 if (ar.exception instanceof CommandException &&
777 ((CommandException)(ar.exception)).getCommandError()
778 == CommandException.Error.PASSWORD_INCORRECT) {
779 mResult = PhoneConstants.PIN_PASSWORD_INCORRECT;
780 } else {
781 mResult = PhoneConstants.PIN_GENERAL_FAILURE;
782 }
783 } else {
784 mResult = PhoneConstants.PIN_RESULT_SUCCESS;
785 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700786 mDone = true;
787 UnlockSim.this.notifyAll();
788 }
789 break;
790 }
791 }
792 };
793 UnlockSim.this.notifyAll();
794 }
795 Looper.loop();
796 }
797
798 /*
799 * Use PIN or PUK to unlock SIM card
800 *
801 * If PUK is null, unlock SIM card with PIN
802 *
803 * If PUK is not null, unlock SIM card with PUK and set PIN code
804 */
Wink Saville9de0f752013-10-22 19:04:03 -0700805 synchronized int[] unlockSim(String puk, String pin) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700806
807 while (mHandler == null) {
808 try {
809 wait();
810 } catch (InterruptedException e) {
811 Thread.currentThread().interrupt();
812 }
813 }
814 Message callback = Message.obtain(mHandler, SUPPLY_PIN_COMPLETE);
815
816 if (puk == null) {
817 mSimCard.supplyPin(pin, callback);
818 } else {
819 mSimCard.supplyPuk(puk, pin, callback);
820 }
821
822 while (!mDone) {
823 try {
824 Log.d(LOG_TAG, "wait for done");
825 wait();
826 } catch (InterruptedException e) {
827 // Restore the interrupted status
828 Thread.currentThread().interrupt();
829 }
830 }
831 Log.d(LOG_TAG, "done");
Wink Saville9de0f752013-10-22 19:04:03 -0700832 int[] resultArray = new int[2];
833 resultArray[0] = mResult;
834 resultArray[1] = mRetryCount;
835 return resultArray;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700836 }
837 }
838
839 public void updateServiceLocation() {
840 // No permission check needed here: this call is harmless, and it's
841 // needed for the ServiceState.requestStateUpdate() call (which is
842 // already intentionally exposed to 3rd parties.)
843 mPhone.updateServiceLocation();
844 }
845
846 public boolean isRadioOn() {
847 return mPhone.getServiceState().getVoiceRegState() != ServiceState.STATE_POWER_OFF;
848 }
849
850 public void toggleRadioOnOff() {
851 enforceModifyPermission();
852 mPhone.setRadioPower(!isRadioOn());
853 }
854 public boolean setRadio(boolean turnOn) {
855 enforceModifyPermission();
856 if ((mPhone.getServiceState().getVoiceRegState() != ServiceState.STATE_POWER_OFF) != turnOn) {
857 toggleRadioOnOff();
858 }
859 return true;
860 }
861 public boolean setRadioPower(boolean turnOn) {
862 enforceModifyPermission();
863 mPhone.setRadioPower(turnOn);
864 return true;
865 }
866
867 public boolean enableDataConnectivity() {
868 enforceModifyPermission();
Robert Greenwalted86e582014-05-21 20:03:20 -0700869 mPhone.setDataEnabled(true);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700870 return true;
871 }
872
873 public int enableApnType(String type) {
874 enforceModifyPermission();
875 return mPhone.enableApnType(type);
876 }
877
878 public int disableApnType(String type) {
879 enforceModifyPermission();
880 return mPhone.disableApnType(type);
881 }
882
883 public boolean disableDataConnectivity() {
884 enforceModifyPermission();
Robert Greenwalted86e582014-05-21 20:03:20 -0700885 mPhone.setDataEnabled(false);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700886 return true;
887 }
888
889 public boolean isDataConnectivityPossible() {
890 return mPhone.isDataConnectivityPossible();
891 }
892
893 public boolean handlePinMmi(String dialString) {
894 enforceModifyPermission();
895 return (Boolean) sendRequest(CMD_HANDLE_PIN_MMI, dialString);
896 }
897
898 public void cancelMissedCallsNotification() {
899 enforceModifyPermission();
900 mApp.notificationMgr.cancelMissedCallNotification();
901 }
902
903 public int getCallState() {
904 return DefaultPhoneNotifier.convertCallState(mCM.getState());
905 }
906
907 public int getDataState() {
908 return DefaultPhoneNotifier.convertDataState(mPhone.getDataConnectionState());
909 }
910
911 public int getDataActivity() {
912 return DefaultPhoneNotifier.convertDataActivityState(mPhone.getDataActivityState());
913 }
914
915 @Override
916 public Bundle getCellLocation() {
917 try {
918 mApp.enforceCallingOrSelfPermission(
919 android.Manifest.permission.ACCESS_FINE_LOCATION, null);
920 } catch (SecurityException e) {
921 // If we have ACCESS_FINE_LOCATION permission, skip the check for ACCESS_COARSE_LOCATION
922 // A failure should throw the SecurityException from ACCESS_COARSE_LOCATION since this
923 // is the weaker precondition
924 mApp.enforceCallingOrSelfPermission(
925 android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
926 }
927
Jake Hambye994d462014-02-03 13:10:13 -0800928 if (checkIfCallerIsSelfOrForegroundUser()) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700929 if (DBG_LOC) log("getCellLocation: is active user");
930 Bundle data = new Bundle();
931 mPhone.getCellLocation().fillInNotifierBundle(data);
932 return data;
933 } else {
934 if (DBG_LOC) log("getCellLocation: suppress non-active user");
935 return null;
936 }
937 }
938
939 @Override
940 public void enableLocationUpdates() {
941 mApp.enforceCallingOrSelfPermission(
942 android.Manifest.permission.CONTROL_LOCATION_UPDATES, null);
943 mPhone.enableLocationUpdates();
944 }
945
946 @Override
947 public void disableLocationUpdates() {
948 mApp.enforceCallingOrSelfPermission(
949 android.Manifest.permission.CONTROL_LOCATION_UPDATES, null);
950 mPhone.disableLocationUpdates();
951 }
952
953 @Override
954 @SuppressWarnings("unchecked")
955 public List<NeighboringCellInfo> getNeighboringCellInfo(String callingPackage) {
956 try {
957 mApp.enforceCallingOrSelfPermission(
958 android.Manifest.permission.ACCESS_FINE_LOCATION, null);
959 } catch (SecurityException e) {
960 // If we have ACCESS_FINE_LOCATION permission, skip the check
961 // for ACCESS_COARSE_LOCATION
962 // A failure should throw the SecurityException from
963 // ACCESS_COARSE_LOCATION since this is the weaker precondition
964 mApp.enforceCallingOrSelfPermission(
965 android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
966 }
967
968 if (mAppOps.noteOp(AppOpsManager.OP_NEIGHBORING_CELLS, Binder.getCallingUid(),
969 callingPackage) != AppOpsManager.MODE_ALLOWED) {
970 return null;
971 }
Jake Hambye994d462014-02-03 13:10:13 -0800972 if (checkIfCallerIsSelfOrForegroundUser()) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700973 if (DBG_LOC) log("getNeighboringCellInfo: is active user");
974
975 ArrayList<NeighboringCellInfo> cells = null;
976
977 try {
978 cells = (ArrayList<NeighboringCellInfo>) sendRequest(
979 CMD_HANDLE_NEIGHBORING_CELL, null);
980 } catch (RuntimeException e) {
Shishir Agrawal566b7612013-10-28 14:41:00 -0700981 loge("getNeighboringCellInfo " + e);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700982 }
983 return cells;
984 } else {
985 if (DBG_LOC) log("getNeighboringCellInfo: suppress non-active user");
986 return null;
987 }
988 }
989
990
991 @Override
992 public List<CellInfo> getAllCellInfo() {
993 try {
994 mApp.enforceCallingOrSelfPermission(
995 android.Manifest.permission.ACCESS_FINE_LOCATION, null);
996 } catch (SecurityException e) {
997 // If we have ACCESS_FINE_LOCATION permission, skip the check for ACCESS_COARSE_LOCATION
998 // A failure should throw the SecurityException from ACCESS_COARSE_LOCATION since this
999 // is the weaker precondition
1000 mApp.enforceCallingOrSelfPermission(
1001 android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
1002 }
1003
Jake Hambye994d462014-02-03 13:10:13 -08001004 if (checkIfCallerIsSelfOrForegroundUser()) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001005 if (DBG_LOC) log("getAllCellInfo: is active user");
1006 return mPhone.getAllCellInfo();
1007 } else {
1008 if (DBG_LOC) log("getAllCellInfo: suppress non-active user");
1009 return null;
1010 }
1011 }
1012
Sailesh Nepalbd76e4e2013-10-27 13:59:44 -07001013 @Override
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001014 public void setCellInfoListRate(int rateInMillis) {
1015 mPhone.setCellInfoListRate(rateInMillis);
1016 }
1017
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001018 //
1019 // Internal helper methods.
1020 //
1021
Jake Hambye994d462014-02-03 13:10:13 -08001022 private static boolean checkIfCallerIsSelfOrForegroundUser() {
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001023 boolean ok;
1024
1025 boolean self = Binder.getCallingUid() == Process.myUid();
1026 if (!self) {
1027 // Get the caller's user id then clear the calling identity
1028 // which will be restored in the finally clause.
1029 int callingUser = UserHandle.getCallingUserId();
1030 long ident = Binder.clearCallingIdentity();
1031
1032 try {
1033 // With calling identity cleared the current user is the foreground user.
1034 int foregroundUser = ActivityManager.getCurrentUser();
1035 ok = (foregroundUser == callingUser);
1036 if (DBG_LOC) {
1037 log("checkIfCallerIsSelfOrForegoundUser: foregroundUser=" + foregroundUser
1038 + " callingUser=" + callingUser + " ok=" + ok);
1039 }
1040 } catch (Exception ex) {
1041 if (DBG_LOC) loge("checkIfCallerIsSelfOrForegoundUser: Exception ex=" + ex);
1042 ok = false;
1043 } finally {
1044 Binder.restoreCallingIdentity(ident);
1045 }
1046 } else {
1047 if (DBG_LOC) log("checkIfCallerIsSelfOrForegoundUser: is self");
1048 ok = true;
1049 }
1050 if (DBG_LOC) log("checkIfCallerIsSelfOrForegoundUser: ret=" + ok);
1051 return ok;
1052 }
1053
1054 /**
1055 * Make sure the caller has the READ_PHONE_STATE permission.
1056 *
1057 * @throws SecurityException if the caller does not have the required permission
1058 */
1059 private void enforceReadPermission() {
1060 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_STATE, null);
1061 }
1062
1063 /**
1064 * Make sure the caller has the MODIFY_PHONE_STATE permission.
1065 *
1066 * @throws SecurityException if the caller does not have the required permission
1067 */
1068 private void enforceModifyPermission() {
1069 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE, null);
1070 }
1071
1072 /**
1073 * Make sure the caller has the CALL_PHONE permission.
1074 *
1075 * @throws SecurityException if the caller does not have the required permission
1076 */
1077 private void enforceCallPermission() {
1078 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.CALL_PHONE, null);
1079 }
1080
Shishir Agrawal566b7612013-10-28 14:41:00 -07001081 /**
Gabriel Peal36ebb0d2014-03-20 09:20:43 -07001082 * Make sure the caller has the READ_PRIVILEGED_PHONE_STATE permission.
1083 *
1084 * @throws SecurityException if the caller does not have the required permission
1085 */
1086 private void enforcePrivilegedPhoneStatePermission() {
1087 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
1088 null);
1089 }
1090
1091 /**
Shishir Agrawal566b7612013-10-28 14:41:00 -07001092 * Make sure the caller has SIM_COMMUNICATION permission.
1093 *
1094 * @throws SecurityException if the caller does not have the required permission.
1095 */
1096 private void enforceSimCommunicationPermission() {
1097 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.SIM_COMMUNICATION, null);
1098 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001099
1100 private String createTelUrl(String number) {
1101 if (TextUtils.isEmpty(number)) {
1102 return null;
1103 }
1104
Jake Hambye994d462014-02-03 13:10:13 -08001105 return "tel:" + number;
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001106 }
1107
Ihab Awadf9e92732013-12-05 18:02:52 -08001108 private static void log(String msg) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001109 Log.d(LOG_TAG, "[PhoneIntfMgr] " + msg);
1110 }
1111
Ihab Awadf9e92732013-12-05 18:02:52 -08001112 private static void loge(String msg) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001113 Log.e(LOG_TAG, "[PhoneIntfMgr] " + msg);
1114 }
1115
1116 public int getActivePhoneType() {
1117 return mPhone.getPhoneType();
1118 }
1119
1120 /**
1121 * Returns the CDMA ERI icon index to display
1122 */
1123 public int getCdmaEriIconIndex() {
1124 return mPhone.getCdmaEriIconIndex();
1125 }
1126
1127 /**
1128 * Returns the CDMA ERI icon mode,
1129 * 0 - ON
1130 * 1 - FLASHING
1131 */
1132 public int getCdmaEriIconMode() {
1133 return mPhone.getCdmaEriIconMode();
1134 }
1135
1136 /**
1137 * Returns the CDMA ERI text,
1138 */
1139 public String getCdmaEriText() {
1140 return mPhone.getCdmaEriText();
1141 }
1142
1143 /**
1144 * Returns true if CDMA provisioning needs to run.
1145 */
1146 public boolean needsOtaServiceProvisioning() {
1147 return mPhone.needsOtaServiceProvisioning();
1148 }
1149
1150 /**
1151 * Returns the unread count of voicemails
1152 */
1153 public int getVoiceMessageCount() {
1154 return mPhone.getVoiceMessageCount();
1155 }
1156
1157 /**
1158 * Returns the data network type
1159 *
1160 * @Deprecated to be removed Q3 2013 use {@link #getDataNetworkType}.
1161 */
1162 @Override
1163 public int getNetworkType() {
1164 return mPhone.getServiceState().getDataNetworkType();
1165 }
1166
1167 /**
1168 * Returns the data network type
1169 */
1170 @Override
1171 public int getDataNetworkType() {
1172 return mPhone.getServiceState().getDataNetworkType();
1173 }
1174
1175 /**
1176 * Returns the data network type
1177 */
1178 @Override
1179 public int getVoiceNetworkType() {
1180 return mPhone.getServiceState().getVoiceNetworkType();
1181 }
1182
1183 /**
1184 * @return true if a ICC card is present
1185 */
1186 public boolean hasIccCard() {
1187 return mPhone.getIccCard().hasIccCard();
1188 }
1189
1190 /**
1191 * Return if the current radio is LTE on CDMA. This
1192 * is a tri-state return value as for a period of time
1193 * the mode may be unknown.
1194 *
1195 * @return {@link Phone#LTE_ON_CDMA_UNKNOWN}, {@link Phone#LTE_ON_CDMA_FALSE}
Jake Hambye994d462014-02-03 13:10:13 -08001196 * or {@link Phone#LTE_ON_CDMA_TRUE}
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001197 */
1198 public int getLteOnCdmaMode() {
1199 return mPhone.getLteOnCdmaMode();
1200 }
Ihab Awadf2177b72013-11-25 13:33:23 -08001201
1202 /**
1203 * @see android.telephony.TelephonyManager.WifiCallingChoices
1204 */
1205 public int getWhenToMakeWifiCalls() {
Sailesh Nepald1e68152013-12-12 19:08:02 -08001206 return Settings.System.getInt(mPhone.getContext().getContentResolver(),
1207 Settings.System.WHEN_TO_MAKE_WIFI_CALLS, getWhenToMakeWifiCallsDefaultPreference());
Ihab Awadf2177b72013-11-25 13:33:23 -08001208 }
1209
1210 /**
1211 * @see android.telephony.TelephonyManager.WifiCallingChoices
1212 */
1213 public void setWhenToMakeWifiCalls(int preference) {
Sailesh Nepald1e68152013-12-12 19:08:02 -08001214 if (DBG) log("setWhenToMakeWifiCallsStr, storing setting = " + preference);
1215 Settings.System.putInt(mPhone.getContext().getContentResolver(),
1216 Settings.System.WHEN_TO_MAKE_WIFI_CALLS, preference);
Ihab Awadf9e92732013-12-05 18:02:52 -08001217 }
1218
Sailesh Nepald1e68152013-12-12 19:08:02 -08001219 private static int getWhenToMakeWifiCallsDefaultPreference() {
1220 // TODO(sail): Use a build property to choose this value.
Evan Charlton9829e882013-12-19 15:30:38 -08001221 return TelephonyManager.WifiCallingChoices.ALWAYS_USE;
Ihab Awadf2177b72013-11-25 13:33:23 -08001222 }
Shishir Agrawal69f68122013-12-16 17:25:49 -08001223
Shishir Agrawal566b7612013-10-28 14:41:00 -07001224 @Override
1225 public int iccOpenLogicalChannel(String AID) {
1226 enforceSimCommunicationPermission();
1227
1228 if (DBG) log("iccOpenLogicalChannel: " + AID);
1229 Integer channel = (Integer)sendRequest(CMD_OPEN_CHANNEL, AID);
1230 if (DBG) log("iccOpenLogicalChannel: " + channel);
Jake Hambye994d462014-02-03 13:10:13 -08001231 return channel;
Shishir Agrawal566b7612013-10-28 14:41:00 -07001232 }
1233
1234 @Override
1235 public boolean iccCloseLogicalChannel(int channel) {
1236 enforceSimCommunicationPermission();
1237
1238 if (DBG) log("iccCloseLogicalChannel: " + channel);
1239 if (channel < 0) {
1240 return false;
1241 }
Jake Hambye994d462014-02-03 13:10:13 -08001242 Boolean success = (Boolean)sendRequest(CMD_CLOSE_CHANNEL, channel);
Shishir Agrawal566b7612013-10-28 14:41:00 -07001243 if (DBG) log("iccCloseLogicalChannel: " + success);
1244 return success;
1245 }
1246
1247 @Override
1248 public String iccTransmitApduLogicalChannel(int channel, int cla,
1249 int command, int p1, int p2, int p3, String data) {
1250 enforceSimCommunicationPermission();
1251
1252 if (DBG) {
1253 log("iccTransmitApduLogicalChannel: chnl=" + channel + " cla=" + cla +
1254 " cmd=" + command + " p1=" + p1 + " p2=" + p2 + " p3=" + p3 +
1255 " data=" + data);
1256 }
1257
1258 if (channel < 0) {
1259 return "";
1260 }
1261
1262 IccIoResult response = (IccIoResult)sendRequest(CMD_TRANSMIT_APDU,
1263 new IccAPDUArgument(channel, cla, command, p1, p2, p3, data));
1264 if (DBG) log("iccTransmitApduLogicalChannel: " + response);
1265
1266 // If the payload is null, there was an error. Indicate that by returning
1267 // an empty string.
1268 if (response.payload == null) {
1269 return "";
1270 }
1271
1272 // Append the returned status code to the end of the response payload.
1273 String s = Integer.toHexString(
1274 (response.sw1 << 8) + response.sw2 + 0x10000).substring(1);
1275 s = IccUtils.bytesToHexString(response.payload) + s;
1276 return s;
1277 }
Jake Hambye994d462014-02-03 13:10:13 -08001278
Evan Charltonc66da362014-05-16 14:06:40 -07001279 @Override
1280 public String sendEnvelopeWithStatus(String content) {
1281 enforceSimCommunicationPermission();
1282
1283 IccIoResult response = (IccIoResult)sendRequest(CMD_SEND_ENVELOPE, content);
1284 if (response.payload == null) {
1285 return "";
1286 }
1287
1288 // Append the returned status code to the end of the response payload.
1289 String s = Integer.toHexString(
1290 (response.sw1 << 8) + response.sw2 + 0x10000).substring(1);
1291 s = IccUtils.bytesToHexString(response.payload) + s;
1292 return s;
1293 }
1294
Jake Hambye994d462014-02-03 13:10:13 -08001295 /**
1296 * Read one of the NV items defined in {@link com.android.internal.telephony.RadioNVItems}
1297 * and {@code ril_nv_items.h}. Used for device configuration by some CDMA operators.
1298 *
1299 * @param itemID the ID of the item to read
1300 * @return the NV item as a String, or null on error.
1301 */
1302 @Override
1303 public String nvReadItem(int itemID) {
1304 enforceModifyPermission();
1305 if (DBG) log("nvReadItem: item " + itemID);
1306 String value = (String) sendRequest(CMD_NV_READ_ITEM, itemID);
1307 if (DBG) log("nvReadItem: item " + itemID + " is \"" + value + '"');
1308 return value;
1309 }
1310
1311 /**
1312 * Write one of the NV items defined in {@link com.android.internal.telephony.RadioNVItems}
1313 * and {@code ril_nv_items.h}. Used for device configuration by some CDMA operators.
1314 *
1315 * @param itemID the ID of the item to read
1316 * @param itemValue the value to write, as a String
1317 * @return true on success; false on any failure
1318 */
1319 @Override
1320 public boolean nvWriteItem(int itemID, String itemValue) {
1321 enforceModifyPermission();
1322 if (DBG) log("nvWriteItem: item " + itemID + " value \"" + itemValue + '"');
1323 Boolean success = (Boolean) sendRequest(CMD_NV_WRITE_ITEM,
1324 new Pair<Integer, String>(itemID, itemValue));
1325 if (DBG) log("nvWriteItem: item " + itemID + ' ' + (success ? "ok" : "fail"));
1326 return success;
1327 }
1328
1329 /**
1330 * Update the CDMA Preferred Roaming List (PRL) in the radio NV storage.
1331 * Used for device configuration by some CDMA operators.
1332 *
1333 * @param preferredRoamingList byte array containing the new PRL
1334 * @return true on success; false on any failure
1335 */
1336 @Override
1337 public boolean nvWriteCdmaPrl(byte[] preferredRoamingList) {
1338 enforceModifyPermission();
1339 if (DBG) log("nvWriteCdmaPrl: value: " + HexDump.toHexString(preferredRoamingList));
1340 Boolean success = (Boolean) sendRequest(CMD_NV_WRITE_CDMA_PRL, preferredRoamingList);
1341 if (DBG) log("nvWriteCdmaPrl: " + (success ? "ok" : "fail"));
1342 return success;
1343 }
1344
1345 /**
1346 * Perform the specified type of NV config reset.
1347 * Used for device configuration by some CDMA operators.
1348 *
1349 * @param resetType the type of reset to perform (1 == factory reset; 2 == NV-only reset)
1350 * @return true on success; false on any failure
1351 */
1352 @Override
1353 public boolean nvResetConfig(int resetType) {
1354 enforceModifyPermission();
1355 if (DBG) log("nvResetConfig: type " + resetType);
1356 Boolean success = (Boolean) sendRequest(CMD_NV_RESET_CONFIG, resetType);
1357 if (DBG) log("nvResetConfig: type " + resetType + ' ' + (success ? "ok" : "fail"));
1358 return success;
1359 }
Jake Hamby7c27be32014-03-03 13:25:59 -08001360
1361 /**
1362 * Get the preferred network type.
1363 * Used for device configuration by some CDMA operators.
1364 *
1365 * @return the preferred network type, defined in RILConstants.java.
1366 */
1367 @Override
1368 public int getPreferredNetworkType() {
1369 enforceModifyPermission();
1370 if (DBG) log("getPreferredNetworkType");
1371 int[] result = (int[]) sendRequest(CMD_GET_PREFERRED_NETWORK_TYPE, null);
1372 int networkType = (result != null ? result[0] : -1);
1373 if (DBG) log("getPreferredNetworkType: " + networkType);
1374 return networkType;
1375 }
1376
1377 /**
1378 * Set the preferred network type.
1379 * Used for device configuration by some CDMA operators.
1380 *
1381 * @param networkType the preferred network type, defined in RILConstants.java.
1382 * @return true on success; false on any failure.
1383 */
1384 @Override
1385 public boolean setPreferredNetworkType(int networkType) {
1386 enforceModifyPermission();
1387 if (DBG) log("setPreferredNetworkType: type " + networkType);
1388 Boolean success = (Boolean) sendRequest(CMD_SET_PREFERRED_NETWORK_TYPE, networkType);
1389 if (DBG) log("setPreferredNetworkType: " + (success ? "ok" : "fail"));
1390 return success;
1391 }
Robert Greenwalted86e582014-05-21 20:03:20 -07001392
1393 /**
1394 * Set mobile data enabled
1395 * Used by the user through settings etc to turn on/off mobile data
1396 *
1397 * @param enable {@code true} turn turn data on, else {@code false}
1398 */
1399 @Override
1400 public void setDataEnabled(boolean enable) {
1401 enforceModifyPermission();
1402 mPhone.setDataEnabled(enable);
1403 }
1404
1405 /**
1406 * Get whether mobile data is enabled
1407 *
1408 * @return {@code true} if data is enabled else {@code false}
1409 */
1410 @Override
1411 public boolean getDataEnabled() {
1412 enforceModifyPermission();
1413 return mPhone.getDataEnabled();
1414 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001415}