blob: 33546872f2948b9ab51464977a01fa30ba8059bd [file] [log] [blame]
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001/*
2 * Copyright (C) 2006 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.phone;
18
19import android.app.ActivityManager;
20import android.app.AppOpsManager;
Sailesh Nepalbd76e4e2013-10-27 13:59:44 -070021import android.content.ComponentName;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070022import android.content.Context;
23import android.content.Intent;
24import android.net.ConnectivityManager;
25import android.net.Uri;
26import android.os.AsyncResult;
27import android.os.Binder;
28import android.os.Bundle;
29import android.os.Handler;
Gabriel Peal36ebb0d2014-03-20 09:20:43 -070030import android.os.IBinder;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070031import android.os.Looper;
32import android.os.Message;
33import android.os.Process;
Gabriel Peal36ebb0d2014-03-20 09:20:43 -070034import android.os.RemoteException;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070035import android.os.ServiceManager;
36import android.os.UserHandle;
Ihab Awadf2177b72013-11-25 13:33:23 -080037import android.provider.Settings;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070038import android.telephony.CellInfo;
Jake Hambye994d462014-02-03 13:10:13 -080039import android.telephony.NeighboringCellInfo;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070040import android.telephony.ServiceState;
Ihab Awadf2177b72013-11-25 13:33:23 -080041import android.telephony.TelephonyManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070042import android.text.TextUtils;
43import android.util.Log;
Jake Hambye994d462014-02-03 13:10:13 -080044import android.util.Pair;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070045
Shishir Agrawal566b7612013-10-28 14:41:00 -070046import com.android.internal.telephony.CallManager;
47import com.android.internal.telephony.CommandException;
Gabriel Peal36ebb0d2014-03-20 09:20:43 -070048import com.android.internal.telephony.Connection;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070049import com.android.internal.telephony.DefaultPhoneNotifier;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070050import com.android.internal.telephony.ITelephony;
Jake Hambye994d462014-02-03 13:10:13 -080051import com.android.internal.telephony.IccCard;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070052import com.android.internal.telephony.Phone;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070053import com.android.internal.telephony.PhoneConstants;
Shishir Agrawal566b7612013-10-28 14:41:00 -070054import com.android.internal.telephony.uicc.IccIoResult;
55import com.android.internal.telephony.uicc.IccUtils;
56import com.android.internal.telephony.uicc.UiccController;
Jake Hambye994d462014-02-03 13:10:13 -080057import com.android.internal.util.HexDump;
Gabriel Peal36ebb0d2014-03-20 09:20:43 -070058import com.android.services.telephony.common.Call;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070059
Santos Cordon7d4ddf62013-07-10 11:58:08 -070060import java.util.ArrayList;
Gabriel Peal36ebb0d2014-03-20 09:20:43 -070061import java.util.HashMap;
62import java.util.Iterator;
Jake Hambye994d462014-02-03 13:10:13 -080063import java.util.List;
Gabriel Peal36ebb0d2014-03-20 09:20:43 -070064import java.util.Map;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070065
66/**
67 * Implementation of the ITelephony interface.
68 */
Santos Cordon117fee72014-05-16 17:56:12 -070069public class PhoneInterfaceManager extends ITelephony.Stub {
Santos Cordon7d4ddf62013-07-10 11:58:08 -070070 private static final String LOG_TAG = "PhoneInterfaceManager";
71 private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
72 private static final boolean DBG_LOC = false;
73
74 // Message codes used with mMainThreadHandler
75 private static final int CMD_HANDLE_PIN_MMI = 1;
76 private static final int CMD_HANDLE_NEIGHBORING_CELL = 2;
77 private static final int EVENT_NEIGHBORING_CELL_DONE = 3;
78 private static final int CMD_ANSWER_RINGING_CALL = 4;
79 private static final int CMD_END_CALL = 5; // not used yet
80 private static final int CMD_SILENCE_RINGER = 6;
Shishir Agrawal566b7612013-10-28 14:41:00 -070081 private static final int CMD_TRANSMIT_APDU = 7;
82 private static final int EVENT_TRANSMIT_APDU_DONE = 8;
83 private static final int CMD_OPEN_CHANNEL = 9;
84 private static final int EVENT_OPEN_CHANNEL_DONE = 10;
85 private static final int CMD_CLOSE_CHANNEL = 11;
86 private static final int EVENT_CLOSE_CHANNEL_DONE = 12;
Jake Hambye994d462014-02-03 13:10:13 -080087 private static final int CMD_NV_READ_ITEM = 13;
88 private static final int EVENT_NV_READ_ITEM_DONE = 14;
89 private static final int CMD_NV_WRITE_ITEM = 15;
90 private static final int EVENT_NV_WRITE_ITEM_DONE = 16;
91 private static final int CMD_NV_WRITE_CDMA_PRL = 17;
92 private static final int EVENT_NV_WRITE_CDMA_PRL_DONE = 18;
93 private static final int CMD_NV_RESET_CONFIG = 19;
94 private static final int EVENT_NV_RESET_CONFIG_DONE = 20;
Jake Hamby7c27be32014-03-03 13:25:59 -080095 private static final int CMD_GET_PREFERRED_NETWORK_TYPE = 21;
96 private static final int EVENT_GET_PREFERRED_NETWORK_TYPE_DONE = 22;
97 private static final int CMD_SET_PREFERRED_NETWORK_TYPE = 23;
98 private static final int EVENT_SET_PREFERRED_NETWORK_TYPE_DONE = 24;
Sailesh Nepal35b59452014-03-06 09:26:56 -080099 private static final int CMD_SEND_ENVELOPE = 25;
100 private static final int EVENT_SEND_ENVELOPE_DONE = 26;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700101
102 /** The singleton instance. */
103 private static PhoneInterfaceManager sInstance;
104
105 PhoneGlobals mApp;
106 Phone mPhone;
107 CallManager mCM;
108 AppOpsManager mAppOps;
109 MainThreadHandler mMainThreadHandler;
Santos Cordon406c0342013-08-28 00:07:47 -0700110 CallHandlerServiceProxy mCallHandlerService;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700111
112 /**
Shishir Agrawal566b7612013-10-28 14:41:00 -0700113 * A request object to use for transmitting data to an ICC.
114 */
115 private static final class IccAPDUArgument {
116 public int channel, cla, command, p1, p2, p3;
117 public String data;
118
119 public IccAPDUArgument(int channel, int cla, int command,
120 int p1, int p2, int p3, String data) {
121 this.channel = channel;
122 this.cla = cla;
123 this.command = command;
124 this.p1 = p1;
125 this.p2 = p2;
126 this.p3 = p3;
127 this.data = data;
128 }
129 }
130
131 /**
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700132 * A request object for use with {@link MainThreadHandler}. Requesters should wait() on the
133 * request after sending. The main thread will notify the request when it is complete.
134 */
135 private static final class MainThreadRequest {
136 /** The argument to use for the request */
137 public Object argument;
138 /** The result of the request that is run on the main thread */
139 public Object result;
140
141 public MainThreadRequest(Object argument) {
142 this.argument = argument;
143 }
144 }
145
Sailesh Nepalcc0375f2013-11-13 09:15:18 -0800146 private static final class IncomingThirdPartyCallArgs {
147 public final ComponentName component;
148 public final String callId;
149 public final String callerDisplayName;
150
151 public IncomingThirdPartyCallArgs(ComponentName component, String callId,
152 String callerDisplayName) {
153 this.component = component;
154 this.callId = callId;
155 this.callerDisplayName = callerDisplayName;
156 }
157 }
158
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700159 /**
160 * A handler that processes messages on the main thread in the phone process. Since many
161 * of the Phone calls are not thread safe this is needed to shuttle the requests from the
162 * inbound binder threads to the main thread in the phone process. The Binder thread
163 * may provide a {@link MainThreadRequest} object in the msg.obj field that they are waiting
164 * on, which will be notified when the operation completes and will contain the result of the
165 * request.
166 *
167 * <p>If a MainThreadRequest object is provided in the msg.obj field,
168 * note that request.result must be set to something non-null for the calling thread to
169 * unblock.
170 */
171 private final class MainThreadHandler extends Handler {
172 @Override
173 public void handleMessage(Message msg) {
174 MainThreadRequest request;
175 Message onCompleted;
176 AsyncResult ar;
177
178 switch (msg.what) {
179 case CMD_HANDLE_PIN_MMI:
180 request = (MainThreadRequest) msg.obj;
Jake Hambye994d462014-02-03 13:10:13 -0800181 request.result = mPhone.handlePinMmi((String) request.argument);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700182 // Wake up the requesting thread
183 synchronized (request) {
184 request.notifyAll();
185 }
186 break;
187
188 case CMD_HANDLE_NEIGHBORING_CELL:
189 request = (MainThreadRequest) msg.obj;
190 onCompleted = obtainMessage(EVENT_NEIGHBORING_CELL_DONE,
191 request);
192 mPhone.getNeighboringCids(onCompleted);
193 break;
194
195 case EVENT_NEIGHBORING_CELL_DONE:
196 ar = (AsyncResult) msg.obj;
197 request = (MainThreadRequest) ar.userObj;
198 if (ar.exception == null && ar.result != null) {
199 request.result = ar.result;
200 } else {
201 // create an empty list to notify the waiting thread
Jake Hambye994d462014-02-03 13:10:13 -0800202 request.result = new ArrayList<NeighboringCellInfo>(0);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700203 }
204 // Wake up the requesting thread
205 synchronized (request) {
206 request.notifyAll();
207 }
208 break;
209
210 case CMD_ANSWER_RINGING_CALL:
211 answerRingingCallInternal();
212 break;
213
214 case CMD_SILENCE_RINGER:
215 silenceRingerInternal();
216 break;
217
218 case CMD_END_CALL:
219 request = (MainThreadRequest) msg.obj;
Jake Hambye994d462014-02-03 13:10:13 -0800220 boolean hungUp;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700221 int phoneType = mPhone.getPhoneType();
222 if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
223 // CDMA: If the user presses the Power button we treat it as
224 // ending the complete call session
225 hungUp = PhoneUtils.hangupRingingAndActive(mPhone);
226 } else if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
227 // GSM: End the call as per the Phone state
228 hungUp = PhoneUtils.hangup(mCM);
229 } else {
230 throw new IllegalStateException("Unexpected phone type: " + phoneType);
231 }
232 if (DBG) log("CMD_END_CALL: " + (hungUp ? "hung up!" : "no call to hang up"));
233 request.result = hungUp;
234 // Wake up the requesting thread
235 synchronized (request) {
236 request.notifyAll();
237 }
238 break;
239
Shishir Agrawal566b7612013-10-28 14:41:00 -0700240 case CMD_TRANSMIT_APDU:
241 request = (MainThreadRequest) msg.obj;
242 IccAPDUArgument argument = (IccAPDUArgument) request.argument;
243 onCompleted = obtainMessage(EVENT_TRANSMIT_APDU_DONE, request);
244 UiccController.getInstance().getUiccCard().iccTransmitApduLogicalChannel(
245 argument.channel, argument.cla, argument.command,
246 argument.p1, argument.p2, argument.p3, argument.data,
247 onCompleted);
248 break;
249
250 case EVENT_TRANSMIT_APDU_DONE:
251 ar = (AsyncResult) msg.obj;
252 request = (MainThreadRequest) ar.userObj;
253 if (ar.exception == null && ar.result != null) {
254 request.result = ar.result;
255 } else {
256 request.result = new IccIoResult(0x6F, 0, (byte[])null);
257 if (ar.result == null) {
258 loge("iccTransmitApduLogicalChannel: Empty response");
Jake Hambye994d462014-02-03 13:10:13 -0800259 } else if (ar.exception instanceof CommandException) {
Shishir Agrawal566b7612013-10-28 14:41:00 -0700260 loge("iccTransmitApduLogicalChannel: CommandException: " +
Jake Hambye994d462014-02-03 13:10:13 -0800261 ar.exception);
Shishir Agrawal566b7612013-10-28 14:41:00 -0700262 } else {
263 loge("iccTransmitApduLogicalChannel: Unknown exception");
264 }
265 }
266 synchronized (request) {
267 request.notifyAll();
268 }
269 break;
270
Derek Tan4d5e5c12014-02-04 11:54:58 -0800271 case CMD_SEND_ENVELOPE:
272 request = (MainThreadRequest) msg.obj;
273 onCompleted = obtainMessage(EVENT_SEND_ENVELOPE_DONE, request);
Shishir Agrawal9f9877d2014-03-14 09:36:27 -0700274 UiccController.getInstance().getUiccCard().sendEnvelopeWithStatus(
Derek Tan4d5e5c12014-02-04 11:54:58 -0800275 (String)request.argument, onCompleted);
276 break;
277
278 case EVENT_SEND_ENVELOPE_DONE:
279 ar = (AsyncResult) msg.obj;
280 request = (MainThreadRequest) ar.userObj;
Shishir Agrawal9f9877d2014-03-14 09:36:27 -0700281 if (ar.exception == null && ar.result != null) {
282 request.result = ar.result;
Derek Tan4d5e5c12014-02-04 11:54:58 -0800283 } else {
Shishir Agrawal9f9877d2014-03-14 09:36:27 -0700284 request.result = new IccIoResult(0x6F, 0, (byte[])null);
285 if (ar.result == null) {
286 loge("sendEnvelopeWithStatus: Empty response");
287 } else if (ar.exception instanceof CommandException) {
288 loge("sendEnvelopeWithStatus: CommandException: " +
289 ar.exception);
290 } else {
291 loge("sendEnvelopeWithStatus: exception:" + ar.exception);
292 }
Derek Tan4d5e5c12014-02-04 11:54:58 -0800293 }
294 synchronized (request) {
295 request.notifyAll();
296 }
297 break;
298
Shishir Agrawal566b7612013-10-28 14:41:00 -0700299 case CMD_OPEN_CHANNEL:
300 request = (MainThreadRequest) msg.obj;
301 onCompleted = obtainMessage(EVENT_OPEN_CHANNEL_DONE, request);
302 UiccController.getInstance().getUiccCard().iccOpenLogicalChannel(
303 (String)request.argument, onCompleted);
304 break;
305
306 case EVENT_OPEN_CHANNEL_DONE:
307 ar = (AsyncResult) msg.obj;
308 request = (MainThreadRequest) ar.userObj;
309 if (ar.exception == null && ar.result != null) {
Jake Hambye994d462014-02-03 13:10:13 -0800310 request.result = ((int[]) ar.result)[0];
Shishir Agrawal566b7612013-10-28 14:41:00 -0700311 } else {
Jake Hambye994d462014-02-03 13:10:13 -0800312 request.result = -1;
Shishir Agrawal566b7612013-10-28 14:41:00 -0700313 if (ar.result == null) {
314 loge("iccOpenLogicalChannel: Empty response");
Jake Hambye994d462014-02-03 13:10:13 -0800315 } else if (ar.exception instanceof CommandException) {
Shishir Agrawal566b7612013-10-28 14:41:00 -0700316 loge("iccOpenLogicalChannel: CommandException: " +
Jake Hambye994d462014-02-03 13:10:13 -0800317 ar.exception);
Shishir Agrawal566b7612013-10-28 14:41:00 -0700318 } else {
319 loge("iccOpenLogicalChannel: Unknown exception");
320 }
321 }
322 synchronized (request) {
323 request.notifyAll();
324 }
325 break;
326
327 case CMD_CLOSE_CHANNEL:
328 request = (MainThreadRequest) msg.obj;
329 onCompleted = obtainMessage(EVENT_CLOSE_CHANNEL_DONE,
330 request);
331 UiccController.getInstance().getUiccCard().iccCloseLogicalChannel(
Jake Hambye994d462014-02-03 13:10:13 -0800332 (Integer) request.argument,
Shishir Agrawal566b7612013-10-28 14:41:00 -0700333 onCompleted);
334 break;
335
336 case EVENT_CLOSE_CHANNEL_DONE:
Jake Hambye994d462014-02-03 13:10:13 -0800337 handleNullReturnEvent(msg, "iccCloseLogicalChannel");
338 break;
339
340 case CMD_NV_READ_ITEM:
341 request = (MainThreadRequest) msg.obj;
342 onCompleted = obtainMessage(EVENT_NV_READ_ITEM_DONE, request);
343 mPhone.nvReadItem((Integer) request.argument, onCompleted);
344 break;
345
346 case EVENT_NV_READ_ITEM_DONE:
Shishir Agrawal566b7612013-10-28 14:41:00 -0700347 ar = (AsyncResult) msg.obj;
348 request = (MainThreadRequest) ar.userObj;
Jake Hambye994d462014-02-03 13:10:13 -0800349 if (ar.exception == null && ar.result != null) {
350 request.result = ar.result; // String
Shishir Agrawal566b7612013-10-28 14:41:00 -0700351 } else {
Jake Hambye994d462014-02-03 13:10:13 -0800352 request.result = "";
353 if (ar.result == null) {
354 loge("nvReadItem: Empty response");
355 } else if (ar.exception instanceof CommandException) {
356 loge("nvReadItem: CommandException: " +
357 ar.exception);
Shishir Agrawal566b7612013-10-28 14:41:00 -0700358 } else {
Jake Hambye994d462014-02-03 13:10:13 -0800359 loge("nvReadItem: Unknown exception");
Shishir Agrawal566b7612013-10-28 14:41:00 -0700360 }
361 }
362 synchronized (request) {
363 request.notifyAll();
364 }
365 break;
366
Jake Hambye994d462014-02-03 13:10:13 -0800367 case CMD_NV_WRITE_ITEM:
368 request = (MainThreadRequest) msg.obj;
369 onCompleted = obtainMessage(EVENT_NV_WRITE_ITEM_DONE, request);
370 Pair<Integer, String> idValue = (Pair<Integer, String>) request.argument;
371 mPhone.nvWriteItem(idValue.first, idValue.second, onCompleted);
372 break;
373
374 case EVENT_NV_WRITE_ITEM_DONE:
375 handleNullReturnEvent(msg, "nvWriteItem");
376 break;
377
378 case CMD_NV_WRITE_CDMA_PRL:
379 request = (MainThreadRequest) msg.obj;
380 onCompleted = obtainMessage(EVENT_NV_WRITE_CDMA_PRL_DONE, request);
381 mPhone.nvWriteCdmaPrl((byte[]) request.argument, onCompleted);
382 break;
383
384 case EVENT_NV_WRITE_CDMA_PRL_DONE:
385 handleNullReturnEvent(msg, "nvWriteCdmaPrl");
386 break;
387
388 case CMD_NV_RESET_CONFIG:
389 request = (MainThreadRequest) msg.obj;
390 onCompleted = obtainMessage(EVENT_NV_RESET_CONFIG_DONE, request);
391 mPhone.nvResetConfig((Integer) request.argument, onCompleted);
392 break;
393
394 case EVENT_NV_RESET_CONFIG_DONE:
395 handleNullReturnEvent(msg, "nvResetConfig");
396 break;
397
Jake Hamby7c27be32014-03-03 13:25:59 -0800398 case CMD_GET_PREFERRED_NETWORK_TYPE:
399 request = (MainThreadRequest) msg.obj;
400 onCompleted = obtainMessage(EVENT_GET_PREFERRED_NETWORK_TYPE_DONE, request);
401 mPhone.getPreferredNetworkType(onCompleted);
402 break;
403
404 case EVENT_GET_PREFERRED_NETWORK_TYPE_DONE:
405 ar = (AsyncResult) msg.obj;
406 request = (MainThreadRequest) ar.userObj;
407 if (ar.exception == null && ar.result != null) {
408 request.result = ar.result; // Integer
409 } else {
410 request.result = -1;
411 if (ar.result == null) {
412 loge("getPreferredNetworkType: Empty response");
413 } else if (ar.exception instanceof CommandException) {
414 loge("getPreferredNetworkType: CommandException: " +
415 ar.exception);
416 } else {
417 loge("getPreferredNetworkType: Unknown exception");
418 }
419 }
420 synchronized (request) {
421 request.notifyAll();
422 }
423 break;
424
425 case CMD_SET_PREFERRED_NETWORK_TYPE:
426 request = (MainThreadRequest) msg.obj;
427 onCompleted = obtainMessage(EVENT_SET_PREFERRED_NETWORK_TYPE_DONE, request);
428 int networkType = (Integer) request.argument;
429 mPhone.setPreferredNetworkType(networkType, onCompleted);
430 break;
431
432 case EVENT_SET_PREFERRED_NETWORK_TYPE_DONE:
433 handleNullReturnEvent(msg, "setPreferredNetworkType");
434 break;
435
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700436 default:
437 Log.w(LOG_TAG, "MainThreadHandler: unexpected message code: " + msg.what);
438 break;
439 }
440 }
Jake Hambye994d462014-02-03 13:10:13 -0800441
442 private void handleNullReturnEvent(Message msg, String command) {
443 AsyncResult ar = (AsyncResult) msg.obj;
444 MainThreadRequest request = (MainThreadRequest) ar.userObj;
445 if (ar.exception == null) {
446 request.result = true;
447 } else {
448 request.result = false;
449 if (ar.exception instanceof CommandException) {
450 loge(command + ": CommandException: " + ar.exception);
451 } else {
452 loge(command + ": Unknown exception");
453 }
454 }
455 synchronized (request) {
456 request.notifyAll();
457 }
458 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700459 }
460
461 /**
462 * Posts the specified command to be executed on the main thread,
463 * waits for the request to complete, and returns the result.
464 * @see #sendRequestAsync
465 */
466 private Object sendRequest(int command, Object argument) {
467 if (Looper.myLooper() == mMainThreadHandler.getLooper()) {
468 throw new RuntimeException("This method will deadlock if called from the main thread.");
469 }
470
471 MainThreadRequest request = new MainThreadRequest(argument);
472 Message msg = mMainThreadHandler.obtainMessage(command, request);
473 msg.sendToTarget();
474
475 // Wait for the request to complete
476 synchronized (request) {
477 while (request.result == null) {
478 try {
479 request.wait();
480 } catch (InterruptedException e) {
481 // Do nothing, go back and wait until the request is complete
482 }
483 }
484 }
485 return request.result;
486 }
487
488 /**
489 * Asynchronous ("fire and forget") version of sendRequest():
490 * Posts the specified command to be executed on the main thread, and
491 * returns immediately.
492 * @see #sendRequest
493 */
494 private void sendRequestAsync(int command) {
495 mMainThreadHandler.sendEmptyMessage(command);
496 }
497
498 /**
Sailesh Nepalbd76e4e2013-10-27 13:59:44 -0700499 * Same as {@link #sendRequestAsync(int)} except it takes an argument.
500 * @see {@link #sendRequest(int,Object)}
501 */
502 private void sendRequestAsync(int command, Object argument) {
503 MainThreadRequest request = new MainThreadRequest(argument);
504 Message msg = mMainThreadHandler.obtainMessage(command, request);
505 msg.sendToTarget();
506 }
507
508 /**
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700509 * Initialize the singleton PhoneInterfaceManager instance.
510 * This is only done once, at startup, from PhoneApp.onCreate().
511 */
Santos Cordon406c0342013-08-28 00:07:47 -0700512 /* package */ static PhoneInterfaceManager init(PhoneGlobals app, Phone phone,
Santos Cordon117fee72014-05-16 17:56:12 -0700513 CallHandlerServiceProxy callHandlerService) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700514 synchronized (PhoneInterfaceManager.class) {
515 if (sInstance == null) {
Santos Cordon117fee72014-05-16 17:56:12 -0700516 sInstance = new PhoneInterfaceManager(app, phone, callHandlerService);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700517 } else {
518 Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance);
519 }
520 return sInstance;
521 }
522 }
523
524 /** Private constructor; @see init() */
Santos Cordon406c0342013-08-28 00:07:47 -0700525 private PhoneInterfaceManager(PhoneGlobals app, Phone phone,
Santos Cordon117fee72014-05-16 17:56:12 -0700526 CallHandlerServiceProxy callHandlerService) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700527 mApp = app;
528 mPhone = phone;
529 mCM = PhoneGlobals.getInstance().mCM;
530 mAppOps = (AppOpsManager)app.getSystemService(Context.APP_OPS_SERVICE);
531 mMainThreadHandler = new MainThreadHandler();
Santos Cordon406c0342013-08-28 00:07:47 -0700532 mCallHandlerService = callHandlerService;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700533 publish();
534 }
535
536 private void publish() {
537 if (DBG) log("publish: " + this);
538
539 ServiceManager.addService("phone", this);
540 }
541
542 //
543 // Implementation of the ITelephony interface.
544 //
545
546 public void dial(String number) {
547 if (DBG) log("dial: " + number);
548 // No permission check needed here: This is just a wrapper around the
549 // ACTION_DIAL intent, which is available to any app since it puts up
550 // the UI before it does anything.
551
552 String url = createTelUrl(number);
553 if (url == null) {
554 return;
555 }
556
557 // PENDING: should we just silently fail if phone is offhook or ringing?
558 PhoneConstants.State state = mCM.getState();
559 if (state != PhoneConstants.State.OFFHOOK && state != PhoneConstants.State.RINGING) {
560 Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse(url));
561 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
562 mApp.startActivity(intent);
563 }
564 }
565
566 public void call(String callingPackage, String number) {
567 if (DBG) log("call: " + number);
568
569 // This is just a wrapper around the ACTION_CALL intent, but we still
570 // need to do a permission check since we're calling startActivity()
571 // from the context of the phone app.
572 enforceCallPermission();
573
574 if (mAppOps.noteOp(AppOpsManager.OP_CALL_PHONE, Binder.getCallingUid(), callingPackage)
575 != AppOpsManager.MODE_ALLOWED) {
576 return;
577 }
578
579 String url = createTelUrl(number);
580 if (url == null) {
581 return;
582 }
583
584 Intent intent = new Intent(Intent.ACTION_CALL, Uri.parse(url));
585 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
586 mApp.startActivity(intent);
587 }
588
589 private boolean showCallScreenInternal(boolean specifyInitialDialpadState,
Makoto Onukibcf20992013-09-12 17:59:30 -0700590 boolean showDialpad) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700591 if (!PhoneGlobals.sVoiceCapable) {
592 // Never allow the InCallScreen to appear on data-only devices.
593 return false;
594 }
595 if (isIdle()) {
596 return false;
597 }
598 // If the phone isn't idle then go to the in-call screen
599 long callingId = Binder.clearCallingIdentity();
Santos Cordon406c0342013-08-28 00:07:47 -0700600
Makoto Onukibcf20992013-09-12 17:59:30 -0700601 mCallHandlerService.bringToForeground(showDialpad);
Santos Cordon406c0342013-08-28 00:07:47 -0700602
603 Binder.restoreCallingIdentity(callingId);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700604 return true;
605 }
606
607 // Show the in-call screen without specifying the initial dialpad state.
608 public boolean showCallScreen() {
609 return showCallScreenInternal(false, false);
610 }
611
612 // The variation of showCallScreen() that specifies the initial dialpad state.
613 // (Ideally this would be called showCallScreen() too, just with a different
614 // signature, but AIDL doesn't allow that.)
615 public boolean showCallScreenWithDialpad(boolean showDialpad) {
616 return showCallScreenInternal(true, showDialpad);
617 }
618
619 /**
620 * End a call based on call state
621 * @return true is a call was ended
622 */
623 public boolean endCall() {
624 enforceCallPermission();
625 return (Boolean) sendRequest(CMD_END_CALL, null);
626 }
627
628 public void answerRingingCall() {
629 if (DBG) log("answerRingingCall...");
630 // TODO: there should eventually be a separate "ANSWER_PHONE" permission,
631 // but that can probably wait till the big TelephonyManager API overhaul.
632 // For now, protect this call with the MODIFY_PHONE_STATE permission.
633 enforceModifyPermission();
634 sendRequestAsync(CMD_ANSWER_RINGING_CALL);
635 }
636
637 /**
638 * Make the actual telephony calls to implement answerRingingCall().
639 * This should only be called from the main thread of the Phone app.
640 * @see #answerRingingCall
641 *
642 * TODO: it would be nice to return true if we answered the call, or
643 * false if there wasn't actually a ringing incoming call, or some
644 * other error occurred. (In other words, pass back the return value
645 * from PhoneUtils.answerCall() or PhoneUtils.answerAndEndActive().)
646 * But that would require calling this method via sendRequest() rather
647 * than sendRequestAsync(), and right now we don't actually *need* that
648 * return value, so let's just return void for now.
649 */
650 private void answerRingingCallInternal() {
651 final boolean hasRingingCall = !mPhone.getRingingCall().isIdle();
652 if (hasRingingCall) {
653 final boolean hasActiveCall = !mPhone.getForegroundCall().isIdle();
654 final boolean hasHoldingCall = !mPhone.getBackgroundCall().isIdle();
655 if (hasActiveCall && hasHoldingCall) {
656 // Both lines are in use!
657 // TODO: provide a flag to let the caller specify what
658 // policy to use if both lines are in use. (The current
659 // behavior is hardwired to "answer incoming, end ongoing",
660 // which is how the CALL button is specced to behave.)
661 PhoneUtils.answerAndEndActive(mCM, mCM.getFirstActiveRingingCall());
662 return;
663 } else {
664 // answerCall() will automatically hold the current active
665 // call, if there is one.
666 PhoneUtils.answerCall(mCM.getFirstActiveRingingCall());
667 return;
668 }
669 } else {
670 // No call was ringing.
671 return;
672 }
673 }
674
675 public void silenceRinger() {
676 if (DBG) log("silenceRinger...");
677 // TODO: find a more appropriate permission to check here.
678 // (That can probably wait till the big TelephonyManager API overhaul.
679 // For now, protect this call with the MODIFY_PHONE_STATE permission.)
680 enforceModifyPermission();
681 sendRequestAsync(CMD_SILENCE_RINGER);
682 }
683
684 /**
685 * Internal implemenation of silenceRinger().
686 * This should only be called from the main thread of the Phone app.
687 * @see #silenceRinger
688 */
689 private void silenceRingerInternal() {
690 if ((mCM.getState() == PhoneConstants.State.RINGING)
691 && mApp.notifier.isRinging()) {
692 // Ringer is actually playing, so silence it.
693 if (DBG) log("silenceRingerInternal: silencing...");
694 mApp.notifier.silenceRinger();
695 }
696 }
697
698 public boolean isOffhook() {
699 return (mCM.getState() == PhoneConstants.State.OFFHOOK);
700 }
701
702 public boolean isRinging() {
703 return (mCM.getState() == PhoneConstants.State.RINGING);
704 }
705
706 public boolean isIdle() {
707 return (mCM.getState() == PhoneConstants.State.IDLE);
708 }
709
710 public boolean isSimPinEnabled() {
711 enforceReadPermission();
712 return (PhoneGlobals.getInstance().isSimPinEnabled());
713 }
714
715 public boolean supplyPin(String pin) {
Wink Saville9de0f752013-10-22 19:04:03 -0700716 int [] resultArray = supplyPinReportResult(pin);
717 return (resultArray[0] == PhoneConstants.PIN_RESULT_SUCCESS) ? true : false;
718 }
719
720 public boolean supplyPuk(String puk, String pin) {
721 int [] resultArray = supplyPukReportResult(puk, pin);
722 return (resultArray[0] == PhoneConstants.PIN_RESULT_SUCCESS) ? true : false;
723 }
724
725 /** {@hide} */
726 public int[] supplyPinReportResult(String pin) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700727 enforceModifyPermission();
728 final UnlockSim checkSimPin = new UnlockSim(mPhone.getIccCard());
729 checkSimPin.start();
730 return checkSimPin.unlockSim(null, pin);
731 }
732
Wink Saville9de0f752013-10-22 19:04:03 -0700733 /** {@hide} */
734 public int[] supplyPukReportResult(String puk, String pin) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700735 enforceModifyPermission();
736 final UnlockSim checkSimPuk = new UnlockSim(mPhone.getIccCard());
737 checkSimPuk.start();
738 return checkSimPuk.unlockSim(puk, pin);
739 }
740
741 /**
Wink Saville9de0f752013-10-22 19:04:03 -0700742 * Helper thread to turn async call to SimCard#supplyPin into
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700743 * a synchronous one.
744 */
745 private static class UnlockSim extends Thread {
746
747 private final IccCard mSimCard;
748
749 private boolean mDone = false;
Wink Saville9de0f752013-10-22 19:04:03 -0700750 private int mResult = PhoneConstants.PIN_GENERAL_FAILURE;
751 private int mRetryCount = -1;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700752
753 // For replies from SimCard interface
754 private Handler mHandler;
755
756 // For async handler to identify request type
757 private static final int SUPPLY_PIN_COMPLETE = 100;
758
759 public UnlockSim(IccCard simCard) {
760 mSimCard = simCard;
761 }
762
763 @Override
764 public void run() {
765 Looper.prepare();
766 synchronized (UnlockSim.this) {
767 mHandler = new Handler() {
768 @Override
769 public void handleMessage(Message msg) {
770 AsyncResult ar = (AsyncResult) msg.obj;
771 switch (msg.what) {
772 case SUPPLY_PIN_COMPLETE:
773 Log.d(LOG_TAG, "SUPPLY_PIN_COMPLETE");
774 synchronized (UnlockSim.this) {
Wink Saville9de0f752013-10-22 19:04:03 -0700775 mRetryCount = msg.arg1;
776 if (ar.exception != null) {
777 if (ar.exception instanceof CommandException &&
778 ((CommandException)(ar.exception)).getCommandError()
779 == CommandException.Error.PASSWORD_INCORRECT) {
780 mResult = PhoneConstants.PIN_PASSWORD_INCORRECT;
781 } else {
782 mResult = PhoneConstants.PIN_GENERAL_FAILURE;
783 }
784 } else {
785 mResult = PhoneConstants.PIN_RESULT_SUCCESS;
786 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700787 mDone = true;
788 UnlockSim.this.notifyAll();
789 }
790 break;
791 }
792 }
793 };
794 UnlockSim.this.notifyAll();
795 }
796 Looper.loop();
797 }
798
799 /*
800 * Use PIN or PUK to unlock SIM card
801 *
802 * If PUK is null, unlock SIM card with PIN
803 *
804 * If PUK is not null, unlock SIM card with PUK and set PIN code
805 */
Wink Saville9de0f752013-10-22 19:04:03 -0700806 synchronized int[] unlockSim(String puk, String pin) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700807
808 while (mHandler == null) {
809 try {
810 wait();
811 } catch (InterruptedException e) {
812 Thread.currentThread().interrupt();
813 }
814 }
815 Message callback = Message.obtain(mHandler, SUPPLY_PIN_COMPLETE);
816
817 if (puk == null) {
818 mSimCard.supplyPin(pin, callback);
819 } else {
820 mSimCard.supplyPuk(puk, pin, callback);
821 }
822
823 while (!mDone) {
824 try {
825 Log.d(LOG_TAG, "wait for done");
826 wait();
827 } catch (InterruptedException e) {
828 // Restore the interrupted status
829 Thread.currentThread().interrupt();
830 }
831 }
832 Log.d(LOG_TAG, "done");
Wink Saville9de0f752013-10-22 19:04:03 -0700833 int[] resultArray = new int[2];
834 resultArray[0] = mResult;
835 resultArray[1] = mRetryCount;
836 return resultArray;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700837 }
838 }
839
840 public void updateServiceLocation() {
841 // No permission check needed here: this call is harmless, and it's
842 // needed for the ServiceState.requestStateUpdate() call (which is
843 // already intentionally exposed to 3rd parties.)
844 mPhone.updateServiceLocation();
845 }
846
847 public boolean isRadioOn() {
848 return mPhone.getServiceState().getVoiceRegState() != ServiceState.STATE_POWER_OFF;
849 }
850
851 public void toggleRadioOnOff() {
852 enforceModifyPermission();
853 mPhone.setRadioPower(!isRadioOn());
854 }
855 public boolean setRadio(boolean turnOn) {
856 enforceModifyPermission();
857 if ((mPhone.getServiceState().getVoiceRegState() != ServiceState.STATE_POWER_OFF) != turnOn) {
858 toggleRadioOnOff();
859 }
860 return true;
861 }
862 public boolean setRadioPower(boolean turnOn) {
863 enforceModifyPermission();
864 mPhone.setRadioPower(turnOn);
865 return true;
866 }
867
868 public boolean enableDataConnectivity() {
869 enforceModifyPermission();
870 ConnectivityManager cm =
871 (ConnectivityManager)mApp.getSystemService(Context.CONNECTIVITY_SERVICE);
872 cm.setMobileDataEnabled(true);
873 return true;
874 }
875
876 public int enableApnType(String type) {
877 enforceModifyPermission();
878 return mPhone.enableApnType(type);
879 }
880
881 public int disableApnType(String type) {
882 enforceModifyPermission();
883 return mPhone.disableApnType(type);
884 }
885
886 public boolean disableDataConnectivity() {
887 enforceModifyPermission();
888 ConnectivityManager cm =
889 (ConnectivityManager)mApp.getSystemService(Context.CONNECTIVITY_SERVICE);
890 cm.setMobileDataEnabled(false);
891 return true;
892 }
893
894 public boolean isDataConnectivityPossible() {
895 return mPhone.isDataConnectivityPossible();
896 }
897
898 public boolean handlePinMmi(String dialString) {
899 enforceModifyPermission();
900 return (Boolean) sendRequest(CMD_HANDLE_PIN_MMI, dialString);
901 }
902
903 public void cancelMissedCallsNotification() {
904 enforceModifyPermission();
905 mApp.notificationMgr.cancelMissedCallNotification();
906 }
907
908 public int getCallState() {
909 return DefaultPhoneNotifier.convertCallState(mCM.getState());
910 }
911
912 public int getDataState() {
913 return DefaultPhoneNotifier.convertDataState(mPhone.getDataConnectionState());
914 }
915
916 public int getDataActivity() {
917 return DefaultPhoneNotifier.convertDataActivityState(mPhone.getDataActivityState());
918 }
919
920 @Override
921 public Bundle getCellLocation() {
922 try {
923 mApp.enforceCallingOrSelfPermission(
924 android.Manifest.permission.ACCESS_FINE_LOCATION, null);
925 } catch (SecurityException e) {
926 // If we have ACCESS_FINE_LOCATION permission, skip the check for ACCESS_COARSE_LOCATION
927 // A failure should throw the SecurityException from ACCESS_COARSE_LOCATION since this
928 // is the weaker precondition
929 mApp.enforceCallingOrSelfPermission(
930 android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
931 }
932
Jake Hambye994d462014-02-03 13:10:13 -0800933 if (checkIfCallerIsSelfOrForegroundUser()) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700934 if (DBG_LOC) log("getCellLocation: is active user");
935 Bundle data = new Bundle();
936 mPhone.getCellLocation().fillInNotifierBundle(data);
937 return data;
938 } else {
939 if (DBG_LOC) log("getCellLocation: suppress non-active user");
940 return null;
941 }
942 }
943
944 @Override
945 public void enableLocationUpdates() {
946 mApp.enforceCallingOrSelfPermission(
947 android.Manifest.permission.CONTROL_LOCATION_UPDATES, null);
948 mPhone.enableLocationUpdates();
949 }
950
951 @Override
952 public void disableLocationUpdates() {
953 mApp.enforceCallingOrSelfPermission(
954 android.Manifest.permission.CONTROL_LOCATION_UPDATES, null);
955 mPhone.disableLocationUpdates();
956 }
957
958 @Override
959 @SuppressWarnings("unchecked")
960 public List<NeighboringCellInfo> getNeighboringCellInfo(String callingPackage) {
961 try {
962 mApp.enforceCallingOrSelfPermission(
963 android.Manifest.permission.ACCESS_FINE_LOCATION, null);
964 } catch (SecurityException e) {
965 // If we have ACCESS_FINE_LOCATION permission, skip the check
966 // for ACCESS_COARSE_LOCATION
967 // A failure should throw the SecurityException from
968 // ACCESS_COARSE_LOCATION since this is the weaker precondition
969 mApp.enforceCallingOrSelfPermission(
970 android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
971 }
972
973 if (mAppOps.noteOp(AppOpsManager.OP_NEIGHBORING_CELLS, Binder.getCallingUid(),
974 callingPackage) != AppOpsManager.MODE_ALLOWED) {
975 return null;
976 }
Jake Hambye994d462014-02-03 13:10:13 -0800977 if (checkIfCallerIsSelfOrForegroundUser()) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700978 if (DBG_LOC) log("getNeighboringCellInfo: is active user");
979
980 ArrayList<NeighboringCellInfo> cells = null;
981
982 try {
983 cells = (ArrayList<NeighboringCellInfo>) sendRequest(
984 CMD_HANDLE_NEIGHBORING_CELL, null);
985 } catch (RuntimeException e) {
Shishir Agrawal566b7612013-10-28 14:41:00 -0700986 loge("getNeighboringCellInfo " + e);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700987 }
988 return cells;
989 } else {
990 if (DBG_LOC) log("getNeighboringCellInfo: suppress non-active user");
991 return null;
992 }
993 }
994
995
996 @Override
997 public List<CellInfo> getAllCellInfo() {
998 try {
999 mApp.enforceCallingOrSelfPermission(
1000 android.Manifest.permission.ACCESS_FINE_LOCATION, null);
1001 } catch (SecurityException e) {
1002 // If we have ACCESS_FINE_LOCATION permission, skip the check for ACCESS_COARSE_LOCATION
1003 // A failure should throw the SecurityException from ACCESS_COARSE_LOCATION since this
1004 // is the weaker precondition
1005 mApp.enforceCallingOrSelfPermission(
1006 android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
1007 }
1008
Jake Hambye994d462014-02-03 13:10:13 -08001009 if (checkIfCallerIsSelfOrForegroundUser()) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001010 if (DBG_LOC) log("getAllCellInfo: is active user");
1011 return mPhone.getAllCellInfo();
1012 } else {
1013 if (DBG_LOC) log("getAllCellInfo: suppress non-active user");
1014 return null;
1015 }
1016 }
1017
Sailesh Nepalbd76e4e2013-10-27 13:59:44 -07001018 @Override
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001019 public void setCellInfoListRate(int rateInMillis) {
1020 mPhone.setCellInfoListRate(rateInMillis);
1021 }
1022
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001023 //
1024 // Internal helper methods.
1025 //
1026
Jake Hambye994d462014-02-03 13:10:13 -08001027 private static boolean checkIfCallerIsSelfOrForegroundUser() {
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001028 boolean ok;
1029
1030 boolean self = Binder.getCallingUid() == Process.myUid();
1031 if (!self) {
1032 // Get the caller's user id then clear the calling identity
1033 // which will be restored in the finally clause.
1034 int callingUser = UserHandle.getCallingUserId();
1035 long ident = Binder.clearCallingIdentity();
1036
1037 try {
1038 // With calling identity cleared the current user is the foreground user.
1039 int foregroundUser = ActivityManager.getCurrentUser();
1040 ok = (foregroundUser == callingUser);
1041 if (DBG_LOC) {
1042 log("checkIfCallerIsSelfOrForegoundUser: foregroundUser=" + foregroundUser
1043 + " callingUser=" + callingUser + " ok=" + ok);
1044 }
1045 } catch (Exception ex) {
1046 if (DBG_LOC) loge("checkIfCallerIsSelfOrForegoundUser: Exception ex=" + ex);
1047 ok = false;
1048 } finally {
1049 Binder.restoreCallingIdentity(ident);
1050 }
1051 } else {
1052 if (DBG_LOC) log("checkIfCallerIsSelfOrForegoundUser: is self");
1053 ok = true;
1054 }
1055 if (DBG_LOC) log("checkIfCallerIsSelfOrForegoundUser: ret=" + ok);
1056 return ok;
1057 }
1058
1059 /**
1060 * Make sure the caller has the READ_PHONE_STATE permission.
1061 *
1062 * @throws SecurityException if the caller does not have the required permission
1063 */
1064 private void enforceReadPermission() {
1065 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_STATE, null);
1066 }
1067
1068 /**
1069 * Make sure the caller has the MODIFY_PHONE_STATE permission.
1070 *
1071 * @throws SecurityException if the caller does not have the required permission
1072 */
1073 private void enforceModifyPermission() {
1074 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE, null);
1075 }
1076
1077 /**
1078 * Make sure the caller has the CALL_PHONE permission.
1079 *
1080 * @throws SecurityException if the caller does not have the required permission
1081 */
1082 private void enforceCallPermission() {
1083 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.CALL_PHONE, null);
1084 }
1085
Shishir Agrawal566b7612013-10-28 14:41:00 -07001086 /**
Gabriel Peal36ebb0d2014-03-20 09:20:43 -07001087 * Make sure the caller has the READ_PRIVILEGED_PHONE_STATE permission.
1088 *
1089 * @throws SecurityException if the caller does not have the required permission
1090 */
1091 private void enforcePrivilegedPhoneStatePermission() {
1092 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
1093 null);
1094 }
1095
1096 /**
Shishir Agrawal566b7612013-10-28 14:41:00 -07001097 * Make sure the caller has SIM_COMMUNICATION permission.
1098 *
1099 * @throws SecurityException if the caller does not have the required permission.
1100 */
1101 private void enforceSimCommunicationPermission() {
1102 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.SIM_COMMUNICATION, null);
1103 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001104
1105 private String createTelUrl(String number) {
1106 if (TextUtils.isEmpty(number)) {
1107 return null;
1108 }
1109
Jake Hambye994d462014-02-03 13:10:13 -08001110 return "tel:" + number;
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001111 }
1112
Ihab Awadf9e92732013-12-05 18:02:52 -08001113 private static void log(String msg) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001114 Log.d(LOG_TAG, "[PhoneIntfMgr] " + msg);
1115 }
1116
Ihab Awadf9e92732013-12-05 18:02:52 -08001117 private static void loge(String msg) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001118 Log.e(LOG_TAG, "[PhoneIntfMgr] " + msg);
1119 }
1120
1121 public int getActivePhoneType() {
1122 return mPhone.getPhoneType();
1123 }
1124
1125 /**
1126 * Returns the CDMA ERI icon index to display
1127 */
1128 public int getCdmaEriIconIndex() {
1129 return mPhone.getCdmaEriIconIndex();
1130 }
1131
1132 /**
1133 * Returns the CDMA ERI icon mode,
1134 * 0 - ON
1135 * 1 - FLASHING
1136 */
1137 public int getCdmaEriIconMode() {
1138 return mPhone.getCdmaEriIconMode();
1139 }
1140
1141 /**
1142 * Returns the CDMA ERI text,
1143 */
1144 public String getCdmaEriText() {
1145 return mPhone.getCdmaEriText();
1146 }
1147
1148 /**
1149 * Returns true if CDMA provisioning needs to run.
1150 */
1151 public boolean needsOtaServiceProvisioning() {
1152 return mPhone.needsOtaServiceProvisioning();
1153 }
1154
1155 /**
1156 * Returns the unread count of voicemails
1157 */
1158 public int getVoiceMessageCount() {
1159 return mPhone.getVoiceMessageCount();
1160 }
1161
1162 /**
1163 * Returns the data network type
1164 *
1165 * @Deprecated to be removed Q3 2013 use {@link #getDataNetworkType}.
1166 */
1167 @Override
1168 public int getNetworkType() {
1169 return mPhone.getServiceState().getDataNetworkType();
1170 }
1171
1172 /**
1173 * Returns the data network type
1174 */
1175 @Override
1176 public int getDataNetworkType() {
1177 return mPhone.getServiceState().getDataNetworkType();
1178 }
1179
1180 /**
1181 * Returns the data network type
1182 */
1183 @Override
1184 public int getVoiceNetworkType() {
1185 return mPhone.getServiceState().getVoiceNetworkType();
1186 }
1187
1188 /**
1189 * @return true if a ICC card is present
1190 */
1191 public boolean hasIccCard() {
1192 return mPhone.getIccCard().hasIccCard();
1193 }
1194
1195 /**
1196 * Return if the current radio is LTE on CDMA. This
1197 * is a tri-state return value as for a period of time
1198 * the mode may be unknown.
1199 *
1200 * @return {@link Phone#LTE_ON_CDMA_UNKNOWN}, {@link Phone#LTE_ON_CDMA_FALSE}
Jake Hambye994d462014-02-03 13:10:13 -08001201 * or {@link Phone#LTE_ON_CDMA_TRUE}
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001202 */
1203 public int getLteOnCdmaMode() {
1204 return mPhone.getLteOnCdmaMode();
1205 }
Ihab Awadf2177b72013-11-25 13:33:23 -08001206
1207 /**
1208 * @see android.telephony.TelephonyManager.WifiCallingChoices
1209 */
1210 public int getWhenToMakeWifiCalls() {
Sailesh Nepald1e68152013-12-12 19:08:02 -08001211 return Settings.System.getInt(mPhone.getContext().getContentResolver(),
1212 Settings.System.WHEN_TO_MAKE_WIFI_CALLS, getWhenToMakeWifiCallsDefaultPreference());
Ihab Awadf2177b72013-11-25 13:33:23 -08001213 }
1214
1215 /**
1216 * @see android.telephony.TelephonyManager.WifiCallingChoices
1217 */
1218 public void setWhenToMakeWifiCalls(int preference) {
Sailesh Nepald1e68152013-12-12 19:08:02 -08001219 if (DBG) log("setWhenToMakeWifiCallsStr, storing setting = " + preference);
1220 Settings.System.putInt(mPhone.getContext().getContentResolver(),
1221 Settings.System.WHEN_TO_MAKE_WIFI_CALLS, preference);
Ihab Awadf9e92732013-12-05 18:02:52 -08001222 }
1223
Sailesh Nepald1e68152013-12-12 19:08:02 -08001224 private static int getWhenToMakeWifiCallsDefaultPreference() {
1225 // TODO(sail): Use a build property to choose this value.
Evan Charlton9829e882013-12-19 15:30:38 -08001226 return TelephonyManager.WifiCallingChoices.ALWAYS_USE;
Ihab Awadf2177b72013-11-25 13:33:23 -08001227 }
Shishir Agrawal69f68122013-12-16 17:25:49 -08001228
Shishir Agrawal566b7612013-10-28 14:41:00 -07001229 @Override
1230 public int iccOpenLogicalChannel(String AID) {
1231 enforceSimCommunicationPermission();
1232
1233 if (DBG) log("iccOpenLogicalChannel: " + AID);
1234 Integer channel = (Integer)sendRequest(CMD_OPEN_CHANNEL, AID);
1235 if (DBG) log("iccOpenLogicalChannel: " + channel);
Jake Hambye994d462014-02-03 13:10:13 -08001236 return channel;
Shishir Agrawal566b7612013-10-28 14:41:00 -07001237 }
1238
1239 @Override
1240 public boolean iccCloseLogicalChannel(int channel) {
1241 enforceSimCommunicationPermission();
1242
1243 if (DBG) log("iccCloseLogicalChannel: " + channel);
1244 if (channel < 0) {
1245 return false;
1246 }
Jake Hambye994d462014-02-03 13:10:13 -08001247 Boolean success = (Boolean)sendRequest(CMD_CLOSE_CHANNEL, channel);
Shishir Agrawal566b7612013-10-28 14:41:00 -07001248 if (DBG) log("iccCloseLogicalChannel: " + success);
1249 return success;
1250 }
1251
1252 @Override
1253 public String iccTransmitApduLogicalChannel(int channel, int cla,
1254 int command, int p1, int p2, int p3, String data) {
1255 enforceSimCommunicationPermission();
1256
1257 if (DBG) {
1258 log("iccTransmitApduLogicalChannel: chnl=" + channel + " cla=" + cla +
1259 " cmd=" + command + " p1=" + p1 + " p2=" + p2 + " p3=" + p3 +
1260 " data=" + data);
1261 }
1262
1263 if (channel < 0) {
1264 return "";
1265 }
1266
1267 IccIoResult response = (IccIoResult)sendRequest(CMD_TRANSMIT_APDU,
1268 new IccAPDUArgument(channel, cla, command, p1, p2, p3, data));
1269 if (DBG) log("iccTransmitApduLogicalChannel: " + response);
1270
1271 // If the payload is null, there was an error. Indicate that by returning
1272 // an empty string.
1273 if (response.payload == null) {
1274 return "";
1275 }
1276
1277 // Append the returned status code to the end of the response payload.
1278 String s = Integer.toHexString(
1279 (response.sw1 << 8) + response.sw2 + 0x10000).substring(1);
1280 s = IccUtils.bytesToHexString(response.payload) + s;
1281 return s;
1282 }
Jake Hambye994d462014-02-03 13:10:13 -08001283
Evan Charltonc66da362014-05-16 14:06:40 -07001284 @Override
1285 public String sendEnvelopeWithStatus(String content) {
1286 enforceSimCommunicationPermission();
1287
1288 IccIoResult response = (IccIoResult)sendRequest(CMD_SEND_ENVELOPE, content);
1289 if (response.payload == null) {
1290 return "";
1291 }
1292
1293 // Append the returned status code to the end of the response payload.
1294 String s = Integer.toHexString(
1295 (response.sw1 << 8) + response.sw2 + 0x10000).substring(1);
1296 s = IccUtils.bytesToHexString(response.payload) + s;
1297 return s;
1298 }
1299
Jake Hambye994d462014-02-03 13:10:13 -08001300 /**
1301 * Read one of the NV items defined in {@link com.android.internal.telephony.RadioNVItems}
1302 * and {@code ril_nv_items.h}. Used for device configuration by some CDMA operators.
1303 *
1304 * @param itemID the ID of the item to read
1305 * @return the NV item as a String, or null on error.
1306 */
1307 @Override
1308 public String nvReadItem(int itemID) {
1309 enforceModifyPermission();
1310 if (DBG) log("nvReadItem: item " + itemID);
1311 String value = (String) sendRequest(CMD_NV_READ_ITEM, itemID);
1312 if (DBG) log("nvReadItem: item " + itemID + " is \"" + value + '"');
1313 return value;
1314 }
1315
1316 /**
1317 * Write one of the NV items defined in {@link com.android.internal.telephony.RadioNVItems}
1318 * and {@code ril_nv_items.h}. Used for device configuration by some CDMA operators.
1319 *
1320 * @param itemID the ID of the item to read
1321 * @param itemValue the value to write, as a String
1322 * @return true on success; false on any failure
1323 */
1324 @Override
1325 public boolean nvWriteItem(int itemID, String itemValue) {
1326 enforceModifyPermission();
1327 if (DBG) log("nvWriteItem: item " + itemID + " value \"" + itemValue + '"');
1328 Boolean success = (Boolean) sendRequest(CMD_NV_WRITE_ITEM,
1329 new Pair<Integer, String>(itemID, itemValue));
1330 if (DBG) log("nvWriteItem: item " + itemID + ' ' + (success ? "ok" : "fail"));
1331 return success;
1332 }
1333
1334 /**
1335 * Update the CDMA Preferred Roaming List (PRL) in the radio NV storage.
1336 * Used for device configuration by some CDMA operators.
1337 *
1338 * @param preferredRoamingList byte array containing the new PRL
1339 * @return true on success; false on any failure
1340 */
1341 @Override
1342 public boolean nvWriteCdmaPrl(byte[] preferredRoamingList) {
1343 enforceModifyPermission();
1344 if (DBG) log("nvWriteCdmaPrl: value: " + HexDump.toHexString(preferredRoamingList));
1345 Boolean success = (Boolean) sendRequest(CMD_NV_WRITE_CDMA_PRL, preferredRoamingList);
1346 if (DBG) log("nvWriteCdmaPrl: " + (success ? "ok" : "fail"));
1347 return success;
1348 }
1349
1350 /**
1351 * Perform the specified type of NV config reset.
1352 * Used for device configuration by some CDMA operators.
1353 *
1354 * @param resetType the type of reset to perform (1 == factory reset; 2 == NV-only reset)
1355 * @return true on success; false on any failure
1356 */
1357 @Override
1358 public boolean nvResetConfig(int resetType) {
1359 enforceModifyPermission();
1360 if (DBG) log("nvResetConfig: type " + resetType);
1361 Boolean success = (Boolean) sendRequest(CMD_NV_RESET_CONFIG, resetType);
1362 if (DBG) log("nvResetConfig: type " + resetType + ' ' + (success ? "ok" : "fail"));
1363 return success;
1364 }
Jake Hamby7c27be32014-03-03 13:25:59 -08001365
1366 /**
1367 * Get the preferred network type.
1368 * Used for device configuration by some CDMA operators.
1369 *
1370 * @return the preferred network type, defined in RILConstants.java.
1371 */
1372 @Override
1373 public int getPreferredNetworkType() {
1374 enforceModifyPermission();
1375 if (DBG) log("getPreferredNetworkType");
1376 int[] result = (int[]) sendRequest(CMD_GET_PREFERRED_NETWORK_TYPE, null);
1377 int networkType = (result != null ? result[0] : -1);
1378 if (DBG) log("getPreferredNetworkType: " + networkType);
1379 return networkType;
1380 }
1381
1382 /**
1383 * Set the preferred network type.
1384 * Used for device configuration by some CDMA operators.
1385 *
1386 * @param networkType the preferred network type, defined in RILConstants.java.
1387 * @return true on success; false on any failure.
1388 */
1389 @Override
1390 public boolean setPreferredNetworkType(int networkType) {
1391 enforceModifyPermission();
1392 if (DBG) log("setPreferredNetworkType: type " + networkType);
1393 Boolean success = (Boolean) sendRequest(CMD_SET_PREFERRED_NETWORK_TYPE, networkType);
1394 if (DBG) log("setPreferredNetworkType: " + (success ? "ok" : "fail"));
1395 return success;
1396 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001397}