blob: 9e891e238591c1714098b5f3753a4953c7e61426 [file] [log] [blame]
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001/*
2 * Copyright (C) 2006 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.phone;
18
19import android.app.ActivityManager;
20import android.app.AppOpsManager;
Sailesh Nepalbd76e4e2013-10-27 13:59:44 -070021import android.content.ComponentName;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070022import android.content.Context;
23import android.content.Intent;
24import android.net.ConnectivityManager;
25import android.net.Uri;
26import android.os.AsyncResult;
27import android.os.Binder;
28import android.os.Bundle;
29import android.os.Handler;
30import android.os.Looper;
31import android.os.Message;
32import android.os.Process;
33import android.os.ServiceManager;
34import android.os.UserHandle;
Ihab Awadf2177b72013-11-25 13:33:23 -080035import android.provider.Settings;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070036import android.telephony.CellInfo;
Jake Hambye994d462014-02-03 13:10:13 -080037import android.telephony.NeighboringCellInfo;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070038import android.telephony.ServiceState;
Ihab Awadf2177b72013-11-25 13:33:23 -080039import android.telephony.TelephonyManager;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070040import android.text.TextUtils;
41import android.util.Log;
Jake Hambye994d462014-02-03 13:10:13 -080042import android.util.Pair;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070043
Shishir Agrawal566b7612013-10-28 14:41:00 -070044import com.android.internal.telephony.CallManager;
45import com.android.internal.telephony.CommandException;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070046import com.android.internal.telephony.DefaultPhoneNotifier;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070047import com.android.internal.telephony.ITelephony;
Sailesh Nepalbd76e4e2013-10-27 13:59:44 -070048import com.android.internal.telephony.IThirdPartyCallProvider;
Jake Hambye994d462014-02-03 13:10:13 -080049import com.android.internal.telephony.IccCard;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070050import com.android.internal.telephony.Phone;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070051import com.android.internal.telephony.PhoneConstants;
Sailesh Nepalbd76e4e2013-10-27 13:59:44 -070052import com.android.internal.telephony.thirdpartyphone.ThirdPartyPhone;
Shishir Agrawal566b7612013-10-28 14:41:00 -070053import com.android.internal.telephony.uicc.IccIoResult;
54import com.android.internal.telephony.uicc.IccUtils;
55import com.android.internal.telephony.uicc.UiccController;
Jake Hambye994d462014-02-03 13:10:13 -080056import com.android.internal.util.HexDump;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070057
Santos Cordon7d4ddf62013-07-10 11:58:08 -070058import java.util.ArrayList;
Jake Hambye994d462014-02-03 13:10:13 -080059import java.util.List;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070060
61/**
62 * Implementation of the ITelephony interface.
63 */
64public class PhoneInterfaceManager extends ITelephony.Stub {
65 private static final String LOG_TAG = "PhoneInterfaceManager";
66 private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
67 private static final boolean DBG_LOC = false;
68
69 // Message codes used with mMainThreadHandler
70 private static final int CMD_HANDLE_PIN_MMI = 1;
71 private static final int CMD_HANDLE_NEIGHBORING_CELL = 2;
72 private static final int EVENT_NEIGHBORING_CELL_DONE = 3;
73 private static final int CMD_ANSWER_RINGING_CALL = 4;
74 private static final int CMD_END_CALL = 5; // not used yet
75 private static final int CMD_SILENCE_RINGER = 6;
Shishir Agrawal566b7612013-10-28 14:41:00 -070076 private static final int CMD_TRANSMIT_APDU = 7;
77 private static final int EVENT_TRANSMIT_APDU_DONE = 8;
78 private static final int CMD_OPEN_CHANNEL = 9;
79 private static final int EVENT_OPEN_CHANNEL_DONE = 10;
80 private static final int CMD_CLOSE_CHANNEL = 11;
81 private static final int EVENT_CLOSE_CHANNEL_DONE = 12;
Evan Charlton62e3eac2014-02-05 09:17:29 -080082 // TODO: Remove this.
83 private static final int CMD_NEW_INCOMING_THIRD_PARTY_CALL = 100;
Jake Hambye994d462014-02-03 13:10:13 -080084 private static final int CMD_NV_READ_ITEM = 13;
85 private static final int EVENT_NV_READ_ITEM_DONE = 14;
86 private static final int CMD_NV_WRITE_ITEM = 15;
87 private static final int EVENT_NV_WRITE_ITEM_DONE = 16;
88 private static final int CMD_NV_WRITE_CDMA_PRL = 17;
89 private static final int EVENT_NV_WRITE_CDMA_PRL_DONE = 18;
90 private static final int CMD_NV_RESET_CONFIG = 19;
91 private static final int EVENT_NV_RESET_CONFIG_DONE = 20;
Jake Hamby7c27be32014-03-03 13:25:59 -080092 private static final int CMD_GET_PREFERRED_NETWORK_TYPE = 21;
93 private static final int EVENT_GET_PREFERRED_NETWORK_TYPE_DONE = 22;
94 private static final int CMD_SET_PREFERRED_NETWORK_TYPE = 23;
95 private static final int EVENT_SET_PREFERRED_NETWORK_TYPE_DONE = 24;
Sailesh Nepal35b59452014-03-06 09:26:56 -080096 private static final int CMD_SEND_ENVELOPE = 25;
97 private static final int EVENT_SEND_ENVELOPE_DONE = 26;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070098
99 /** The singleton instance. */
100 private static PhoneInterfaceManager sInstance;
101
102 PhoneGlobals mApp;
103 Phone mPhone;
104 CallManager mCM;
105 AppOpsManager mAppOps;
106 MainThreadHandler mMainThreadHandler;
Santos Cordon406c0342013-08-28 00:07:47 -0700107 CallHandlerServiceProxy mCallHandlerService;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700108
109 /**
Shishir Agrawal566b7612013-10-28 14:41:00 -0700110 * A request object to use for transmitting data to an ICC.
111 */
112 private static final class IccAPDUArgument {
113 public int channel, cla, command, p1, p2, p3;
114 public String data;
115
116 public IccAPDUArgument(int channel, int cla, int command,
117 int p1, int p2, int p3, String data) {
118 this.channel = channel;
119 this.cla = cla;
120 this.command = command;
121 this.p1 = p1;
122 this.p2 = p2;
123 this.p3 = p3;
124 this.data = data;
125 }
126 }
127
128 /**
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700129 * A request object for use with {@link MainThreadHandler}. Requesters should wait() on the
130 * request after sending. The main thread will notify the request when it is complete.
131 */
132 private static final class MainThreadRequest {
133 /** The argument to use for the request */
134 public Object argument;
135 /** The result of the request that is run on the main thread */
136 public Object result;
137
138 public MainThreadRequest(Object argument) {
139 this.argument = argument;
140 }
141 }
142
Sailesh Nepalcc0375f2013-11-13 09:15:18 -0800143 private static final class IncomingThirdPartyCallArgs {
144 public final ComponentName component;
145 public final String callId;
146 public final String callerDisplayName;
147
148 public IncomingThirdPartyCallArgs(ComponentName component, String callId,
149 String callerDisplayName) {
150 this.component = component;
151 this.callId = callId;
152 this.callerDisplayName = callerDisplayName;
153 }
154 }
155
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700156 /**
157 * A handler that processes messages on the main thread in the phone process. Since many
158 * of the Phone calls are not thread safe this is needed to shuttle the requests from the
159 * inbound binder threads to the main thread in the phone process. The Binder thread
160 * may provide a {@link MainThreadRequest} object in the msg.obj field that they are waiting
161 * on, which will be notified when the operation completes and will contain the result of the
162 * request.
163 *
164 * <p>If a MainThreadRequest object is provided in the msg.obj field,
165 * note that request.result must be set to something non-null for the calling thread to
166 * unblock.
167 */
168 private final class MainThreadHandler extends Handler {
169 @Override
170 public void handleMessage(Message msg) {
171 MainThreadRequest request;
172 Message onCompleted;
173 AsyncResult ar;
174
175 switch (msg.what) {
176 case CMD_HANDLE_PIN_MMI:
177 request = (MainThreadRequest) msg.obj;
Jake Hambye994d462014-02-03 13:10:13 -0800178 request.result = mPhone.handlePinMmi((String) request.argument);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700179 // Wake up the requesting thread
180 synchronized (request) {
181 request.notifyAll();
182 }
183 break;
184
185 case CMD_HANDLE_NEIGHBORING_CELL:
186 request = (MainThreadRequest) msg.obj;
187 onCompleted = obtainMessage(EVENT_NEIGHBORING_CELL_DONE,
188 request);
189 mPhone.getNeighboringCids(onCompleted);
190 break;
191
192 case EVENT_NEIGHBORING_CELL_DONE:
193 ar = (AsyncResult) msg.obj;
194 request = (MainThreadRequest) ar.userObj;
195 if (ar.exception == null && ar.result != null) {
196 request.result = ar.result;
197 } else {
198 // create an empty list to notify the waiting thread
Jake Hambye994d462014-02-03 13:10:13 -0800199 request.result = new ArrayList<NeighboringCellInfo>(0);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700200 }
201 // Wake up the requesting thread
202 synchronized (request) {
203 request.notifyAll();
204 }
205 break;
206
207 case CMD_ANSWER_RINGING_CALL:
208 answerRingingCallInternal();
209 break;
210
211 case CMD_SILENCE_RINGER:
212 silenceRingerInternal();
213 break;
214
215 case CMD_END_CALL:
216 request = (MainThreadRequest) msg.obj;
Jake Hambye994d462014-02-03 13:10:13 -0800217 boolean hungUp;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700218 int phoneType = mPhone.getPhoneType();
219 if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
220 // CDMA: If the user presses the Power button we treat it as
221 // ending the complete call session
222 hungUp = PhoneUtils.hangupRingingAndActive(mPhone);
223 } else if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
224 // GSM: End the call as per the Phone state
225 hungUp = PhoneUtils.hangup(mCM);
226 } else {
227 throw new IllegalStateException("Unexpected phone type: " + phoneType);
228 }
229 if (DBG) log("CMD_END_CALL: " + (hungUp ? "hung up!" : "no call to hang up"));
230 request.result = hungUp;
231 // Wake up the requesting thread
232 synchronized (request) {
233 request.notifyAll();
234 }
235 break;
236
Sailesh Nepalbd76e4e2013-10-27 13:59:44 -0700237 case CMD_NEW_INCOMING_THIRD_PARTY_CALL: {
238 request = (MainThreadRequest) msg.obj;
Sailesh Nepalcc0375f2013-11-13 09:15:18 -0800239 IncomingThirdPartyCallArgs args = (IncomingThirdPartyCallArgs) request.argument;
Sailesh Nepalbfb68322013-11-07 14:07:41 -0800240 ThirdPartyPhone thirdPartyPhone = (ThirdPartyPhone)
Sailesh Nepalcc0375f2013-11-13 09:15:18 -0800241 PhoneUtils.getThirdPartyPhoneFromComponent(mCM, args.component);
242 thirdPartyPhone.takeIncomingCall(args.callId, args.callerDisplayName);
Sailesh Nepalbd76e4e2013-10-27 13:59:44 -0700243 break;
244 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700245
Shishir Agrawal566b7612013-10-28 14:41:00 -0700246 case CMD_TRANSMIT_APDU:
247 request = (MainThreadRequest) msg.obj;
248 IccAPDUArgument argument = (IccAPDUArgument) request.argument;
249 onCompleted = obtainMessage(EVENT_TRANSMIT_APDU_DONE, request);
250 UiccController.getInstance().getUiccCard().iccTransmitApduLogicalChannel(
251 argument.channel, argument.cla, argument.command,
252 argument.p1, argument.p2, argument.p3, argument.data,
253 onCompleted);
254 break;
255
256 case EVENT_TRANSMIT_APDU_DONE:
257 ar = (AsyncResult) msg.obj;
258 request = (MainThreadRequest) ar.userObj;
259 if (ar.exception == null && ar.result != null) {
260 request.result = ar.result;
261 } else {
262 request.result = new IccIoResult(0x6F, 0, (byte[])null);
263 if (ar.result == null) {
264 loge("iccTransmitApduLogicalChannel: Empty response");
Jake Hambye994d462014-02-03 13:10:13 -0800265 } else if (ar.exception instanceof CommandException) {
Shishir Agrawal566b7612013-10-28 14:41:00 -0700266 loge("iccTransmitApduLogicalChannel: CommandException: " +
Jake Hambye994d462014-02-03 13:10:13 -0800267 ar.exception);
Shishir Agrawal566b7612013-10-28 14:41:00 -0700268 } else {
269 loge("iccTransmitApduLogicalChannel: Unknown exception");
270 }
271 }
272 synchronized (request) {
273 request.notifyAll();
274 }
275 break;
276
Derek Tan4d5e5c12014-02-04 11:54:58 -0800277 case CMD_SEND_ENVELOPE:
278 request = (MainThreadRequest) msg.obj;
279 onCompleted = obtainMessage(EVENT_SEND_ENVELOPE_DONE, request);
Shishir Agrawal9f9877d2014-03-14 09:36:27 -0700280 UiccController.getInstance().getUiccCard().sendEnvelopeWithStatus(
Derek Tan4d5e5c12014-02-04 11:54:58 -0800281 (String)request.argument, onCompleted);
282 break;
283
284 case EVENT_SEND_ENVELOPE_DONE:
285 ar = (AsyncResult) msg.obj;
286 request = (MainThreadRequest) ar.userObj;
Shishir Agrawal9f9877d2014-03-14 09:36:27 -0700287 if (ar.exception == null && ar.result != null) {
288 request.result = ar.result;
Derek Tan4d5e5c12014-02-04 11:54:58 -0800289 } else {
Shishir Agrawal9f9877d2014-03-14 09:36:27 -0700290 request.result = new IccIoResult(0x6F, 0, (byte[])null);
291 if (ar.result == null) {
292 loge("sendEnvelopeWithStatus: Empty response");
293 } else if (ar.exception instanceof CommandException) {
294 loge("sendEnvelopeWithStatus: CommandException: " +
295 ar.exception);
296 } else {
297 loge("sendEnvelopeWithStatus: exception:" + ar.exception);
298 }
Derek Tan4d5e5c12014-02-04 11:54:58 -0800299 }
300 synchronized (request) {
301 request.notifyAll();
302 }
303 break;
304
Shishir Agrawal566b7612013-10-28 14:41:00 -0700305 case CMD_OPEN_CHANNEL:
306 request = (MainThreadRequest) msg.obj;
307 onCompleted = obtainMessage(EVENT_OPEN_CHANNEL_DONE, request);
308 UiccController.getInstance().getUiccCard().iccOpenLogicalChannel(
309 (String)request.argument, onCompleted);
310 break;
311
312 case EVENT_OPEN_CHANNEL_DONE:
313 ar = (AsyncResult) msg.obj;
314 request = (MainThreadRequest) ar.userObj;
315 if (ar.exception == null && ar.result != null) {
Jake Hambye994d462014-02-03 13:10:13 -0800316 request.result = ((int[]) ar.result)[0];
Shishir Agrawal566b7612013-10-28 14:41:00 -0700317 } else {
Jake Hambye994d462014-02-03 13:10:13 -0800318 request.result = -1;
Shishir Agrawal566b7612013-10-28 14:41:00 -0700319 if (ar.result == null) {
320 loge("iccOpenLogicalChannel: Empty response");
Jake Hambye994d462014-02-03 13:10:13 -0800321 } else if (ar.exception instanceof CommandException) {
Shishir Agrawal566b7612013-10-28 14:41:00 -0700322 loge("iccOpenLogicalChannel: CommandException: " +
Jake Hambye994d462014-02-03 13:10:13 -0800323 ar.exception);
Shishir Agrawal566b7612013-10-28 14:41:00 -0700324 } else {
325 loge("iccOpenLogicalChannel: Unknown exception");
326 }
327 }
328 synchronized (request) {
329 request.notifyAll();
330 }
331 break;
332
333 case CMD_CLOSE_CHANNEL:
334 request = (MainThreadRequest) msg.obj;
335 onCompleted = obtainMessage(EVENT_CLOSE_CHANNEL_DONE,
336 request);
337 UiccController.getInstance().getUiccCard().iccCloseLogicalChannel(
Jake Hambye994d462014-02-03 13:10:13 -0800338 (Integer) request.argument,
Shishir Agrawal566b7612013-10-28 14:41:00 -0700339 onCompleted);
340 break;
341
342 case EVENT_CLOSE_CHANNEL_DONE:
Jake Hambye994d462014-02-03 13:10:13 -0800343 handleNullReturnEvent(msg, "iccCloseLogicalChannel");
344 break;
345
346 case CMD_NV_READ_ITEM:
347 request = (MainThreadRequest) msg.obj;
348 onCompleted = obtainMessage(EVENT_NV_READ_ITEM_DONE, request);
349 mPhone.nvReadItem((Integer) request.argument, onCompleted);
350 break;
351
352 case EVENT_NV_READ_ITEM_DONE:
Shishir Agrawal566b7612013-10-28 14:41:00 -0700353 ar = (AsyncResult) msg.obj;
354 request = (MainThreadRequest) ar.userObj;
Jake Hambye994d462014-02-03 13:10:13 -0800355 if (ar.exception == null && ar.result != null) {
356 request.result = ar.result; // String
Shishir Agrawal566b7612013-10-28 14:41:00 -0700357 } else {
Jake Hambye994d462014-02-03 13:10:13 -0800358 request.result = "";
359 if (ar.result == null) {
360 loge("nvReadItem: Empty response");
361 } else if (ar.exception instanceof CommandException) {
362 loge("nvReadItem: CommandException: " +
363 ar.exception);
Shishir Agrawal566b7612013-10-28 14:41:00 -0700364 } else {
Jake Hambye994d462014-02-03 13:10:13 -0800365 loge("nvReadItem: Unknown exception");
Shishir Agrawal566b7612013-10-28 14:41:00 -0700366 }
367 }
368 synchronized (request) {
369 request.notifyAll();
370 }
371 break;
372
Jake Hambye994d462014-02-03 13:10:13 -0800373 case CMD_NV_WRITE_ITEM:
374 request = (MainThreadRequest) msg.obj;
375 onCompleted = obtainMessage(EVENT_NV_WRITE_ITEM_DONE, request);
376 Pair<Integer, String> idValue = (Pair<Integer, String>) request.argument;
377 mPhone.nvWriteItem(idValue.first, idValue.second, onCompleted);
378 break;
379
380 case EVENT_NV_WRITE_ITEM_DONE:
381 handleNullReturnEvent(msg, "nvWriteItem");
382 break;
383
384 case CMD_NV_WRITE_CDMA_PRL:
385 request = (MainThreadRequest) msg.obj;
386 onCompleted = obtainMessage(EVENT_NV_WRITE_CDMA_PRL_DONE, request);
387 mPhone.nvWriteCdmaPrl((byte[]) request.argument, onCompleted);
388 break;
389
390 case EVENT_NV_WRITE_CDMA_PRL_DONE:
391 handleNullReturnEvent(msg, "nvWriteCdmaPrl");
392 break;
393
394 case CMD_NV_RESET_CONFIG:
395 request = (MainThreadRequest) msg.obj;
396 onCompleted = obtainMessage(EVENT_NV_RESET_CONFIG_DONE, request);
397 mPhone.nvResetConfig((Integer) request.argument, onCompleted);
398 break;
399
400 case EVENT_NV_RESET_CONFIG_DONE:
401 handleNullReturnEvent(msg, "nvResetConfig");
402 break;
403
Jake Hamby7c27be32014-03-03 13:25:59 -0800404 case CMD_GET_PREFERRED_NETWORK_TYPE:
405 request = (MainThreadRequest) msg.obj;
406 onCompleted = obtainMessage(EVENT_GET_PREFERRED_NETWORK_TYPE_DONE, request);
407 mPhone.getPreferredNetworkType(onCompleted);
408 break;
409
410 case EVENT_GET_PREFERRED_NETWORK_TYPE_DONE:
411 ar = (AsyncResult) msg.obj;
412 request = (MainThreadRequest) ar.userObj;
413 if (ar.exception == null && ar.result != null) {
414 request.result = ar.result; // Integer
415 } else {
416 request.result = -1;
417 if (ar.result == null) {
418 loge("getPreferredNetworkType: Empty response");
419 } else if (ar.exception instanceof CommandException) {
420 loge("getPreferredNetworkType: CommandException: " +
421 ar.exception);
422 } else {
423 loge("getPreferredNetworkType: Unknown exception");
424 }
425 }
426 synchronized (request) {
427 request.notifyAll();
428 }
429 break;
430
431 case CMD_SET_PREFERRED_NETWORK_TYPE:
432 request = (MainThreadRequest) msg.obj;
433 onCompleted = obtainMessage(EVENT_SET_PREFERRED_NETWORK_TYPE_DONE, request);
434 int networkType = (Integer) request.argument;
435 mPhone.setPreferredNetworkType(networkType, onCompleted);
436 break;
437
438 case EVENT_SET_PREFERRED_NETWORK_TYPE_DONE:
439 handleNullReturnEvent(msg, "setPreferredNetworkType");
440 break;
441
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700442 default:
443 Log.w(LOG_TAG, "MainThreadHandler: unexpected message code: " + msg.what);
444 break;
445 }
446 }
Jake Hambye994d462014-02-03 13:10:13 -0800447
448 private void handleNullReturnEvent(Message msg, String command) {
449 AsyncResult ar = (AsyncResult) msg.obj;
450 MainThreadRequest request = (MainThreadRequest) ar.userObj;
451 if (ar.exception == null) {
452 request.result = true;
453 } else {
454 request.result = false;
455 if (ar.exception instanceof CommandException) {
456 loge(command + ": CommandException: " + ar.exception);
457 } else {
458 loge(command + ": Unknown exception");
459 }
460 }
461 synchronized (request) {
462 request.notifyAll();
463 }
464 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700465 }
466
467 /**
468 * Posts the specified command to be executed on the main thread,
469 * waits for the request to complete, and returns the result.
470 * @see #sendRequestAsync
471 */
472 private Object sendRequest(int command, Object argument) {
473 if (Looper.myLooper() == mMainThreadHandler.getLooper()) {
474 throw new RuntimeException("This method will deadlock if called from the main thread.");
475 }
476
477 MainThreadRequest request = new MainThreadRequest(argument);
478 Message msg = mMainThreadHandler.obtainMessage(command, request);
479 msg.sendToTarget();
480
481 // Wait for the request to complete
482 synchronized (request) {
483 while (request.result == null) {
484 try {
485 request.wait();
486 } catch (InterruptedException e) {
487 // Do nothing, go back and wait until the request is complete
488 }
489 }
490 }
491 return request.result;
492 }
493
494 /**
495 * Asynchronous ("fire and forget") version of sendRequest():
496 * Posts the specified command to be executed on the main thread, and
497 * returns immediately.
498 * @see #sendRequest
499 */
500 private void sendRequestAsync(int command) {
501 mMainThreadHandler.sendEmptyMessage(command);
502 }
503
504 /**
Sailesh Nepalbd76e4e2013-10-27 13:59:44 -0700505 * Same as {@link #sendRequestAsync(int)} except it takes an argument.
506 * @see {@link #sendRequest(int,Object)}
507 */
508 private void sendRequestAsync(int command, Object argument) {
509 MainThreadRequest request = new MainThreadRequest(argument);
510 Message msg = mMainThreadHandler.obtainMessage(command, request);
511 msg.sendToTarget();
512 }
513
514 /**
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700515 * Initialize the singleton PhoneInterfaceManager instance.
516 * This is only done once, at startup, from PhoneApp.onCreate().
517 */
Santos Cordon406c0342013-08-28 00:07:47 -0700518 /* package */ static PhoneInterfaceManager init(PhoneGlobals app, Phone phone,
519 CallHandlerServiceProxy callHandlerService) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700520 synchronized (PhoneInterfaceManager.class) {
521 if (sInstance == null) {
Santos Cordon406c0342013-08-28 00:07:47 -0700522 sInstance = new PhoneInterfaceManager(app, phone, callHandlerService);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700523 } else {
524 Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance);
525 }
526 return sInstance;
527 }
528 }
529
530 /** Private constructor; @see init() */
Santos Cordon406c0342013-08-28 00:07:47 -0700531 private PhoneInterfaceManager(PhoneGlobals app, Phone phone,
532 CallHandlerServiceProxy callHandlerService) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700533 mApp = app;
534 mPhone = phone;
535 mCM = PhoneGlobals.getInstance().mCM;
536 mAppOps = (AppOpsManager)app.getSystemService(Context.APP_OPS_SERVICE);
537 mMainThreadHandler = new MainThreadHandler();
Santos Cordon406c0342013-08-28 00:07:47 -0700538 mCallHandlerService = callHandlerService;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700539 publish();
540 }
541
542 private void publish() {
543 if (DBG) log("publish: " + this);
544
545 ServiceManager.addService("phone", this);
546 }
547
548 //
549 // Implementation of the ITelephony interface.
550 //
551
552 public void dial(String number) {
553 if (DBG) log("dial: " + number);
554 // No permission check needed here: This is just a wrapper around the
555 // ACTION_DIAL intent, which is available to any app since it puts up
556 // the UI before it does anything.
557
558 String url = createTelUrl(number);
559 if (url == null) {
560 return;
561 }
562
563 // PENDING: should we just silently fail if phone is offhook or ringing?
564 PhoneConstants.State state = mCM.getState();
565 if (state != PhoneConstants.State.OFFHOOK && state != PhoneConstants.State.RINGING) {
566 Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse(url));
567 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
568 mApp.startActivity(intent);
569 }
570 }
571
572 public void call(String callingPackage, String number) {
573 if (DBG) log("call: " + number);
574
575 // This is just a wrapper around the ACTION_CALL intent, but we still
576 // need to do a permission check since we're calling startActivity()
577 // from the context of the phone app.
578 enforceCallPermission();
579
580 if (mAppOps.noteOp(AppOpsManager.OP_CALL_PHONE, Binder.getCallingUid(), callingPackage)
581 != AppOpsManager.MODE_ALLOWED) {
582 return;
583 }
584
585 String url = createTelUrl(number);
586 if (url == null) {
587 return;
588 }
589
590 Intent intent = new Intent(Intent.ACTION_CALL, Uri.parse(url));
591 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
592 mApp.startActivity(intent);
593 }
594
595 private boolean showCallScreenInternal(boolean specifyInitialDialpadState,
Makoto Onukibcf20992013-09-12 17:59:30 -0700596 boolean showDialpad) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700597 if (!PhoneGlobals.sVoiceCapable) {
598 // Never allow the InCallScreen to appear on data-only devices.
599 return false;
600 }
601 if (isIdle()) {
602 return false;
603 }
604 // If the phone isn't idle then go to the in-call screen
605 long callingId = Binder.clearCallingIdentity();
Santos Cordon406c0342013-08-28 00:07:47 -0700606
Makoto Onukibcf20992013-09-12 17:59:30 -0700607 mCallHandlerService.bringToForeground(showDialpad);
Santos Cordon406c0342013-08-28 00:07:47 -0700608
609 Binder.restoreCallingIdentity(callingId);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700610 return true;
611 }
612
613 // Show the in-call screen without specifying the initial dialpad state.
614 public boolean showCallScreen() {
615 return showCallScreenInternal(false, false);
616 }
617
618 // The variation of showCallScreen() that specifies the initial dialpad state.
619 // (Ideally this would be called showCallScreen() too, just with a different
620 // signature, but AIDL doesn't allow that.)
621 public boolean showCallScreenWithDialpad(boolean showDialpad) {
622 return showCallScreenInternal(true, showDialpad);
623 }
624
625 /**
626 * End a call based on call state
627 * @return true is a call was ended
628 */
629 public boolean endCall() {
630 enforceCallPermission();
631 return (Boolean) sendRequest(CMD_END_CALL, null);
632 }
633
634 public void answerRingingCall() {
635 if (DBG) log("answerRingingCall...");
636 // TODO: there should eventually be a separate "ANSWER_PHONE" permission,
637 // but that can probably wait till the big TelephonyManager API overhaul.
638 // For now, protect this call with the MODIFY_PHONE_STATE permission.
639 enforceModifyPermission();
640 sendRequestAsync(CMD_ANSWER_RINGING_CALL);
641 }
642
643 /**
644 * Make the actual telephony calls to implement answerRingingCall().
645 * This should only be called from the main thread of the Phone app.
646 * @see #answerRingingCall
647 *
648 * TODO: it would be nice to return true if we answered the call, or
649 * false if there wasn't actually a ringing incoming call, or some
650 * other error occurred. (In other words, pass back the return value
651 * from PhoneUtils.answerCall() or PhoneUtils.answerAndEndActive().)
652 * But that would require calling this method via sendRequest() rather
653 * than sendRequestAsync(), and right now we don't actually *need* that
654 * return value, so let's just return void for now.
655 */
656 private void answerRingingCallInternal() {
657 final boolean hasRingingCall = !mPhone.getRingingCall().isIdle();
658 if (hasRingingCall) {
659 final boolean hasActiveCall = !mPhone.getForegroundCall().isIdle();
660 final boolean hasHoldingCall = !mPhone.getBackgroundCall().isIdle();
661 if (hasActiveCall && hasHoldingCall) {
662 // Both lines are in use!
663 // TODO: provide a flag to let the caller specify what
664 // policy to use if both lines are in use. (The current
665 // behavior is hardwired to "answer incoming, end ongoing",
666 // which is how the CALL button is specced to behave.)
667 PhoneUtils.answerAndEndActive(mCM, mCM.getFirstActiveRingingCall());
668 return;
669 } else {
670 // answerCall() will automatically hold the current active
671 // call, if there is one.
672 PhoneUtils.answerCall(mCM.getFirstActiveRingingCall());
673 return;
674 }
675 } else {
676 // No call was ringing.
677 return;
678 }
679 }
680
681 public void silenceRinger() {
682 if (DBG) log("silenceRinger...");
683 // TODO: find a more appropriate permission to check here.
684 // (That can probably wait till the big TelephonyManager API overhaul.
685 // For now, protect this call with the MODIFY_PHONE_STATE permission.)
686 enforceModifyPermission();
687 sendRequestAsync(CMD_SILENCE_RINGER);
688 }
689
690 /**
691 * Internal implemenation of silenceRinger().
692 * This should only be called from the main thread of the Phone app.
693 * @see #silenceRinger
694 */
695 private void silenceRingerInternal() {
696 if ((mCM.getState() == PhoneConstants.State.RINGING)
697 && mApp.notifier.isRinging()) {
698 // Ringer is actually playing, so silence it.
699 if (DBG) log("silenceRingerInternal: silencing...");
700 mApp.notifier.silenceRinger();
701 }
702 }
703
704 public boolean isOffhook() {
705 return (mCM.getState() == PhoneConstants.State.OFFHOOK);
706 }
707
708 public boolean isRinging() {
709 return (mCM.getState() == PhoneConstants.State.RINGING);
710 }
711
712 public boolean isIdle() {
713 return (mCM.getState() == PhoneConstants.State.IDLE);
714 }
715
716 public boolean isSimPinEnabled() {
717 enforceReadPermission();
718 return (PhoneGlobals.getInstance().isSimPinEnabled());
719 }
720
721 public boolean supplyPin(String pin) {
Wink Saville9de0f752013-10-22 19:04:03 -0700722 int [] resultArray = supplyPinReportResult(pin);
723 return (resultArray[0] == PhoneConstants.PIN_RESULT_SUCCESS) ? true : false;
724 }
725
726 public boolean supplyPuk(String puk, String pin) {
727 int [] resultArray = supplyPukReportResult(puk, pin);
728 return (resultArray[0] == PhoneConstants.PIN_RESULT_SUCCESS) ? true : false;
729 }
730
731 /** {@hide} */
732 public int[] supplyPinReportResult(String pin) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700733 enforceModifyPermission();
734 final UnlockSim checkSimPin = new UnlockSim(mPhone.getIccCard());
735 checkSimPin.start();
736 return checkSimPin.unlockSim(null, pin);
737 }
738
Wink Saville9de0f752013-10-22 19:04:03 -0700739 /** {@hide} */
740 public int[] supplyPukReportResult(String puk, String pin) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700741 enforceModifyPermission();
742 final UnlockSim checkSimPuk = new UnlockSim(mPhone.getIccCard());
743 checkSimPuk.start();
744 return checkSimPuk.unlockSim(puk, pin);
745 }
746
747 /**
Wink Saville9de0f752013-10-22 19:04:03 -0700748 * Helper thread to turn async call to SimCard#supplyPin into
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700749 * a synchronous one.
750 */
751 private static class UnlockSim extends Thread {
752
753 private final IccCard mSimCard;
754
755 private boolean mDone = false;
Wink Saville9de0f752013-10-22 19:04:03 -0700756 private int mResult = PhoneConstants.PIN_GENERAL_FAILURE;
757 private int mRetryCount = -1;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700758
759 // For replies from SimCard interface
760 private Handler mHandler;
761
762 // For async handler to identify request type
763 private static final int SUPPLY_PIN_COMPLETE = 100;
764
765 public UnlockSim(IccCard simCard) {
766 mSimCard = simCard;
767 }
768
769 @Override
770 public void run() {
771 Looper.prepare();
772 synchronized (UnlockSim.this) {
773 mHandler = new Handler() {
774 @Override
775 public void handleMessage(Message msg) {
776 AsyncResult ar = (AsyncResult) msg.obj;
777 switch (msg.what) {
778 case SUPPLY_PIN_COMPLETE:
779 Log.d(LOG_TAG, "SUPPLY_PIN_COMPLETE");
780 synchronized (UnlockSim.this) {
Wink Saville9de0f752013-10-22 19:04:03 -0700781 mRetryCount = msg.arg1;
782 if (ar.exception != null) {
783 if (ar.exception instanceof CommandException &&
784 ((CommandException)(ar.exception)).getCommandError()
785 == CommandException.Error.PASSWORD_INCORRECT) {
786 mResult = PhoneConstants.PIN_PASSWORD_INCORRECT;
787 } else {
788 mResult = PhoneConstants.PIN_GENERAL_FAILURE;
789 }
790 } else {
791 mResult = PhoneConstants.PIN_RESULT_SUCCESS;
792 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700793 mDone = true;
794 UnlockSim.this.notifyAll();
795 }
796 break;
797 }
798 }
799 };
800 UnlockSim.this.notifyAll();
801 }
802 Looper.loop();
803 }
804
805 /*
806 * Use PIN or PUK to unlock SIM card
807 *
808 * If PUK is null, unlock SIM card with PIN
809 *
810 * If PUK is not null, unlock SIM card with PUK and set PIN code
811 */
Wink Saville9de0f752013-10-22 19:04:03 -0700812 synchronized int[] unlockSim(String puk, String pin) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700813
814 while (mHandler == null) {
815 try {
816 wait();
817 } catch (InterruptedException e) {
818 Thread.currentThread().interrupt();
819 }
820 }
821 Message callback = Message.obtain(mHandler, SUPPLY_PIN_COMPLETE);
822
823 if (puk == null) {
824 mSimCard.supplyPin(pin, callback);
825 } else {
826 mSimCard.supplyPuk(puk, pin, callback);
827 }
828
829 while (!mDone) {
830 try {
831 Log.d(LOG_TAG, "wait for done");
832 wait();
833 } catch (InterruptedException e) {
834 // Restore the interrupted status
835 Thread.currentThread().interrupt();
836 }
837 }
838 Log.d(LOG_TAG, "done");
Wink Saville9de0f752013-10-22 19:04:03 -0700839 int[] resultArray = new int[2];
840 resultArray[0] = mResult;
841 resultArray[1] = mRetryCount;
842 return resultArray;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700843 }
844 }
845
846 public void updateServiceLocation() {
847 // No permission check needed here: this call is harmless, and it's
848 // needed for the ServiceState.requestStateUpdate() call (which is
849 // already intentionally exposed to 3rd parties.)
850 mPhone.updateServiceLocation();
851 }
852
853 public boolean isRadioOn() {
854 return mPhone.getServiceState().getVoiceRegState() != ServiceState.STATE_POWER_OFF;
855 }
856
857 public void toggleRadioOnOff() {
858 enforceModifyPermission();
859 mPhone.setRadioPower(!isRadioOn());
860 }
861 public boolean setRadio(boolean turnOn) {
862 enforceModifyPermission();
863 if ((mPhone.getServiceState().getVoiceRegState() != ServiceState.STATE_POWER_OFF) != turnOn) {
864 toggleRadioOnOff();
865 }
866 return true;
867 }
868 public boolean setRadioPower(boolean turnOn) {
869 enforceModifyPermission();
870 mPhone.setRadioPower(turnOn);
871 return true;
872 }
873
874 public boolean enableDataConnectivity() {
875 enforceModifyPermission();
876 ConnectivityManager cm =
877 (ConnectivityManager)mApp.getSystemService(Context.CONNECTIVITY_SERVICE);
878 cm.setMobileDataEnabled(true);
879 return true;
880 }
881
882 public int enableApnType(String type) {
883 enforceModifyPermission();
884 return mPhone.enableApnType(type);
885 }
886
887 public int disableApnType(String type) {
888 enforceModifyPermission();
889 return mPhone.disableApnType(type);
890 }
891
892 public boolean disableDataConnectivity() {
893 enforceModifyPermission();
894 ConnectivityManager cm =
895 (ConnectivityManager)mApp.getSystemService(Context.CONNECTIVITY_SERVICE);
896 cm.setMobileDataEnabled(false);
897 return true;
898 }
899
900 public boolean isDataConnectivityPossible() {
901 return mPhone.isDataConnectivityPossible();
902 }
903
904 public boolean handlePinMmi(String dialString) {
905 enforceModifyPermission();
906 return (Boolean) sendRequest(CMD_HANDLE_PIN_MMI, dialString);
907 }
908
909 public void cancelMissedCallsNotification() {
910 enforceModifyPermission();
911 mApp.notificationMgr.cancelMissedCallNotification();
912 }
913
914 public int getCallState() {
915 return DefaultPhoneNotifier.convertCallState(mCM.getState());
916 }
917
918 public int getDataState() {
919 return DefaultPhoneNotifier.convertDataState(mPhone.getDataConnectionState());
920 }
921
922 public int getDataActivity() {
923 return DefaultPhoneNotifier.convertDataActivityState(mPhone.getDataActivityState());
924 }
925
926 @Override
927 public Bundle getCellLocation() {
928 try {
929 mApp.enforceCallingOrSelfPermission(
930 android.Manifest.permission.ACCESS_FINE_LOCATION, null);
931 } catch (SecurityException e) {
932 // If we have ACCESS_FINE_LOCATION permission, skip the check for ACCESS_COARSE_LOCATION
933 // A failure should throw the SecurityException from ACCESS_COARSE_LOCATION since this
934 // is the weaker precondition
935 mApp.enforceCallingOrSelfPermission(
936 android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
937 }
938
Jake Hambye994d462014-02-03 13:10:13 -0800939 if (checkIfCallerIsSelfOrForegroundUser()) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700940 if (DBG_LOC) log("getCellLocation: is active user");
941 Bundle data = new Bundle();
942 mPhone.getCellLocation().fillInNotifierBundle(data);
943 return data;
944 } else {
945 if (DBG_LOC) log("getCellLocation: suppress non-active user");
946 return null;
947 }
948 }
949
950 @Override
951 public void enableLocationUpdates() {
952 mApp.enforceCallingOrSelfPermission(
953 android.Manifest.permission.CONTROL_LOCATION_UPDATES, null);
954 mPhone.enableLocationUpdates();
955 }
956
957 @Override
958 public void disableLocationUpdates() {
959 mApp.enforceCallingOrSelfPermission(
960 android.Manifest.permission.CONTROL_LOCATION_UPDATES, null);
961 mPhone.disableLocationUpdates();
962 }
963
964 @Override
965 @SuppressWarnings("unchecked")
966 public List<NeighboringCellInfo> getNeighboringCellInfo(String callingPackage) {
967 try {
968 mApp.enforceCallingOrSelfPermission(
969 android.Manifest.permission.ACCESS_FINE_LOCATION, null);
970 } catch (SecurityException e) {
971 // If we have ACCESS_FINE_LOCATION permission, skip the check
972 // for ACCESS_COARSE_LOCATION
973 // A failure should throw the SecurityException from
974 // ACCESS_COARSE_LOCATION since this is the weaker precondition
975 mApp.enforceCallingOrSelfPermission(
976 android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
977 }
978
979 if (mAppOps.noteOp(AppOpsManager.OP_NEIGHBORING_CELLS, Binder.getCallingUid(),
980 callingPackage) != AppOpsManager.MODE_ALLOWED) {
981 return null;
982 }
Jake Hambye994d462014-02-03 13:10:13 -0800983 if (checkIfCallerIsSelfOrForegroundUser()) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700984 if (DBG_LOC) log("getNeighboringCellInfo: is active user");
985
986 ArrayList<NeighboringCellInfo> cells = null;
987
988 try {
989 cells = (ArrayList<NeighboringCellInfo>) sendRequest(
990 CMD_HANDLE_NEIGHBORING_CELL, null);
991 } catch (RuntimeException e) {
Shishir Agrawal566b7612013-10-28 14:41:00 -0700992 loge("getNeighboringCellInfo " + e);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700993 }
994 return cells;
995 } else {
996 if (DBG_LOC) log("getNeighboringCellInfo: suppress non-active user");
997 return null;
998 }
999 }
1000
1001
1002 @Override
1003 public List<CellInfo> getAllCellInfo() {
1004 try {
1005 mApp.enforceCallingOrSelfPermission(
1006 android.Manifest.permission.ACCESS_FINE_LOCATION, null);
1007 } catch (SecurityException e) {
1008 // If we have ACCESS_FINE_LOCATION permission, skip the check for ACCESS_COARSE_LOCATION
1009 // A failure should throw the SecurityException from ACCESS_COARSE_LOCATION since this
1010 // is the weaker precondition
1011 mApp.enforceCallingOrSelfPermission(
1012 android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
1013 }
1014
Jake Hambye994d462014-02-03 13:10:13 -08001015 if (checkIfCallerIsSelfOrForegroundUser()) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001016 if (DBG_LOC) log("getAllCellInfo: is active user");
1017 return mPhone.getAllCellInfo();
1018 } else {
1019 if (DBG_LOC) log("getAllCellInfo: suppress non-active user");
1020 return null;
1021 }
1022 }
1023
Sailesh Nepalbd76e4e2013-10-27 13:59:44 -07001024 @Override
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001025 public void setCellInfoListRate(int rateInMillis) {
1026 mPhone.setCellInfoListRate(rateInMillis);
1027 }
1028
Sailesh Nepalbd76e4e2013-10-27 13:59:44 -07001029 @Override
Sailesh Nepalcc0375f2013-11-13 09:15:18 -08001030 public void newIncomingThirdPartyCall(ComponentName component, String callId,
1031 String callerDisplayName) {
Sailesh Nepalbd76e4e2013-10-27 13:59:44 -07001032 // TODO(sail): Enforce that the component belongs to the calling package.
1033 if (DBG) {
1034 log("newIncomingThirdPartyCall: component: " + component + " callId: " + callId);
1035 }
1036 enforceCallPermission();
Sailesh Nepalcc0375f2013-11-13 09:15:18 -08001037 sendRequestAsync(CMD_NEW_INCOMING_THIRD_PARTY_CALL, new IncomingThirdPartyCallArgs(
1038 component, callId, callerDisplayName));
Sailesh Nepalbd76e4e2013-10-27 13:59:44 -07001039 }
1040
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001041 //
1042 // Internal helper methods.
1043 //
1044
Jake Hambye994d462014-02-03 13:10:13 -08001045 private static boolean checkIfCallerIsSelfOrForegroundUser() {
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001046 boolean ok;
1047
1048 boolean self = Binder.getCallingUid() == Process.myUid();
1049 if (!self) {
1050 // Get the caller's user id then clear the calling identity
1051 // which will be restored in the finally clause.
1052 int callingUser = UserHandle.getCallingUserId();
1053 long ident = Binder.clearCallingIdentity();
1054
1055 try {
1056 // With calling identity cleared the current user is the foreground user.
1057 int foregroundUser = ActivityManager.getCurrentUser();
1058 ok = (foregroundUser == callingUser);
1059 if (DBG_LOC) {
1060 log("checkIfCallerIsSelfOrForegoundUser: foregroundUser=" + foregroundUser
1061 + " callingUser=" + callingUser + " ok=" + ok);
1062 }
1063 } catch (Exception ex) {
1064 if (DBG_LOC) loge("checkIfCallerIsSelfOrForegoundUser: Exception ex=" + ex);
1065 ok = false;
1066 } finally {
1067 Binder.restoreCallingIdentity(ident);
1068 }
1069 } else {
1070 if (DBG_LOC) log("checkIfCallerIsSelfOrForegoundUser: is self");
1071 ok = true;
1072 }
1073 if (DBG_LOC) log("checkIfCallerIsSelfOrForegoundUser: ret=" + ok);
1074 return ok;
1075 }
1076
1077 /**
1078 * Make sure the caller has the READ_PHONE_STATE permission.
1079 *
1080 * @throws SecurityException if the caller does not have the required permission
1081 */
1082 private void enforceReadPermission() {
1083 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_STATE, null);
1084 }
1085
1086 /**
1087 * Make sure the caller has the MODIFY_PHONE_STATE permission.
1088 *
1089 * @throws SecurityException if the caller does not have the required permission
1090 */
1091 private void enforceModifyPermission() {
1092 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE, null);
1093 }
1094
1095 /**
1096 * Make sure the caller has the CALL_PHONE permission.
1097 *
1098 * @throws SecurityException if the caller does not have the required permission
1099 */
1100 private void enforceCallPermission() {
1101 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.CALL_PHONE, null);
1102 }
1103
Shishir Agrawal566b7612013-10-28 14:41:00 -07001104 /**
1105 * Make sure the caller has SIM_COMMUNICATION permission.
1106 *
1107 * @throws SecurityException if the caller does not have the required permission.
1108 */
1109 private void enforceSimCommunicationPermission() {
1110 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.SIM_COMMUNICATION, null);
1111 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001112
1113 private String createTelUrl(String number) {
1114 if (TextUtils.isEmpty(number)) {
1115 return null;
1116 }
1117
Jake Hambye994d462014-02-03 13:10:13 -08001118 return "tel:" + number;
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001119 }
1120
Ihab Awadf9e92732013-12-05 18:02:52 -08001121 private static void log(String msg) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001122 Log.d(LOG_TAG, "[PhoneIntfMgr] " + msg);
1123 }
1124
Ihab Awadf9e92732013-12-05 18:02:52 -08001125 private static void loge(String msg) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001126 Log.e(LOG_TAG, "[PhoneIntfMgr] " + msg);
1127 }
1128
1129 public int getActivePhoneType() {
1130 return mPhone.getPhoneType();
1131 }
1132
1133 /**
1134 * Returns the CDMA ERI icon index to display
1135 */
1136 public int getCdmaEriIconIndex() {
1137 return mPhone.getCdmaEriIconIndex();
1138 }
1139
1140 /**
1141 * Returns the CDMA ERI icon mode,
1142 * 0 - ON
1143 * 1 - FLASHING
1144 */
1145 public int getCdmaEriIconMode() {
1146 return mPhone.getCdmaEriIconMode();
1147 }
1148
1149 /**
1150 * Returns the CDMA ERI text,
1151 */
1152 public String getCdmaEriText() {
1153 return mPhone.getCdmaEriText();
1154 }
1155
1156 /**
1157 * Returns true if CDMA provisioning needs to run.
1158 */
1159 public boolean needsOtaServiceProvisioning() {
1160 return mPhone.needsOtaServiceProvisioning();
1161 }
1162
1163 /**
1164 * Returns the unread count of voicemails
1165 */
1166 public int getVoiceMessageCount() {
1167 return mPhone.getVoiceMessageCount();
1168 }
1169
1170 /**
1171 * Returns the data network type
1172 *
1173 * @Deprecated to be removed Q3 2013 use {@link #getDataNetworkType}.
1174 */
1175 @Override
1176 public int getNetworkType() {
1177 return mPhone.getServiceState().getDataNetworkType();
1178 }
1179
1180 /**
1181 * Returns the data network type
1182 */
1183 @Override
1184 public int getDataNetworkType() {
1185 return mPhone.getServiceState().getDataNetworkType();
1186 }
1187
1188 /**
1189 * Returns the data network type
1190 */
1191 @Override
1192 public int getVoiceNetworkType() {
1193 return mPhone.getServiceState().getVoiceNetworkType();
1194 }
1195
1196 /**
1197 * @return true if a ICC card is present
1198 */
1199 public boolean hasIccCard() {
1200 return mPhone.getIccCard().hasIccCard();
1201 }
1202
1203 /**
1204 * Return if the current radio is LTE on CDMA. This
1205 * is a tri-state return value as for a period of time
1206 * the mode may be unknown.
1207 *
1208 * @return {@link Phone#LTE_ON_CDMA_UNKNOWN}, {@link Phone#LTE_ON_CDMA_FALSE}
Jake Hambye994d462014-02-03 13:10:13 -08001209 * or {@link Phone#LTE_ON_CDMA_TRUE}
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001210 */
1211 public int getLteOnCdmaMode() {
1212 return mPhone.getLteOnCdmaMode();
1213 }
Ihab Awadf2177b72013-11-25 13:33:23 -08001214
1215 /**
1216 * @see android.telephony.TelephonyManager.WifiCallingChoices
1217 */
1218 public int getWhenToMakeWifiCalls() {
Sailesh Nepald1e68152013-12-12 19:08:02 -08001219 return Settings.System.getInt(mPhone.getContext().getContentResolver(),
1220 Settings.System.WHEN_TO_MAKE_WIFI_CALLS, getWhenToMakeWifiCallsDefaultPreference());
Ihab Awadf2177b72013-11-25 13:33:23 -08001221 }
1222
1223 /**
1224 * @see android.telephony.TelephonyManager.WifiCallingChoices
1225 */
1226 public void setWhenToMakeWifiCalls(int preference) {
Sailesh Nepald1e68152013-12-12 19:08:02 -08001227 if (DBG) log("setWhenToMakeWifiCallsStr, storing setting = " + preference);
1228 Settings.System.putInt(mPhone.getContext().getContentResolver(),
1229 Settings.System.WHEN_TO_MAKE_WIFI_CALLS, preference);
Ihab Awadf9e92732013-12-05 18:02:52 -08001230 }
1231
Sailesh Nepald1e68152013-12-12 19:08:02 -08001232 private static int getWhenToMakeWifiCallsDefaultPreference() {
1233 // TODO(sail): Use a build property to choose this value.
Evan Charlton9829e882013-12-19 15:30:38 -08001234 return TelephonyManager.WifiCallingChoices.ALWAYS_USE;
Ihab Awadf2177b72013-11-25 13:33:23 -08001235 }
Shishir Agrawal69f68122013-12-16 17:25:49 -08001236
Shishir Agrawal566b7612013-10-28 14:41:00 -07001237 @Override
1238 public int iccOpenLogicalChannel(String AID) {
1239 enforceSimCommunicationPermission();
1240
1241 if (DBG) log("iccOpenLogicalChannel: " + AID);
1242 Integer channel = (Integer)sendRequest(CMD_OPEN_CHANNEL, AID);
1243 if (DBG) log("iccOpenLogicalChannel: " + channel);
Jake Hambye994d462014-02-03 13:10:13 -08001244 return channel;
Shishir Agrawal566b7612013-10-28 14:41:00 -07001245 }
1246
1247 @Override
1248 public boolean iccCloseLogicalChannel(int channel) {
1249 enforceSimCommunicationPermission();
1250
1251 if (DBG) log("iccCloseLogicalChannel: " + channel);
1252 if (channel < 0) {
1253 return false;
1254 }
Jake Hambye994d462014-02-03 13:10:13 -08001255 Boolean success = (Boolean)sendRequest(CMD_CLOSE_CHANNEL, channel);
Shishir Agrawal566b7612013-10-28 14:41:00 -07001256 if (DBG) log("iccCloseLogicalChannel: " + success);
1257 return success;
1258 }
1259
1260 @Override
1261 public String iccTransmitApduLogicalChannel(int channel, int cla,
1262 int command, int p1, int p2, int p3, String data) {
1263 enforceSimCommunicationPermission();
1264
1265 if (DBG) {
1266 log("iccTransmitApduLogicalChannel: chnl=" + channel + " cla=" + cla +
1267 " cmd=" + command + " p1=" + p1 + " p2=" + p2 + " p3=" + p3 +
1268 " data=" + data);
1269 }
1270
1271 if (channel < 0) {
1272 return "";
1273 }
1274
1275 IccIoResult response = (IccIoResult)sendRequest(CMD_TRANSMIT_APDU,
1276 new IccAPDUArgument(channel, cla, command, p1, p2, p3, data));
1277 if (DBG) log("iccTransmitApduLogicalChannel: " + response);
1278
1279 // If the payload is null, there was an error. Indicate that by returning
1280 // an empty string.
1281 if (response.payload == null) {
1282 return "";
1283 }
1284
1285 // Append the returned status code to the end of the response payload.
1286 String s = Integer.toHexString(
1287 (response.sw1 << 8) + response.sw2 + 0x10000).substring(1);
1288 s = IccUtils.bytesToHexString(response.payload) + s;
1289 return s;
1290 }
Jake Hambye994d462014-02-03 13:10:13 -08001291
Derek Tan4d5e5c12014-02-04 11:54:58 -08001292 @Override
Shishir Agrawal9f9877d2014-03-14 09:36:27 -07001293 public String sendEnvelopeWithStatus(String content) {
Derek Tan4d5e5c12014-02-04 11:54:58 -08001294 enforceSimCommunicationPermission();
1295
Shishir Agrawal9f9877d2014-03-14 09:36:27 -07001296 IccIoResult response = (IccIoResult)sendRequest(CMD_SEND_ENVELOPE, content);
1297 if (response.payload == null) {
1298 return "";
1299 }
Derek Tan4d5e5c12014-02-04 11:54:58 -08001300
Shishir Agrawal9f9877d2014-03-14 09:36:27 -07001301 // Append the returned status code to the end of the response payload.
1302 String s = Integer.toHexString(
1303 (response.sw1 << 8) + response.sw2 + 0x10000).substring(1);
1304 s = IccUtils.bytesToHexString(response.payload) + s;
1305 return s;
Derek Tan4d5e5c12014-02-04 11:54:58 -08001306 }
1307
Jake Hambye994d462014-02-03 13:10:13 -08001308 /**
1309 * Read one of the NV items defined in {@link com.android.internal.telephony.RadioNVItems}
1310 * and {@code ril_nv_items.h}. Used for device configuration by some CDMA operators.
1311 *
1312 * @param itemID the ID of the item to read
1313 * @return the NV item as a String, or null on error.
1314 */
1315 @Override
1316 public String nvReadItem(int itemID) {
1317 enforceModifyPermission();
1318 if (DBG) log("nvReadItem: item " + itemID);
1319 String value = (String) sendRequest(CMD_NV_READ_ITEM, itemID);
1320 if (DBG) log("nvReadItem: item " + itemID + " is \"" + value + '"');
1321 return value;
1322 }
1323
1324 /**
1325 * Write one of the NV items defined in {@link com.android.internal.telephony.RadioNVItems}
1326 * and {@code ril_nv_items.h}. Used for device configuration by some CDMA operators.
1327 *
1328 * @param itemID the ID of the item to read
1329 * @param itemValue the value to write, as a String
1330 * @return true on success; false on any failure
1331 */
1332 @Override
1333 public boolean nvWriteItem(int itemID, String itemValue) {
1334 enforceModifyPermission();
1335 if (DBG) log("nvWriteItem: item " + itemID + " value \"" + itemValue + '"');
1336 Boolean success = (Boolean) sendRequest(CMD_NV_WRITE_ITEM,
1337 new Pair<Integer, String>(itemID, itemValue));
1338 if (DBG) log("nvWriteItem: item " + itemID + ' ' + (success ? "ok" : "fail"));
1339 return success;
1340 }
1341
1342 /**
1343 * Update the CDMA Preferred Roaming List (PRL) in the radio NV storage.
1344 * Used for device configuration by some CDMA operators.
1345 *
1346 * @param preferredRoamingList byte array containing the new PRL
1347 * @return true on success; false on any failure
1348 */
1349 @Override
1350 public boolean nvWriteCdmaPrl(byte[] preferredRoamingList) {
1351 enforceModifyPermission();
1352 if (DBG) log("nvWriteCdmaPrl: value: " + HexDump.toHexString(preferredRoamingList));
1353 Boolean success = (Boolean) sendRequest(CMD_NV_WRITE_CDMA_PRL, preferredRoamingList);
1354 if (DBG) log("nvWriteCdmaPrl: " + (success ? "ok" : "fail"));
1355 return success;
1356 }
1357
1358 /**
1359 * Perform the specified type of NV config reset.
1360 * Used for device configuration by some CDMA operators.
1361 *
1362 * @param resetType the type of reset to perform (1 == factory reset; 2 == NV-only reset)
1363 * @return true on success; false on any failure
1364 */
1365 @Override
1366 public boolean nvResetConfig(int resetType) {
1367 enforceModifyPermission();
1368 if (DBG) log("nvResetConfig: type " + resetType);
1369 Boolean success = (Boolean) sendRequest(CMD_NV_RESET_CONFIG, resetType);
1370 if (DBG) log("nvResetConfig: type " + resetType + ' ' + (success ? "ok" : "fail"));
1371 return success;
1372 }
Jake Hamby7c27be32014-03-03 13:25:59 -08001373
1374 /**
1375 * Get the preferred network type.
1376 * Used for device configuration by some CDMA operators.
1377 *
1378 * @return the preferred network type, defined in RILConstants.java.
1379 */
1380 @Override
1381 public int getPreferredNetworkType() {
1382 enforceModifyPermission();
1383 if (DBG) log("getPreferredNetworkType");
1384 int[] result = (int[]) sendRequest(CMD_GET_PREFERRED_NETWORK_TYPE, null);
1385 int networkType = (result != null ? result[0] : -1);
1386 if (DBG) log("getPreferredNetworkType: " + networkType);
1387 return networkType;
1388 }
1389
1390 /**
1391 * Set the preferred network type.
1392 * Used for device configuration by some CDMA operators.
1393 *
1394 * @param networkType the preferred network type, defined in RILConstants.java.
1395 * @return true on success; false on any failure.
1396 */
1397 @Override
1398 public boolean setPreferredNetworkType(int networkType) {
1399 enforceModifyPermission();
1400 if (DBG) log("setPreferredNetworkType: type " + networkType);
1401 Boolean success = (Boolean) sendRequest(CMD_SET_PREFERRED_NETWORK_TYPE, networkType);
1402 if (DBG) log("setPreferredNetworkType: " + (success ? "ok" : "fail"));
1403 return success;
1404 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001405}