blob: 5a80321df039451ae9035f54c65ff1c9dbed6963 [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;
Gabriel Peal805a72e2014-03-20 09:20:43 -070021import android.bluetooth.IBluetoothHeadsetPhone;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070022import android.content.ActivityNotFoundException;
23import android.content.Context;
24import android.content.Intent;
25import android.net.ConnectivityManager;
26import android.net.Uri;
27import android.os.AsyncResult;
28import android.os.Binder;
29import android.os.Bundle;
30import android.os.Handler;
Gabriel Peal805a72e2014-03-20 09:20:43 -070031import android.os.IBinder;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070032import android.os.Looper;
33import android.os.Message;
34import android.os.Process;
Gabriel Peal805a72e2014-03-20 09:20:43 -070035import android.os.RemoteException;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070036import android.os.ServiceManager;
37import android.os.UserHandle;
Scott Warner0377fa72014-01-22 23:45:48 -050038import android.provider.Settings;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070039import android.telephony.NeighboringCellInfo;
40import android.telephony.CellInfo;
41import android.telephony.ServiceState;
42import android.text.TextUtils;
43import android.util.Log;
44
Gabriel Peal805a72e2014-03-20 09:20:43 -070045import com.android.internal.telephony.CallManager;
46import com.android.internal.telephony.CommandException;
47import com.android.internal.telephony.Connection;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070048import com.android.internal.telephony.DefaultPhoneNotifier;
49import com.android.internal.telephony.IccCard;
50import com.android.internal.telephony.ITelephony;
Gabriel Peal805a72e2014-03-20 09:20:43 -070051import com.android.internal.telephony.ITelephonyListener;
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;
Gabriel Peal805a72e2014-03-20 09:20:43 -070054import com.android.services.telephony.common.Call;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070055
Gabriel Peal805a72e2014-03-20 09:20:43 -070056import com.android.internal.util.HexDump;
Scott Warner0377fa72014-01-22 23:45:48 -050057import com.android.internal.telephony.RILConstants;
Gabriel Peal805a72e2014-03-20 09:20:43 -070058
Santos Cordon7d4ddf62013-07-10 11:58:08 -070059import java.util.ArrayList;
Gabriel Peal805a72e2014-03-20 09:20:43 -070060import java.util.HashMap;
61import java.util.Iterator;
62import java.util.List;
63import java.util.Map;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070064
65/**
66 * Implementation of the ITelephony interface.
67 */
Gabriel Peal805a72e2014-03-20 09:20:43 -070068public class PhoneInterfaceManager extends ITelephony.Stub implements CallModeler.Listener {
Santos Cordon7d4ddf62013-07-10 11:58:08 -070069 private static final String LOG_TAG = "PhoneInterfaceManager";
70 private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
71 private static final boolean DBG_LOC = false;
72
73 // Message codes used with mMainThreadHandler
74 private static final int CMD_HANDLE_PIN_MMI = 1;
75 private static final int CMD_HANDLE_NEIGHBORING_CELL = 2;
76 private static final int EVENT_NEIGHBORING_CELL_DONE = 3;
77 private static final int CMD_ANSWER_RINGING_CALL = 4;
78 private static final int CMD_END_CALL = 5; // not used yet
79 private static final int CMD_SILENCE_RINGER = 6;
Scott Warner0377fa72014-01-22 23:45:48 -050080 private static final int CMD_TOGGLE_LTE = 7; // not used yet
Santos Cordon7d4ddf62013-07-10 11:58:08 -070081
82 /** The singleton instance. */
83 private static PhoneInterfaceManager sInstance;
84
85 PhoneGlobals mApp;
86 Phone mPhone;
87 CallManager mCM;
88 AppOpsManager mAppOps;
89 MainThreadHandler mMainThreadHandler;
Santos Cordon406c0342013-08-28 00:07:47 -070090 CallHandlerServiceProxy mCallHandlerService;
Gabriel Peal805a72e2014-03-20 09:20:43 -070091 CallModeler mCallModeler;
92 DTMFTonePlayer mDtmfTonePlayer;
93 Handler mDtmfStopHandler = new Handler();
94 Runnable mDtmfStopRunnable;
95
96 private final List<ITelephonyListener> mListeners = new ArrayList<ITelephonyListener>();
97 private final Map<IBinder, TelephonyListenerDeathRecipient> mDeathRecipients =
98 new HashMap<IBinder, TelephonyListenerDeathRecipient>();
Santos Cordon7d4ddf62013-07-10 11:58:08 -070099
100 /**
101 * A request object for use with {@link MainThreadHandler}. Requesters should wait() on the
102 * request after sending. The main thread will notify the request when it is complete.
103 */
104 private static final class MainThreadRequest {
105 /** The argument to use for the request */
106 public Object argument;
107 /** The result of the request that is run on the main thread */
108 public Object result;
109
110 public MainThreadRequest(Object argument) {
111 this.argument = argument;
112 }
113 }
114
115 /**
116 * A handler that processes messages on the main thread in the phone process. Since many
117 * of the Phone calls are not thread safe this is needed to shuttle the requests from the
118 * inbound binder threads to the main thread in the phone process. The Binder thread
119 * may provide a {@link MainThreadRequest} object in the msg.obj field that they are waiting
120 * on, which will be notified when the operation completes and will contain the result of the
121 * request.
122 *
123 * <p>If a MainThreadRequest object is provided in the msg.obj field,
124 * note that request.result must be set to something non-null for the calling thread to
125 * unblock.
126 */
127 private final class MainThreadHandler extends Handler {
128 @Override
129 public void handleMessage(Message msg) {
130 MainThreadRequest request;
131 Message onCompleted;
132 AsyncResult ar;
133
134 switch (msg.what) {
135 case CMD_HANDLE_PIN_MMI:
136 request = (MainThreadRequest) msg.obj;
137 request.result = Boolean.valueOf(
138 mPhone.handlePinMmi((String) request.argument));
139 // Wake up the requesting thread
140 synchronized (request) {
141 request.notifyAll();
142 }
143 break;
144
145 case CMD_HANDLE_NEIGHBORING_CELL:
146 request = (MainThreadRequest) msg.obj;
147 onCompleted = obtainMessage(EVENT_NEIGHBORING_CELL_DONE,
148 request);
149 mPhone.getNeighboringCids(onCompleted);
150 break;
151
152 case EVENT_NEIGHBORING_CELL_DONE:
153 ar = (AsyncResult) msg.obj;
154 request = (MainThreadRequest) ar.userObj;
155 if (ar.exception == null && ar.result != null) {
156 request.result = ar.result;
157 } else {
158 // create an empty list to notify the waiting thread
159 request.result = new ArrayList<NeighboringCellInfo>();
160 }
161 // Wake up the requesting thread
162 synchronized (request) {
163 request.notifyAll();
164 }
165 break;
166
167 case CMD_ANSWER_RINGING_CALL:
168 answerRingingCallInternal();
169 break;
170
171 case CMD_SILENCE_RINGER:
172 silenceRingerInternal();
173 break;
174
175 case CMD_END_CALL:
176 request = (MainThreadRequest) msg.obj;
177 boolean hungUp = false;
178 int phoneType = mPhone.getPhoneType();
179 if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
180 // CDMA: If the user presses the Power button we treat it as
181 // ending the complete call session
182 hungUp = PhoneUtils.hangupRingingAndActive(mPhone);
183 } else if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
184 // GSM: End the call as per the Phone state
185 hungUp = PhoneUtils.hangup(mCM);
186 } else {
187 throw new IllegalStateException("Unexpected phone type: " + phoneType);
188 }
189 if (DBG) log("CMD_END_CALL: " + (hungUp ? "hung up!" : "no call to hang up"));
190 request.result = hungUp;
191 // Wake up the requesting thread
192 synchronized (request) {
193 request.notifyAll();
194 }
195 break;
196
197 default:
198 Log.w(LOG_TAG, "MainThreadHandler: unexpected message code: " + msg.what);
199 break;
200 }
201 }
202 }
203
204 /**
205 * Posts the specified command to be executed on the main thread,
206 * waits for the request to complete, and returns the result.
207 * @see #sendRequestAsync
208 */
209 private Object sendRequest(int command, Object argument) {
210 if (Looper.myLooper() == mMainThreadHandler.getLooper()) {
211 throw new RuntimeException("This method will deadlock if called from the main thread.");
212 }
213
214 MainThreadRequest request = new MainThreadRequest(argument);
215 Message msg = mMainThreadHandler.obtainMessage(command, request);
216 msg.sendToTarget();
217
218 // Wait for the request to complete
219 synchronized (request) {
220 while (request.result == null) {
221 try {
222 request.wait();
223 } catch (InterruptedException e) {
224 // Do nothing, go back and wait until the request is complete
225 }
226 }
227 }
228 return request.result;
229 }
230
231 /**
232 * Asynchronous ("fire and forget") version of sendRequest():
233 * Posts the specified command to be executed on the main thread, and
234 * returns immediately.
235 * @see #sendRequest
236 */
237 private void sendRequestAsync(int command) {
238 mMainThreadHandler.sendEmptyMessage(command);
239 }
240
241 /**
242 * Initialize the singleton PhoneInterfaceManager instance.
243 * This is only done once, at startup, from PhoneApp.onCreate().
244 */
Santos Cordon406c0342013-08-28 00:07:47 -0700245 /* package */ static PhoneInterfaceManager init(PhoneGlobals app, Phone phone,
Gabriel Peal805a72e2014-03-20 09:20:43 -0700246 CallHandlerServiceProxy callHandlerService, CallModeler callModeler,
247 DTMFTonePlayer dtmfTonePlayer) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700248 synchronized (PhoneInterfaceManager.class) {
249 if (sInstance == null) {
Gabriel Peal805a72e2014-03-20 09:20:43 -0700250 sInstance = new PhoneInterfaceManager(app, phone, callHandlerService, callModeler,
251 dtmfTonePlayer);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700252 } else {
253 Log.wtf(LOG_TAG, "init() called multiple times! sInstance = " + sInstance);
254 }
255 return sInstance;
256 }
257 }
258
259 /** Private constructor; @see init() */
Santos Cordon406c0342013-08-28 00:07:47 -0700260 private PhoneInterfaceManager(PhoneGlobals app, Phone phone,
Gabriel Peal805a72e2014-03-20 09:20:43 -0700261 CallHandlerServiceProxy callHandlerService, CallModeler callModeler,
262 DTMFTonePlayer dtmfTonePlayer) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700263 mApp = app;
264 mPhone = phone;
265 mCM = PhoneGlobals.getInstance().mCM;
266 mAppOps = (AppOpsManager)app.getSystemService(Context.APP_OPS_SERVICE);
267 mMainThreadHandler = new MainThreadHandler();
Santos Cordon406c0342013-08-28 00:07:47 -0700268 mCallHandlerService = callHandlerService;
Gabriel Peal805a72e2014-03-20 09:20:43 -0700269 mCallModeler = callModeler;
270 mCallModeler.addListener(this);
271 mDtmfTonePlayer = dtmfTonePlayer;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700272 publish();
273 }
274
275 private void publish() {
276 if (DBG) log("publish: " + this);
277
278 ServiceManager.addService("phone", this);
279 }
280
281 //
282 // Implementation of the ITelephony interface.
283 //
284
285 public void dial(String number) {
286 if (DBG) log("dial: " + number);
287 // No permission check needed here: This is just a wrapper around the
288 // ACTION_DIAL intent, which is available to any app since it puts up
289 // the UI before it does anything.
290
291 String url = createTelUrl(number);
292 if (url == null) {
293 return;
294 }
295
296 // PENDING: should we just silently fail if phone is offhook or ringing?
297 PhoneConstants.State state = mCM.getState();
298 if (state != PhoneConstants.State.OFFHOOK && state != PhoneConstants.State.RINGING) {
299 Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse(url));
300 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
301 mApp.startActivity(intent);
302 }
303 }
304
305 public void call(String callingPackage, String number) {
306 if (DBG) log("call: " + number);
307
308 // This is just a wrapper around the ACTION_CALL intent, but we still
309 // need to do a permission check since we're calling startActivity()
310 // from the context of the phone app.
311 enforceCallPermission();
312
313 if (mAppOps.noteOp(AppOpsManager.OP_CALL_PHONE, Binder.getCallingUid(), callingPackage)
314 != AppOpsManager.MODE_ALLOWED) {
315 return;
316 }
317
318 String url = createTelUrl(number);
319 if (url == null) {
320 return;
321 }
322
323 Intent intent = new Intent(Intent.ACTION_CALL, Uri.parse(url));
324 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
325 mApp.startActivity(intent);
326 }
327
Scott Warner0377fa72014-01-22 23:45:48 -0500328 private int getPreferredNetworkMode() {
329 int preferredNetworkMode = RILConstants.PREFERRED_NETWORK_MODE;
330 if (mPhone.getLteOnCdmaMode() == PhoneConstants.LTE_ON_CDMA_TRUE) {
331 preferredNetworkMode = Phone.NT_MODE_GLOBAL;
332 }
333 int network = Settings.Global.getInt(mPhone.getContext().getContentResolver(),
334 Settings.Global.PREFERRED_NETWORK_MODE, preferredNetworkMode);
335 return network;
336 }
337
Ricardo Cerqueiraf7515382012-04-17 01:34:31 +0100338 public void toggleLTE(boolean on) {
Scott Warner0377fa72014-01-22 23:45:48 -0500339 int network = getPreferredNetworkMode();
340 boolean isCdmaDevice = mPhone.getLteOnCdmaMode() == PhoneConstants.LTE_ON_CDMA_TRUE;
341
342 switch (network) {
343 // GSM Devices
344 case Phone.NT_MODE_WCDMA_PREF:
345 case Phone.NT_MODE_GSM_UMTS:
346 network = Phone.NT_MODE_LTE_GSM_WCDMA;
347 break;
348 case Phone.NT_MODE_LTE_GSM_WCDMA:
349 network = Phone.NT_MODE_WCDMA_PREF;
350 break;
351 // GSM and CDMA devices
352 case Phone.NT_MODE_GLOBAL:
353 // Wtf to do here?
354 network = Phone.NT_MODE_LTE_CMDA_EVDO_GSM_WCDMA;
355 break;
356 case Phone.NT_MODE_LTE_CMDA_EVDO_GSM_WCDMA:
357 // Determine the correct network type
358 if (isCdmaDevice) {
359 network = Phone.NT_MODE_CDMA;
360 } else {
361 network = Phone.NT_MODE_WCDMA_PREF;
362 }
363 break;
364 // CDMA Devices
365 case Phone.NT_MODE_CDMA:
366 network = Phone.NT_MODE_LTE_CDMA_AND_EVDO;
367 break;
368 case Phone.NT_MODE_LTE_CDMA_AND_EVDO:
369 network = Phone.NT_MODE_CDMA;
370 break;
Ricardo Cerqueira171ae342014-04-28 11:42:05 +0100371 // TD-SCDMA Devices
372 case Phone.NT_MODE_TD_SCDMA_GSM_WCDMA:
373 network = Phone.NT_MODE_TD_SCDMA_GSM_WCDMA_LTE;
374 break;
375 case Phone.NT_MODE_TD_SCDMA_GSM_WCDMA_LTE:
376 network = Phone.NT_MODE_TD_SCDMA_GSM_WCDMA;
377 break;
Scott Warner0377fa72014-01-22 23:45:48 -0500378 }
379
380 mPhone.setPreferredNetworkType(network,
381 mMainThreadHandler.obtainMessage(CMD_TOGGLE_LTE));
382 android.provider.Settings.Global.putInt(mApp.getContentResolver(),
383 android.provider.Settings.Global.PREFERRED_NETWORK_MODE, network);
Ricardo Cerqueiraf7515382012-04-17 01:34:31 +0100384 }
385
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700386 private boolean showCallScreenInternal(boolean specifyInitialDialpadState,
Makoto Onukibcf20992013-09-12 17:59:30 -0700387 boolean showDialpad) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700388 if (!PhoneGlobals.sVoiceCapable) {
389 // Never allow the InCallScreen to appear on data-only devices.
390 return false;
391 }
392 if (isIdle()) {
393 return false;
394 }
395 // If the phone isn't idle then go to the in-call screen
396 long callingId = Binder.clearCallingIdentity();
Santos Cordon406c0342013-08-28 00:07:47 -0700397
Makoto Onukibcf20992013-09-12 17:59:30 -0700398 mCallHandlerService.bringToForeground(showDialpad);
Santos Cordon406c0342013-08-28 00:07:47 -0700399
400 Binder.restoreCallingIdentity(callingId);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700401 return true;
402 }
403
404 // Show the in-call screen without specifying the initial dialpad state.
405 public boolean showCallScreen() {
406 return showCallScreenInternal(false, false);
407 }
408
409 // The variation of showCallScreen() that specifies the initial dialpad state.
410 // (Ideally this would be called showCallScreen() too, just with a different
411 // signature, but AIDL doesn't allow that.)
412 public boolean showCallScreenWithDialpad(boolean showDialpad) {
413 return showCallScreenInternal(true, showDialpad);
414 }
415
416 /**
417 * End a call based on call state
418 * @return true is a call was ended
419 */
420 public boolean endCall() {
421 enforceCallPermission();
422 return (Boolean) sendRequest(CMD_END_CALL, null);
423 }
424
425 public void answerRingingCall() {
426 if (DBG) log("answerRingingCall...");
427 // TODO: there should eventually be a separate "ANSWER_PHONE" permission,
428 // but that can probably wait till the big TelephonyManager API overhaul.
429 // For now, protect this call with the MODIFY_PHONE_STATE permission.
430 enforceModifyPermission();
431 sendRequestAsync(CMD_ANSWER_RINGING_CALL);
432 }
433
434 /**
435 * Make the actual telephony calls to implement answerRingingCall().
436 * This should only be called from the main thread of the Phone app.
437 * @see #answerRingingCall
438 *
439 * TODO: it would be nice to return true if we answered the call, or
440 * false if there wasn't actually a ringing incoming call, or some
441 * other error occurred. (In other words, pass back the return value
442 * from PhoneUtils.answerCall() or PhoneUtils.answerAndEndActive().)
443 * But that would require calling this method via sendRequest() rather
444 * than sendRequestAsync(), and right now we don't actually *need* that
445 * return value, so let's just return void for now.
446 */
447 private void answerRingingCallInternal() {
448 final boolean hasRingingCall = !mPhone.getRingingCall().isIdle();
449 if (hasRingingCall) {
450 final boolean hasActiveCall = !mPhone.getForegroundCall().isIdle();
451 final boolean hasHoldingCall = !mPhone.getBackgroundCall().isIdle();
452 if (hasActiveCall && hasHoldingCall) {
453 // Both lines are in use!
454 // TODO: provide a flag to let the caller specify what
455 // policy to use if both lines are in use. (The current
456 // behavior is hardwired to "answer incoming, end ongoing",
457 // which is how the CALL button is specced to behave.)
458 PhoneUtils.answerAndEndActive(mCM, mCM.getFirstActiveRingingCall());
459 return;
460 } else {
461 // answerCall() will automatically hold the current active
462 // call, if there is one.
463 PhoneUtils.answerCall(mCM.getFirstActiveRingingCall());
464 return;
465 }
466 } else {
467 // No call was ringing.
468 return;
469 }
470 }
471
472 public void silenceRinger() {
473 if (DBG) log("silenceRinger...");
474 // TODO: find a more appropriate permission to check here.
475 // (That can probably wait till the big TelephonyManager API overhaul.
476 // For now, protect this call with the MODIFY_PHONE_STATE permission.)
477 enforceModifyPermission();
478 sendRequestAsync(CMD_SILENCE_RINGER);
479 }
480
481 /**
482 * Internal implemenation of silenceRinger().
483 * This should only be called from the main thread of the Phone app.
484 * @see #silenceRinger
485 */
486 private void silenceRingerInternal() {
487 if ((mCM.getState() == PhoneConstants.State.RINGING)
488 && mApp.notifier.isRinging()) {
489 // Ringer is actually playing, so silence it.
490 if (DBG) log("silenceRingerInternal: silencing...");
491 mApp.notifier.silenceRinger();
492 }
493 }
494
495 public boolean isOffhook() {
496 return (mCM.getState() == PhoneConstants.State.OFFHOOK);
497 }
498
499 public boolean isRinging() {
500 return (mCM.getState() == PhoneConstants.State.RINGING);
501 }
502
503 public boolean isIdle() {
504 return (mCM.getState() == PhoneConstants.State.IDLE);
505 }
506
507 public boolean isSimPinEnabled() {
508 enforceReadPermission();
509 return (PhoneGlobals.getInstance().isSimPinEnabled());
510 }
511
512 public boolean supplyPin(String pin) {
Wink Saville9de0f752013-10-22 19:04:03 -0700513 int [] resultArray = supplyPinReportResult(pin);
514 return (resultArray[0] == PhoneConstants.PIN_RESULT_SUCCESS) ? true : false;
515 }
516
517 public boolean supplyPuk(String puk, String pin) {
518 int [] resultArray = supplyPukReportResult(puk, pin);
519 return (resultArray[0] == PhoneConstants.PIN_RESULT_SUCCESS) ? true : false;
520 }
521
522 /** {@hide} */
523 public int[] supplyPinReportResult(String pin) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700524 enforceModifyPermission();
525 final UnlockSim checkSimPin = new UnlockSim(mPhone.getIccCard());
526 checkSimPin.start();
527 return checkSimPin.unlockSim(null, pin);
528 }
529
Wink Saville9de0f752013-10-22 19:04:03 -0700530 /** {@hide} */
531 public int[] supplyPukReportResult(String puk, String pin) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700532 enforceModifyPermission();
533 final UnlockSim checkSimPuk = new UnlockSim(mPhone.getIccCard());
534 checkSimPuk.start();
535 return checkSimPuk.unlockSim(puk, pin);
536 }
537
538 /**
Wink Saville9de0f752013-10-22 19:04:03 -0700539 * Helper thread to turn async call to SimCard#supplyPin into
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700540 * a synchronous one.
541 */
542 private static class UnlockSim extends Thread {
543
544 private final IccCard mSimCard;
545
546 private boolean mDone = false;
Wink Saville9de0f752013-10-22 19:04:03 -0700547 private int mResult = PhoneConstants.PIN_GENERAL_FAILURE;
548 private int mRetryCount = -1;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700549
550 // For replies from SimCard interface
551 private Handler mHandler;
552
553 // For async handler to identify request type
554 private static final int SUPPLY_PIN_COMPLETE = 100;
555
556 public UnlockSim(IccCard simCard) {
557 mSimCard = simCard;
558 }
559
560 @Override
561 public void run() {
562 Looper.prepare();
563 synchronized (UnlockSim.this) {
564 mHandler = new Handler() {
565 @Override
566 public void handleMessage(Message msg) {
567 AsyncResult ar = (AsyncResult) msg.obj;
568 switch (msg.what) {
569 case SUPPLY_PIN_COMPLETE:
570 Log.d(LOG_TAG, "SUPPLY_PIN_COMPLETE");
571 synchronized (UnlockSim.this) {
Wink Saville9de0f752013-10-22 19:04:03 -0700572 mRetryCount = msg.arg1;
573 if (ar.exception != null) {
574 if (ar.exception instanceof CommandException &&
575 ((CommandException)(ar.exception)).getCommandError()
576 == CommandException.Error.PASSWORD_INCORRECT) {
577 mResult = PhoneConstants.PIN_PASSWORD_INCORRECT;
578 } else {
579 mResult = PhoneConstants.PIN_GENERAL_FAILURE;
580 }
581 } else {
582 mResult = PhoneConstants.PIN_RESULT_SUCCESS;
583 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700584 mDone = true;
585 UnlockSim.this.notifyAll();
586 }
587 break;
588 }
589 }
590 };
591 UnlockSim.this.notifyAll();
592 }
593 Looper.loop();
594 }
595
596 /*
597 * Use PIN or PUK to unlock SIM card
598 *
599 * If PUK is null, unlock SIM card with PIN
600 *
601 * If PUK is not null, unlock SIM card with PUK and set PIN code
602 */
Wink Saville9de0f752013-10-22 19:04:03 -0700603 synchronized int[] unlockSim(String puk, String pin) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700604
605 while (mHandler == null) {
606 try {
607 wait();
608 } catch (InterruptedException e) {
609 Thread.currentThread().interrupt();
610 }
611 }
612 Message callback = Message.obtain(mHandler, SUPPLY_PIN_COMPLETE);
613
614 if (puk == null) {
615 mSimCard.supplyPin(pin, callback);
616 } else {
617 mSimCard.supplyPuk(puk, pin, callback);
618 }
619
620 while (!mDone) {
621 try {
622 Log.d(LOG_TAG, "wait for done");
623 wait();
624 } catch (InterruptedException e) {
625 // Restore the interrupted status
626 Thread.currentThread().interrupt();
627 }
628 }
629 Log.d(LOG_TAG, "done");
Wink Saville9de0f752013-10-22 19:04:03 -0700630 int[] resultArray = new int[2];
631 resultArray[0] = mResult;
632 resultArray[1] = mRetryCount;
633 return resultArray;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700634 }
635 }
636
637 public void updateServiceLocation() {
638 // No permission check needed here: this call is harmless, and it's
639 // needed for the ServiceState.requestStateUpdate() call (which is
640 // already intentionally exposed to 3rd parties.)
641 mPhone.updateServiceLocation();
642 }
643
644 public boolean isRadioOn() {
645 return mPhone.getServiceState().getVoiceRegState() != ServiceState.STATE_POWER_OFF;
646 }
647
648 public void toggleRadioOnOff() {
649 enforceModifyPermission();
650 mPhone.setRadioPower(!isRadioOn());
651 }
652 public boolean setRadio(boolean turnOn) {
653 enforceModifyPermission();
654 if ((mPhone.getServiceState().getVoiceRegState() != ServiceState.STATE_POWER_OFF) != turnOn) {
655 toggleRadioOnOff();
656 }
657 return true;
658 }
659 public boolean setRadioPower(boolean turnOn) {
660 enforceModifyPermission();
661 mPhone.setRadioPower(turnOn);
662 return true;
663 }
664
665 public boolean enableDataConnectivity() {
666 enforceModifyPermission();
667 ConnectivityManager cm =
668 (ConnectivityManager)mApp.getSystemService(Context.CONNECTIVITY_SERVICE);
669 cm.setMobileDataEnabled(true);
670 return true;
671 }
672
673 public int enableApnType(String type) {
674 enforceModifyPermission();
675 return mPhone.enableApnType(type);
676 }
677
678 public int disableApnType(String type) {
679 enforceModifyPermission();
680 return mPhone.disableApnType(type);
681 }
682
683 public boolean disableDataConnectivity() {
684 enforceModifyPermission();
685 ConnectivityManager cm =
686 (ConnectivityManager)mApp.getSystemService(Context.CONNECTIVITY_SERVICE);
687 cm.setMobileDataEnabled(false);
688 return true;
689 }
690
691 public boolean isDataConnectivityPossible() {
692 return mPhone.isDataConnectivityPossible();
693 }
694
695 public boolean handlePinMmi(String dialString) {
696 enforceModifyPermission();
697 return (Boolean) sendRequest(CMD_HANDLE_PIN_MMI, dialString);
698 }
699
700 public void cancelMissedCallsNotification() {
701 enforceModifyPermission();
702 mApp.notificationMgr.cancelMissedCallNotification();
703 }
704
705 public int getCallState() {
706 return DefaultPhoneNotifier.convertCallState(mCM.getState());
707 }
708
709 public int getDataState() {
710 return DefaultPhoneNotifier.convertDataState(mPhone.getDataConnectionState());
711 }
712
713 public int getDataActivity() {
714 return DefaultPhoneNotifier.convertDataActivityState(mPhone.getDataActivityState());
715 }
716
717 @Override
718 public Bundle getCellLocation() {
719 try {
720 mApp.enforceCallingOrSelfPermission(
721 android.Manifest.permission.ACCESS_FINE_LOCATION, null);
722 } catch (SecurityException e) {
723 // If we have ACCESS_FINE_LOCATION permission, skip the check for ACCESS_COARSE_LOCATION
724 // A failure should throw the SecurityException from ACCESS_COARSE_LOCATION since this
725 // is the weaker precondition
726 mApp.enforceCallingOrSelfPermission(
727 android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
728 }
729
730 if (checkIfCallerIsSelfOrForegoundUser()) {
731 if (DBG_LOC) log("getCellLocation: is active user");
732 Bundle data = new Bundle();
733 mPhone.getCellLocation().fillInNotifierBundle(data);
734 return data;
735 } else {
736 if (DBG_LOC) log("getCellLocation: suppress non-active user");
737 return null;
738 }
739 }
740
741 @Override
742 public void enableLocationUpdates() {
743 mApp.enforceCallingOrSelfPermission(
744 android.Manifest.permission.CONTROL_LOCATION_UPDATES, null);
745 mPhone.enableLocationUpdates();
746 }
747
748 @Override
749 public void disableLocationUpdates() {
750 mApp.enforceCallingOrSelfPermission(
751 android.Manifest.permission.CONTROL_LOCATION_UPDATES, null);
752 mPhone.disableLocationUpdates();
753 }
754
755 @Override
756 @SuppressWarnings("unchecked")
757 public List<NeighboringCellInfo> getNeighboringCellInfo(String callingPackage) {
758 try {
759 mApp.enforceCallingOrSelfPermission(
760 android.Manifest.permission.ACCESS_FINE_LOCATION, null);
761 } catch (SecurityException e) {
762 // If we have ACCESS_FINE_LOCATION permission, skip the check
763 // for ACCESS_COARSE_LOCATION
764 // A failure should throw the SecurityException from
765 // ACCESS_COARSE_LOCATION since this is the weaker precondition
766 mApp.enforceCallingOrSelfPermission(
767 android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
768 }
769
770 if (mAppOps.noteOp(AppOpsManager.OP_NEIGHBORING_CELLS, Binder.getCallingUid(),
771 callingPackage) != AppOpsManager.MODE_ALLOWED) {
772 return null;
773 }
774 if (checkIfCallerIsSelfOrForegoundUser()) {
775 if (DBG_LOC) log("getNeighboringCellInfo: is active user");
776
777 ArrayList<NeighboringCellInfo> cells = null;
778
779 try {
780 cells = (ArrayList<NeighboringCellInfo>) sendRequest(
781 CMD_HANDLE_NEIGHBORING_CELL, null);
782 } catch (RuntimeException e) {
783 Log.e(LOG_TAG, "getNeighboringCellInfo " + e);
784 }
785 return cells;
786 } else {
787 if (DBG_LOC) log("getNeighboringCellInfo: suppress non-active user");
788 return null;
789 }
790 }
791
792
793 @Override
794 public List<CellInfo> getAllCellInfo() {
795 try {
796 mApp.enforceCallingOrSelfPermission(
797 android.Manifest.permission.ACCESS_FINE_LOCATION, null);
798 } catch (SecurityException e) {
799 // If we have ACCESS_FINE_LOCATION permission, skip the check for ACCESS_COARSE_LOCATION
800 // A failure should throw the SecurityException from ACCESS_COARSE_LOCATION since this
801 // is the weaker precondition
802 mApp.enforceCallingOrSelfPermission(
803 android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
804 }
805
806 if (checkIfCallerIsSelfOrForegoundUser()) {
807 if (DBG_LOC) log("getAllCellInfo: is active user");
808 return mPhone.getAllCellInfo();
809 } else {
810 if (DBG_LOC) log("getAllCellInfo: suppress non-active user");
811 return null;
812 }
813 }
814
815 public void setCellInfoListRate(int rateInMillis) {
816 mPhone.setCellInfoListRate(rateInMillis);
817 }
818
819 //
820 // Internal helper methods.
821 //
822
823 private boolean checkIfCallerIsSelfOrForegoundUser() {
824 boolean ok;
825
826 boolean self = Binder.getCallingUid() == Process.myUid();
827 if (!self) {
828 // Get the caller's user id then clear the calling identity
829 // which will be restored in the finally clause.
830 int callingUser = UserHandle.getCallingUserId();
831 long ident = Binder.clearCallingIdentity();
832
833 try {
834 // With calling identity cleared the current user is the foreground user.
835 int foregroundUser = ActivityManager.getCurrentUser();
836 ok = (foregroundUser == callingUser);
837 if (DBG_LOC) {
838 log("checkIfCallerIsSelfOrForegoundUser: foregroundUser=" + foregroundUser
839 + " callingUser=" + callingUser + " ok=" + ok);
840 }
841 } catch (Exception ex) {
842 if (DBG_LOC) loge("checkIfCallerIsSelfOrForegoundUser: Exception ex=" + ex);
843 ok = false;
844 } finally {
845 Binder.restoreCallingIdentity(ident);
846 }
847 } else {
848 if (DBG_LOC) log("checkIfCallerIsSelfOrForegoundUser: is self");
849 ok = true;
850 }
851 if (DBG_LOC) log("checkIfCallerIsSelfOrForegoundUser: ret=" + ok);
852 return ok;
853 }
854
855 /**
856 * Make sure the caller has the READ_PHONE_STATE permission.
857 *
858 * @throws SecurityException if the caller does not have the required permission
859 */
860 private void enforceReadPermission() {
861 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_STATE, null);
862 }
863
864 /**
865 * Make sure the caller has the MODIFY_PHONE_STATE permission.
866 *
867 * @throws SecurityException if the caller does not have the required permission
868 */
869 private void enforceModifyPermission() {
870 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE, null);
871 }
872
873 /**
874 * Make sure the caller has the CALL_PHONE permission.
875 *
876 * @throws SecurityException if the caller does not have the required permission
877 */
878 private void enforceCallPermission() {
879 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.CALL_PHONE, null);
880 }
881
Gabriel Peal805a72e2014-03-20 09:20:43 -0700882 /**
883 * Make sure the caller has the READ_PRIVILEGED_PHONE_STATE permission.
884 *
885 * @throws SecurityException if the caller does not have the required permission
886 */
887 private void enforcePrivilegedPhoneStatePermission() {
888 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
889 null);
890 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700891
892 private String createTelUrl(String number) {
893 if (TextUtils.isEmpty(number)) {
894 return null;
895 }
896
897 StringBuilder buf = new StringBuilder("tel:");
898 buf.append(number);
899 return buf.toString();
900 }
901
902 private void log(String msg) {
903 Log.d(LOG_TAG, "[PhoneIntfMgr] " + msg);
904 }
905
906 private void loge(String msg) {
907 Log.e(LOG_TAG, "[PhoneIntfMgr] " + msg);
908 }
909
910 public int getActivePhoneType() {
911 return mPhone.getPhoneType();
912 }
913
914 /**
915 * Returns the CDMA ERI icon index to display
916 */
917 public int getCdmaEriIconIndex() {
918 return mPhone.getCdmaEriIconIndex();
919 }
920
921 /**
922 * Returns the CDMA ERI icon mode,
923 * 0 - ON
924 * 1 - FLASHING
925 */
926 public int getCdmaEriIconMode() {
927 return mPhone.getCdmaEriIconMode();
928 }
929
930 /**
931 * Returns the CDMA ERI text,
932 */
933 public String getCdmaEriText() {
934 return mPhone.getCdmaEriText();
935 }
936
937 /**
938 * Returns true if CDMA provisioning needs to run.
939 */
940 public boolean needsOtaServiceProvisioning() {
941 return mPhone.needsOtaServiceProvisioning();
942 }
943
944 /**
945 * Returns the unread count of voicemails
946 */
947 public int getVoiceMessageCount() {
948 return mPhone.getVoiceMessageCount();
949 }
950
951 /**
952 * Returns the data network type
953 *
954 * @Deprecated to be removed Q3 2013 use {@link #getDataNetworkType}.
955 */
956 @Override
957 public int getNetworkType() {
958 return mPhone.getServiceState().getDataNetworkType();
959 }
960
961 /**
962 * Returns the data network type
963 */
964 @Override
965 public int getDataNetworkType() {
966 return mPhone.getServiceState().getDataNetworkType();
967 }
968
969 /**
970 * Returns the data network type
971 */
972 @Override
973 public int getVoiceNetworkType() {
974 return mPhone.getServiceState().getVoiceNetworkType();
975 }
976
977 /**
978 * @return true if a ICC card is present
979 */
980 public boolean hasIccCard() {
981 return mPhone.getIccCard().hasIccCard();
982 }
983
984 /**
985 * Return if the current radio is LTE on CDMA. This
986 * is a tri-state return value as for a period of time
987 * the mode may be unknown.
988 *
989 * @return {@link Phone#LTE_ON_CDMA_UNKNOWN}, {@link Phone#LTE_ON_CDMA_FALSE}
990 * or {@link PHone#LTE_ON_CDMA_TRUE}
991 */
992 public int getLteOnCdmaMode() {
993 return mPhone.getLteOnCdmaMode();
994 }
Ricardo Cerqueiraf7515382012-04-17 01:34:31 +0100995
996 public int getLteOnGsmMode() {
997 return mPhone.getLteOnGsmMode();
998 }
Nick Reuter6e917ed2014-06-04 23:07:32 -0500999
Gabriel Peal805a72e2014-03-20 09:20:43 -07001000 @Override
1001 public void toggleHold() {
1002 enforceModifyPermission();
1003
1004 try {
1005 PhoneUtils.switchHoldingAndActive(mCM.getFirstActiveBgCall());
1006 } catch (Exception e) {
1007 Log.e(LOG_TAG, "Error during toggleHold().", e);
1008 }
1009 }
1010
1011 @Override
1012 public void merge() {
1013 enforceModifyPermission();
1014
1015 try {
1016 if (PhoneUtils.okToMergeCalls(mCM)) {
1017 PhoneUtils.mergeCalls(mCM);
1018 }
1019 } catch (Exception e) {
1020 Log.e(LOG_TAG, "Error during merge().", e);
1021 }
1022 }
1023
1024 @Override
1025 public void swap() {
1026 enforceModifyPermission();
1027
1028 try {
1029 PhoneUtils.swap();
1030 } catch (Exception e) {
1031 Log.e(LOG_TAG, "Error during swap().", e);
1032 }
1033 }
1034
1035 @Override
1036 public void mute(boolean onOff) {
1037 enforceModifyPermission();
1038
1039 try {
1040 PhoneUtils.setMute(onOff);
1041 } catch (Exception e) {
1042 Log.e(LOG_TAG, "Error during mute().", e);
1043 }
1044 }
1045
1046 @Override
1047 public void playDtmfTone(char digit, boolean timedShortTone) {
1048 enforceModifyPermission();
1049
1050 synchronized (mDtmfStopHandler) {
1051 try {
1052 mDtmfTonePlayer.playDtmfTone(digit, timedShortTone);
1053 } catch (Exception e) {
1054 Log.e(LOG_TAG, "Error playing DTMF tone.", e);
1055 }
1056
1057 if (mDtmfStopRunnable != null) {
1058 mDtmfStopHandler.removeCallbacks(mDtmfStopRunnable);
1059 }
1060 mDtmfStopRunnable = new Runnable() {
1061 @Override
1062 public void run() {
1063 synchronized (mDtmfStopHandler) {
1064 if (mDtmfStopRunnable == this) {
1065 mDtmfTonePlayer.stopDtmfTone();
1066 mDtmfStopRunnable = null;
1067 }
1068 }
1069 }
1070 };
1071 mDtmfStopHandler.postDelayed(mDtmfStopRunnable, 5000);
1072 }
1073 }
1074
1075 @Override
1076 public void stopDtmfTone() {
1077 enforceModifyPermission();
1078
1079 synchronized (mDtmfStopHandler) {
1080 try {
1081 mDtmfTonePlayer.stopDtmfTone();
1082 } catch (Exception e) {
1083 Log.e(LOG_TAG, "Error stopping DTMF tone.", e);
1084 }
1085
1086 if (mDtmfStopRunnable != null) {
1087 mDtmfStopHandler.removeCallbacks(mDtmfStopRunnable);
1088 mDtmfStopRunnable = null;
1089 }
1090 }
1091 }
1092
1093 @Override
1094 public void addListener(ITelephonyListener listener) {
1095 enforcePrivilegedPhoneStatePermission();
1096
1097 if (listener == null) {
1098 throw new IllegalArgumentException("Listener must not be null.");
1099 }
1100
1101 synchronized (mListeners) {
1102 IBinder listenerBinder = listener.asBinder();
1103 for (ITelephonyListener l : mListeners) {
1104 if (l.asBinder().equals(listenerBinder)) {
1105 Log.w(LOG_TAG, "Listener already registered. Ignoring.");
1106 return;
1107 }
1108 }
1109 mListeners.add(listener);
1110 mDeathRecipients.put(listener.asBinder(),
1111 new TelephonyListenerDeathRecipient(listener.asBinder()));
1112
1113 // update the new listener so they get the full call state immediately
1114 for (Call call : mCallModeler.getFullList()) {
1115 try {
1116 notifyListenerOfCallLocked(call, listener);
1117 } catch (RemoteException e) {
1118 Log.e(LOG_TAG, "Error updating new listener. Ignoring.");
1119 removeListenerInternal(listener);
1120 }
1121 }
1122 }
1123 }
1124
1125 @Override
1126 public void removeListener(ITelephonyListener listener) {
1127 enforcePrivilegedPhoneStatePermission();
1128
1129 if (listener == null) {
1130 throw new IllegalArgumentException("Listener must not be null.");
1131 }
1132
1133 removeListenerInternal(listener);
1134 }
1135
1136 private void removeListenerInternal(ITelephonyListener listener) {
1137 IBinder listenerBinder = listener.asBinder();
1138
1139 synchronized (mListeners) {
1140 for (Iterator<ITelephonyListener> it = mListeners.iterator(); it.hasNext(); ) {
1141 ITelephonyListener nextListener = it.next();
1142 if (nextListener.asBinder().equals(listenerBinder)) {
1143 TelephonyListenerDeathRecipient dr = mDeathRecipients.get(listener.asBinder());
1144 if (dr != null) {
1145 dr.unlinkDeathRecipient();
1146 }
1147 it.remove();
1148 }
1149 }
1150 }
1151 }
1152
1153 /** CallModeler.Listener implementation **/
1154
1155 @Override
1156 public void onDisconnect(Call call) {
1157 notifyListenersOfCall(call);
1158 }
1159
1160 @Override
1161 public void onIncoming(Call call) {
1162 notifyListenersOfCall(call);
1163 }
1164
1165 @Override
1166 public void onUpdate(List<Call> calls) {
1167 for (Call call : calls) {
1168 notifyListenersOfCall(call);
1169 }
1170 }
1171
1172 @Override
1173 public void onPostDialAction(
1174 Connection.PostDialState state, int callId, String remainingChars, char c) { }
1175
1176 private void notifyListenersOfCall(Call call) {
1177 synchronized (mListeners) {
1178 for (Iterator<ITelephonyListener> it = mListeners.iterator(); it.hasNext(); ) {
1179 ITelephonyListener listener = it.next();
1180 try {
1181 notifyListenerOfCallLocked(call, listener);
1182 } catch (RemoteException e) {
1183 TelephonyListenerDeathRecipient deathRecipient =
1184 mDeathRecipients.get(listener.asBinder());
1185 if (deathRecipient != null) {
1186 deathRecipient.unlinkDeathRecipient();
1187 }
1188 it.remove();
1189 }
1190 }
1191 }
1192 }
1193
1194 private void notifyListenerOfCallLocked(final Call call,final ITelephonyListener listener)
1195 throws RemoteException {
1196 if (Binder.isProxy(listener)) {
1197 listener.onUpdate(call.getCallId(), call.getState(), call.getNumber());
1198 } else {
1199 mMainThreadHandler.post(new Runnable() {
1200
1201 @Override
1202 public void run() {
1203 try {
1204 listener.onUpdate(call.getCallId(), call.getState(), call.getNumber());
1205 } catch (RemoteException e) {
1206 Log.wtf(LOG_TAG, "Local binder call failed with RemoteException.", e);
1207 }
1208 }
1209 });
1210 }
1211
1212 }
1213
1214 private class TelephonyListenerDeathRecipient implements Binder.DeathRecipient {
1215 private final IBinder mBinder;
1216
1217 public TelephonyListenerDeathRecipient(IBinder listener) {
1218 mBinder = listener;
1219 try {
1220 mBinder.linkToDeath(this, 0);
1221 } catch (RemoteException e) {
1222 unlinkDeathRecipient();
1223 }
1224 }
1225
1226 @Override
1227 public void binderDied() {
1228 synchronized (mListeners) {
1229 if (mListeners.contains(mBinder)) {
1230 mListeners.remove(mBinder);
1231 Log.w(LOG_TAG, "ITelephonyListener died. Removing.");
1232 } else {
1233 Log.w(LOG_TAG, "TelephonyListener binder died but the listener " +
1234 "is not registered.");
1235 }
1236 }
1237 }
1238
1239 public void unlinkDeathRecipient() {
1240 mBinder.unlinkToDeath(this, 0);
1241 }
1242 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -07001243}