blob: 05b2308cb3ea040dfe8b60f41d2c4f5cde777aa0 [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;
21import android.content.ActivityNotFoundException;
Sailesh Nepalbd76e4e2013-10-27 13:59:44 -070022import android.content.ComponentName;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070023import 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;
31import android.os.Looper;
32import android.os.Message;
33import android.os.Process;
34import android.os.ServiceManager;
35import android.os.UserHandle;
36import android.telephony.NeighboringCellInfo;
37import android.telephony.CellInfo;
38import android.telephony.ServiceState;
39import android.text.TextUtils;
40import android.util.Log;
Sailesh Nepalbd76e4e2013-10-27 13:59:44 -070041import android.util.Pair;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070042
43import com.android.internal.telephony.DefaultPhoneNotifier;
44import com.android.internal.telephony.IccCard;
45import com.android.internal.telephony.ITelephony;
Sailesh Nepalbd76e4e2013-10-27 13:59:44 -070046import com.android.internal.telephony.IThirdPartyCallProvider;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070047import com.android.internal.telephony.Phone;
48import com.android.internal.telephony.CallManager;
Wink Saville9de0f752013-10-22 19:04:03 -070049import com.android.internal.telephony.CommandException;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070050import com.android.internal.telephony.PhoneConstants;
Sailesh Nepalbd76e4e2013-10-27 13:59:44 -070051import com.android.internal.telephony.thirdpartyphone.ThirdPartyPhone;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070052
53import java.util.List;
54import java.util.ArrayList;
55
56/**
57 * Implementation of the ITelephony interface.
58 */
59public class PhoneInterfaceManager extends ITelephony.Stub {
60 private static final String LOG_TAG = "PhoneInterfaceManager";
61 private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
62 private static final boolean DBG_LOC = false;
63
64 // Message codes used with mMainThreadHandler
65 private static final int CMD_HANDLE_PIN_MMI = 1;
66 private static final int CMD_HANDLE_NEIGHBORING_CELL = 2;
67 private static final int EVENT_NEIGHBORING_CELL_DONE = 3;
68 private static final int CMD_ANSWER_RINGING_CALL = 4;
69 private static final int CMD_END_CALL = 5; // not used yet
70 private static final int CMD_SILENCE_RINGER = 6;
Sailesh Nepalbd76e4e2013-10-27 13:59:44 -070071 private static final int CMD_NEW_INCOMING_THIRD_PARTY_CALL = 7;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070072
73 /** The singleton instance. */
74 private static PhoneInterfaceManager sInstance;
75
76 PhoneGlobals mApp;
77 Phone mPhone;
78 CallManager mCM;
79 AppOpsManager mAppOps;
80 MainThreadHandler mMainThreadHandler;
Santos Cordon406c0342013-08-28 00:07:47 -070081 CallHandlerServiceProxy mCallHandlerService;
Santos Cordon7d4ddf62013-07-10 11:58:08 -070082
83 /**
84 * A request object for use with {@link MainThreadHandler}. Requesters should wait() on the
85 * request after sending. The main thread will notify the request when it is complete.
86 */
87 private static final class MainThreadRequest {
88 /** The argument to use for the request */
89 public Object argument;
90 /** The result of the request that is run on the main thread */
91 public Object result;
92
93 public MainThreadRequest(Object argument) {
94 this.argument = argument;
95 }
96 }
97
98 /**
99 * A handler that processes messages on the main thread in the phone process. Since many
100 * of the Phone calls are not thread safe this is needed to shuttle the requests from the
101 * inbound binder threads to the main thread in the phone process. The Binder thread
102 * may provide a {@link MainThreadRequest} object in the msg.obj field that they are waiting
103 * on, which will be notified when the operation completes and will contain the result of the
104 * request.
105 *
106 * <p>If a MainThreadRequest object is provided in the msg.obj field,
107 * note that request.result must be set to something non-null for the calling thread to
108 * unblock.
109 */
110 private final class MainThreadHandler extends Handler {
111 @Override
112 public void handleMessage(Message msg) {
113 MainThreadRequest request;
114 Message onCompleted;
115 AsyncResult ar;
116
117 switch (msg.what) {
118 case CMD_HANDLE_PIN_MMI:
119 request = (MainThreadRequest) msg.obj;
120 request.result = Boolean.valueOf(
121 mPhone.handlePinMmi((String) request.argument));
122 // Wake up the requesting thread
123 synchronized (request) {
124 request.notifyAll();
125 }
126 break;
127
128 case CMD_HANDLE_NEIGHBORING_CELL:
129 request = (MainThreadRequest) msg.obj;
130 onCompleted = obtainMessage(EVENT_NEIGHBORING_CELL_DONE,
131 request);
132 mPhone.getNeighboringCids(onCompleted);
133 break;
134
135 case EVENT_NEIGHBORING_CELL_DONE:
136 ar = (AsyncResult) msg.obj;
137 request = (MainThreadRequest) ar.userObj;
138 if (ar.exception == null && ar.result != null) {
139 request.result = ar.result;
140 } else {
141 // create an empty list to notify the waiting thread
142 request.result = new ArrayList<NeighboringCellInfo>();
143 }
144 // Wake up the requesting thread
145 synchronized (request) {
146 request.notifyAll();
147 }
148 break;
149
150 case CMD_ANSWER_RINGING_CALL:
151 answerRingingCallInternal();
152 break;
153
154 case CMD_SILENCE_RINGER:
155 silenceRingerInternal();
156 break;
157
158 case CMD_END_CALL:
159 request = (MainThreadRequest) msg.obj;
160 boolean hungUp = false;
161 int phoneType = mPhone.getPhoneType();
162 if (phoneType == PhoneConstants.PHONE_TYPE_CDMA) {
163 // CDMA: If the user presses the Power button we treat it as
164 // ending the complete call session
165 hungUp = PhoneUtils.hangupRingingAndActive(mPhone);
166 } else if (phoneType == PhoneConstants.PHONE_TYPE_GSM) {
167 // GSM: End the call as per the Phone state
168 hungUp = PhoneUtils.hangup(mCM);
169 } else {
170 throw new IllegalStateException("Unexpected phone type: " + phoneType);
171 }
172 if (DBG) log("CMD_END_CALL: " + (hungUp ? "hung up!" : "no call to hang up"));
173 request.result = hungUp;
174 // Wake up the requesting thread
175 synchronized (request) {
176 request.notifyAll();
177 }
178 break;
Sailesh Nepalbd76e4e2013-10-27 13:59:44 -0700179 case CMD_NEW_INCOMING_THIRD_PARTY_CALL: {
180 request = (MainThreadRequest) msg.obj;
181 Pair<ComponentName, String> pair =
182 (Pair<ComponentName, String>) request.argument;
Sailesh Nepalbfb68322013-11-07 14:07:41 -0800183 ThirdPartyPhone thirdPartyPhone = (ThirdPartyPhone)
184 PhoneUtils.getThirdPartyPhoneFromComponent(mCM, pair.first);
185 thirdPartyPhone.takeIncomingCall(pair.first, pair.second);
Sailesh Nepalbd76e4e2013-10-27 13:59:44 -0700186 break;
187 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700188
189 default:
190 Log.w(LOG_TAG, "MainThreadHandler: unexpected message code: " + msg.what);
191 break;
192 }
193 }
194 }
195
196 /**
197 * Posts the specified command to be executed on the main thread,
198 * waits for the request to complete, and returns the result.
199 * @see #sendRequestAsync
200 */
201 private Object sendRequest(int command, Object argument) {
202 if (Looper.myLooper() == mMainThreadHandler.getLooper()) {
203 throw new RuntimeException("This method will deadlock if called from the main thread.");
204 }
205
206 MainThreadRequest request = new MainThreadRequest(argument);
207 Message msg = mMainThreadHandler.obtainMessage(command, request);
208 msg.sendToTarget();
209
210 // Wait for the request to complete
211 synchronized (request) {
212 while (request.result == null) {
213 try {
214 request.wait();
215 } catch (InterruptedException e) {
216 // Do nothing, go back and wait until the request is complete
217 }
218 }
219 }
220 return request.result;
221 }
222
223 /**
224 * Asynchronous ("fire and forget") version of sendRequest():
225 * Posts the specified command to be executed on the main thread, and
226 * returns immediately.
227 * @see #sendRequest
228 */
229 private void sendRequestAsync(int command) {
230 mMainThreadHandler.sendEmptyMessage(command);
231 }
232
233 /**
Sailesh Nepalbd76e4e2013-10-27 13:59:44 -0700234 * Same as {@link #sendRequestAsync(int)} except it takes an argument.
235 * @see {@link #sendRequest(int,Object)}
236 */
237 private void sendRequestAsync(int command, Object argument) {
238 MainThreadRequest request = new MainThreadRequest(argument);
239 Message msg = mMainThreadHandler.obtainMessage(command, request);
240 msg.sendToTarget();
241 }
242
243 /**
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700244 * Initialize the singleton PhoneInterfaceManager instance.
245 * This is only done once, at startup, from PhoneApp.onCreate().
246 */
Santos Cordon406c0342013-08-28 00:07:47 -0700247 /* package */ static PhoneInterfaceManager init(PhoneGlobals app, Phone phone,
248 CallHandlerServiceProxy callHandlerService) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700249 synchronized (PhoneInterfaceManager.class) {
250 if (sInstance == null) {
Santos Cordon406c0342013-08-28 00:07:47 -0700251 sInstance = new PhoneInterfaceManager(app, phone, callHandlerService);
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,
261 CallHandlerServiceProxy callHandlerService) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700262 mApp = app;
263 mPhone = phone;
264 mCM = PhoneGlobals.getInstance().mCM;
265 mAppOps = (AppOpsManager)app.getSystemService(Context.APP_OPS_SERVICE);
266 mMainThreadHandler = new MainThreadHandler();
Santos Cordon406c0342013-08-28 00:07:47 -0700267 mCallHandlerService = callHandlerService;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700268 publish();
269 }
270
271 private void publish() {
272 if (DBG) log("publish: " + this);
273
274 ServiceManager.addService("phone", this);
275 }
276
277 //
278 // Implementation of the ITelephony interface.
279 //
280
281 public void dial(String number) {
282 if (DBG) log("dial: " + number);
283 // No permission check needed here: This is just a wrapper around the
284 // ACTION_DIAL intent, which is available to any app since it puts up
285 // the UI before it does anything.
286
287 String url = createTelUrl(number);
288 if (url == null) {
289 return;
290 }
291
292 // PENDING: should we just silently fail if phone is offhook or ringing?
293 PhoneConstants.State state = mCM.getState();
294 if (state != PhoneConstants.State.OFFHOOK && state != PhoneConstants.State.RINGING) {
295 Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse(url));
296 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
297 mApp.startActivity(intent);
298 }
299 }
300
301 public void call(String callingPackage, String number) {
302 if (DBG) log("call: " + number);
303
304 // This is just a wrapper around the ACTION_CALL intent, but we still
305 // need to do a permission check since we're calling startActivity()
306 // from the context of the phone app.
307 enforceCallPermission();
308
309 if (mAppOps.noteOp(AppOpsManager.OP_CALL_PHONE, Binder.getCallingUid(), callingPackage)
310 != AppOpsManager.MODE_ALLOWED) {
311 return;
312 }
313
314 String url = createTelUrl(number);
315 if (url == null) {
316 return;
317 }
318
319 Intent intent = new Intent(Intent.ACTION_CALL, Uri.parse(url));
320 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
321 mApp.startActivity(intent);
322 }
323
324 private boolean showCallScreenInternal(boolean specifyInitialDialpadState,
Makoto Onukibcf20992013-09-12 17:59:30 -0700325 boolean showDialpad) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700326 if (!PhoneGlobals.sVoiceCapable) {
327 // Never allow the InCallScreen to appear on data-only devices.
328 return false;
329 }
330 if (isIdle()) {
331 return false;
332 }
333 // If the phone isn't idle then go to the in-call screen
334 long callingId = Binder.clearCallingIdentity();
Santos Cordon406c0342013-08-28 00:07:47 -0700335
Makoto Onukibcf20992013-09-12 17:59:30 -0700336 mCallHandlerService.bringToForeground(showDialpad);
Santos Cordon406c0342013-08-28 00:07:47 -0700337
338 Binder.restoreCallingIdentity(callingId);
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700339 return true;
340 }
341
342 // Show the in-call screen without specifying the initial dialpad state.
343 public boolean showCallScreen() {
344 return showCallScreenInternal(false, false);
345 }
346
347 // The variation of showCallScreen() that specifies the initial dialpad state.
348 // (Ideally this would be called showCallScreen() too, just with a different
349 // signature, but AIDL doesn't allow that.)
350 public boolean showCallScreenWithDialpad(boolean showDialpad) {
351 return showCallScreenInternal(true, showDialpad);
352 }
353
354 /**
355 * End a call based on call state
356 * @return true is a call was ended
357 */
358 public boolean endCall() {
359 enforceCallPermission();
360 return (Boolean) sendRequest(CMD_END_CALL, null);
361 }
362
363 public void answerRingingCall() {
364 if (DBG) log("answerRingingCall...");
365 // TODO: there should eventually be a separate "ANSWER_PHONE" permission,
366 // but that can probably wait till the big TelephonyManager API overhaul.
367 // For now, protect this call with the MODIFY_PHONE_STATE permission.
368 enforceModifyPermission();
369 sendRequestAsync(CMD_ANSWER_RINGING_CALL);
370 }
371
372 /**
373 * Make the actual telephony calls to implement answerRingingCall().
374 * This should only be called from the main thread of the Phone app.
375 * @see #answerRingingCall
376 *
377 * TODO: it would be nice to return true if we answered the call, or
378 * false if there wasn't actually a ringing incoming call, or some
379 * other error occurred. (In other words, pass back the return value
380 * from PhoneUtils.answerCall() or PhoneUtils.answerAndEndActive().)
381 * But that would require calling this method via sendRequest() rather
382 * than sendRequestAsync(), and right now we don't actually *need* that
383 * return value, so let's just return void for now.
384 */
385 private void answerRingingCallInternal() {
386 final boolean hasRingingCall = !mPhone.getRingingCall().isIdle();
387 if (hasRingingCall) {
388 final boolean hasActiveCall = !mPhone.getForegroundCall().isIdle();
389 final boolean hasHoldingCall = !mPhone.getBackgroundCall().isIdle();
390 if (hasActiveCall && hasHoldingCall) {
391 // Both lines are in use!
392 // TODO: provide a flag to let the caller specify what
393 // policy to use if both lines are in use. (The current
394 // behavior is hardwired to "answer incoming, end ongoing",
395 // which is how the CALL button is specced to behave.)
396 PhoneUtils.answerAndEndActive(mCM, mCM.getFirstActiveRingingCall());
397 return;
398 } else {
399 // answerCall() will automatically hold the current active
400 // call, if there is one.
401 PhoneUtils.answerCall(mCM.getFirstActiveRingingCall());
402 return;
403 }
404 } else {
405 // No call was ringing.
406 return;
407 }
408 }
409
410 public void silenceRinger() {
411 if (DBG) log("silenceRinger...");
412 // TODO: find a more appropriate permission to check here.
413 // (That can probably wait till the big TelephonyManager API overhaul.
414 // For now, protect this call with the MODIFY_PHONE_STATE permission.)
415 enforceModifyPermission();
416 sendRequestAsync(CMD_SILENCE_RINGER);
417 }
418
419 /**
420 * Internal implemenation of silenceRinger().
421 * This should only be called from the main thread of the Phone app.
422 * @see #silenceRinger
423 */
424 private void silenceRingerInternal() {
425 if ((mCM.getState() == PhoneConstants.State.RINGING)
426 && mApp.notifier.isRinging()) {
427 // Ringer is actually playing, so silence it.
428 if (DBG) log("silenceRingerInternal: silencing...");
429 mApp.notifier.silenceRinger();
430 }
431 }
432
433 public boolean isOffhook() {
434 return (mCM.getState() == PhoneConstants.State.OFFHOOK);
435 }
436
437 public boolean isRinging() {
438 return (mCM.getState() == PhoneConstants.State.RINGING);
439 }
440
441 public boolean isIdle() {
442 return (mCM.getState() == PhoneConstants.State.IDLE);
443 }
444
445 public boolean isSimPinEnabled() {
446 enforceReadPermission();
447 return (PhoneGlobals.getInstance().isSimPinEnabled());
448 }
449
450 public boolean supplyPin(String pin) {
Wink Saville9de0f752013-10-22 19:04:03 -0700451 int [] resultArray = supplyPinReportResult(pin);
452 return (resultArray[0] == PhoneConstants.PIN_RESULT_SUCCESS) ? true : false;
453 }
454
455 public boolean supplyPuk(String puk, String pin) {
456 int [] resultArray = supplyPukReportResult(puk, pin);
457 return (resultArray[0] == PhoneConstants.PIN_RESULT_SUCCESS) ? true : false;
458 }
459
460 /** {@hide} */
461 public int[] supplyPinReportResult(String pin) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700462 enforceModifyPermission();
463 final UnlockSim checkSimPin = new UnlockSim(mPhone.getIccCard());
464 checkSimPin.start();
465 return checkSimPin.unlockSim(null, pin);
466 }
467
Wink Saville9de0f752013-10-22 19:04:03 -0700468 /** {@hide} */
469 public int[] supplyPukReportResult(String puk, String pin) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700470 enforceModifyPermission();
471 final UnlockSim checkSimPuk = new UnlockSim(mPhone.getIccCard());
472 checkSimPuk.start();
473 return checkSimPuk.unlockSim(puk, pin);
474 }
475
476 /**
Wink Saville9de0f752013-10-22 19:04:03 -0700477 * Helper thread to turn async call to SimCard#supplyPin into
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700478 * a synchronous one.
479 */
480 private static class UnlockSim extends Thread {
481
482 private final IccCard mSimCard;
483
484 private boolean mDone = false;
Wink Saville9de0f752013-10-22 19:04:03 -0700485 private int mResult = PhoneConstants.PIN_GENERAL_FAILURE;
486 private int mRetryCount = -1;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700487
488 // For replies from SimCard interface
489 private Handler mHandler;
490
491 // For async handler to identify request type
492 private static final int SUPPLY_PIN_COMPLETE = 100;
493
494 public UnlockSim(IccCard simCard) {
495 mSimCard = simCard;
496 }
497
498 @Override
499 public void run() {
500 Looper.prepare();
501 synchronized (UnlockSim.this) {
502 mHandler = new Handler() {
503 @Override
504 public void handleMessage(Message msg) {
505 AsyncResult ar = (AsyncResult) msg.obj;
506 switch (msg.what) {
507 case SUPPLY_PIN_COMPLETE:
508 Log.d(LOG_TAG, "SUPPLY_PIN_COMPLETE");
509 synchronized (UnlockSim.this) {
Wink Saville9de0f752013-10-22 19:04:03 -0700510 mRetryCount = msg.arg1;
511 if (ar.exception != null) {
512 if (ar.exception instanceof CommandException &&
513 ((CommandException)(ar.exception)).getCommandError()
514 == CommandException.Error.PASSWORD_INCORRECT) {
515 mResult = PhoneConstants.PIN_PASSWORD_INCORRECT;
516 } else {
517 mResult = PhoneConstants.PIN_GENERAL_FAILURE;
518 }
519 } else {
520 mResult = PhoneConstants.PIN_RESULT_SUCCESS;
521 }
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700522 mDone = true;
523 UnlockSim.this.notifyAll();
524 }
525 break;
526 }
527 }
528 };
529 UnlockSim.this.notifyAll();
530 }
531 Looper.loop();
532 }
533
534 /*
535 * Use PIN or PUK to unlock SIM card
536 *
537 * If PUK is null, unlock SIM card with PIN
538 *
539 * If PUK is not null, unlock SIM card with PUK and set PIN code
540 */
Wink Saville9de0f752013-10-22 19:04:03 -0700541 synchronized int[] unlockSim(String puk, String pin) {
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700542
543 while (mHandler == null) {
544 try {
545 wait();
546 } catch (InterruptedException e) {
547 Thread.currentThread().interrupt();
548 }
549 }
550 Message callback = Message.obtain(mHandler, SUPPLY_PIN_COMPLETE);
551
552 if (puk == null) {
553 mSimCard.supplyPin(pin, callback);
554 } else {
555 mSimCard.supplyPuk(puk, pin, callback);
556 }
557
558 while (!mDone) {
559 try {
560 Log.d(LOG_TAG, "wait for done");
561 wait();
562 } catch (InterruptedException e) {
563 // Restore the interrupted status
564 Thread.currentThread().interrupt();
565 }
566 }
567 Log.d(LOG_TAG, "done");
Wink Saville9de0f752013-10-22 19:04:03 -0700568 int[] resultArray = new int[2];
569 resultArray[0] = mResult;
570 resultArray[1] = mRetryCount;
571 return resultArray;
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700572 }
573 }
574
575 public void updateServiceLocation() {
576 // No permission check needed here: this call is harmless, and it's
577 // needed for the ServiceState.requestStateUpdate() call (which is
578 // already intentionally exposed to 3rd parties.)
579 mPhone.updateServiceLocation();
580 }
581
582 public boolean isRadioOn() {
583 return mPhone.getServiceState().getVoiceRegState() != ServiceState.STATE_POWER_OFF;
584 }
585
586 public void toggleRadioOnOff() {
587 enforceModifyPermission();
588 mPhone.setRadioPower(!isRadioOn());
589 }
590 public boolean setRadio(boolean turnOn) {
591 enforceModifyPermission();
592 if ((mPhone.getServiceState().getVoiceRegState() != ServiceState.STATE_POWER_OFF) != turnOn) {
593 toggleRadioOnOff();
594 }
595 return true;
596 }
597 public boolean setRadioPower(boolean turnOn) {
598 enforceModifyPermission();
599 mPhone.setRadioPower(turnOn);
600 return true;
601 }
602
603 public boolean enableDataConnectivity() {
604 enforceModifyPermission();
605 ConnectivityManager cm =
606 (ConnectivityManager)mApp.getSystemService(Context.CONNECTIVITY_SERVICE);
607 cm.setMobileDataEnabled(true);
608 return true;
609 }
610
611 public int enableApnType(String type) {
612 enforceModifyPermission();
613 return mPhone.enableApnType(type);
614 }
615
616 public int disableApnType(String type) {
617 enforceModifyPermission();
618 return mPhone.disableApnType(type);
619 }
620
621 public boolean disableDataConnectivity() {
622 enforceModifyPermission();
623 ConnectivityManager cm =
624 (ConnectivityManager)mApp.getSystemService(Context.CONNECTIVITY_SERVICE);
625 cm.setMobileDataEnabled(false);
626 return true;
627 }
628
629 public boolean isDataConnectivityPossible() {
630 return mPhone.isDataConnectivityPossible();
631 }
632
633 public boolean handlePinMmi(String dialString) {
634 enforceModifyPermission();
635 return (Boolean) sendRequest(CMD_HANDLE_PIN_MMI, dialString);
636 }
637
638 public void cancelMissedCallsNotification() {
639 enforceModifyPermission();
640 mApp.notificationMgr.cancelMissedCallNotification();
641 }
642
643 public int getCallState() {
644 return DefaultPhoneNotifier.convertCallState(mCM.getState());
645 }
646
647 public int getDataState() {
648 return DefaultPhoneNotifier.convertDataState(mPhone.getDataConnectionState());
649 }
650
651 public int getDataActivity() {
652 return DefaultPhoneNotifier.convertDataActivityState(mPhone.getDataActivityState());
653 }
654
655 @Override
656 public Bundle getCellLocation() {
657 try {
658 mApp.enforceCallingOrSelfPermission(
659 android.Manifest.permission.ACCESS_FINE_LOCATION, null);
660 } catch (SecurityException e) {
661 // If we have ACCESS_FINE_LOCATION permission, skip the check for ACCESS_COARSE_LOCATION
662 // A failure should throw the SecurityException from ACCESS_COARSE_LOCATION since this
663 // is the weaker precondition
664 mApp.enforceCallingOrSelfPermission(
665 android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
666 }
667
668 if (checkIfCallerIsSelfOrForegoundUser()) {
669 if (DBG_LOC) log("getCellLocation: is active user");
670 Bundle data = new Bundle();
671 mPhone.getCellLocation().fillInNotifierBundle(data);
672 return data;
673 } else {
674 if (DBG_LOC) log("getCellLocation: suppress non-active user");
675 return null;
676 }
677 }
678
679 @Override
680 public void enableLocationUpdates() {
681 mApp.enforceCallingOrSelfPermission(
682 android.Manifest.permission.CONTROL_LOCATION_UPDATES, null);
683 mPhone.enableLocationUpdates();
684 }
685
686 @Override
687 public void disableLocationUpdates() {
688 mApp.enforceCallingOrSelfPermission(
689 android.Manifest.permission.CONTROL_LOCATION_UPDATES, null);
690 mPhone.disableLocationUpdates();
691 }
692
693 @Override
694 @SuppressWarnings("unchecked")
695 public List<NeighboringCellInfo> getNeighboringCellInfo(String callingPackage) {
696 try {
697 mApp.enforceCallingOrSelfPermission(
698 android.Manifest.permission.ACCESS_FINE_LOCATION, null);
699 } catch (SecurityException e) {
700 // If we have ACCESS_FINE_LOCATION permission, skip the check
701 // for ACCESS_COARSE_LOCATION
702 // A failure should throw the SecurityException from
703 // ACCESS_COARSE_LOCATION since this is the weaker precondition
704 mApp.enforceCallingOrSelfPermission(
705 android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
706 }
707
708 if (mAppOps.noteOp(AppOpsManager.OP_NEIGHBORING_CELLS, Binder.getCallingUid(),
709 callingPackage) != AppOpsManager.MODE_ALLOWED) {
710 return null;
711 }
712 if (checkIfCallerIsSelfOrForegoundUser()) {
713 if (DBG_LOC) log("getNeighboringCellInfo: is active user");
714
715 ArrayList<NeighboringCellInfo> cells = null;
716
717 try {
718 cells = (ArrayList<NeighboringCellInfo>) sendRequest(
719 CMD_HANDLE_NEIGHBORING_CELL, null);
720 } catch (RuntimeException e) {
721 Log.e(LOG_TAG, "getNeighboringCellInfo " + e);
722 }
723 return cells;
724 } else {
725 if (DBG_LOC) log("getNeighboringCellInfo: suppress non-active user");
726 return null;
727 }
728 }
729
730
731 @Override
732 public List<CellInfo> getAllCellInfo() {
733 try {
734 mApp.enforceCallingOrSelfPermission(
735 android.Manifest.permission.ACCESS_FINE_LOCATION, null);
736 } catch (SecurityException e) {
737 // If we have ACCESS_FINE_LOCATION permission, skip the check for ACCESS_COARSE_LOCATION
738 // A failure should throw the SecurityException from ACCESS_COARSE_LOCATION since this
739 // is the weaker precondition
740 mApp.enforceCallingOrSelfPermission(
741 android.Manifest.permission.ACCESS_COARSE_LOCATION, null);
742 }
743
744 if (checkIfCallerIsSelfOrForegoundUser()) {
745 if (DBG_LOC) log("getAllCellInfo: is active user");
746 return mPhone.getAllCellInfo();
747 } else {
748 if (DBG_LOC) log("getAllCellInfo: suppress non-active user");
749 return null;
750 }
751 }
752
Sailesh Nepalbd76e4e2013-10-27 13:59:44 -0700753 @Override
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700754 public void setCellInfoListRate(int rateInMillis) {
755 mPhone.setCellInfoListRate(rateInMillis);
756 }
757
Sailesh Nepalbd76e4e2013-10-27 13:59:44 -0700758 @Override
759 public void newIncomingThirdPartyCall(ComponentName component, String callId) {
760 // TODO(sail): Enforce that the component belongs to the calling package.
761 if (DBG) {
762 log("newIncomingThirdPartyCall: component: " + component + " callId: " + callId);
763 }
764 enforceCallPermission();
765 sendRequestAsync(CMD_NEW_INCOMING_THIRD_PARTY_CALL, Pair.create(component, callId));
766 }
767
Santos Cordon7d4ddf62013-07-10 11:58:08 -0700768 //
769 // Internal helper methods.
770 //
771
772 private boolean checkIfCallerIsSelfOrForegoundUser() {
773 boolean ok;
774
775 boolean self = Binder.getCallingUid() == Process.myUid();
776 if (!self) {
777 // Get the caller's user id then clear the calling identity
778 // which will be restored in the finally clause.
779 int callingUser = UserHandle.getCallingUserId();
780 long ident = Binder.clearCallingIdentity();
781
782 try {
783 // With calling identity cleared the current user is the foreground user.
784 int foregroundUser = ActivityManager.getCurrentUser();
785 ok = (foregroundUser == callingUser);
786 if (DBG_LOC) {
787 log("checkIfCallerIsSelfOrForegoundUser: foregroundUser=" + foregroundUser
788 + " callingUser=" + callingUser + " ok=" + ok);
789 }
790 } catch (Exception ex) {
791 if (DBG_LOC) loge("checkIfCallerIsSelfOrForegoundUser: Exception ex=" + ex);
792 ok = false;
793 } finally {
794 Binder.restoreCallingIdentity(ident);
795 }
796 } else {
797 if (DBG_LOC) log("checkIfCallerIsSelfOrForegoundUser: is self");
798 ok = true;
799 }
800 if (DBG_LOC) log("checkIfCallerIsSelfOrForegoundUser: ret=" + ok);
801 return ok;
802 }
803
804 /**
805 * Make sure the caller has the READ_PHONE_STATE permission.
806 *
807 * @throws SecurityException if the caller does not have the required permission
808 */
809 private void enforceReadPermission() {
810 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_STATE, null);
811 }
812
813 /**
814 * Make sure the caller has the MODIFY_PHONE_STATE permission.
815 *
816 * @throws SecurityException if the caller does not have the required permission
817 */
818 private void enforceModifyPermission() {
819 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE, null);
820 }
821
822 /**
823 * Make sure the caller has the CALL_PHONE permission.
824 *
825 * @throws SecurityException if the caller does not have the required permission
826 */
827 private void enforceCallPermission() {
828 mApp.enforceCallingOrSelfPermission(android.Manifest.permission.CALL_PHONE, null);
829 }
830
831
832 private String createTelUrl(String number) {
833 if (TextUtils.isEmpty(number)) {
834 return null;
835 }
836
837 StringBuilder buf = new StringBuilder("tel:");
838 buf.append(number);
839 return buf.toString();
840 }
841
842 private void log(String msg) {
843 Log.d(LOG_TAG, "[PhoneIntfMgr] " + msg);
844 }
845
846 private void loge(String msg) {
847 Log.e(LOG_TAG, "[PhoneIntfMgr] " + msg);
848 }
849
850 public int getActivePhoneType() {
851 return mPhone.getPhoneType();
852 }
853
854 /**
855 * Returns the CDMA ERI icon index to display
856 */
857 public int getCdmaEriIconIndex() {
858 return mPhone.getCdmaEriIconIndex();
859 }
860
861 /**
862 * Returns the CDMA ERI icon mode,
863 * 0 - ON
864 * 1 - FLASHING
865 */
866 public int getCdmaEriIconMode() {
867 return mPhone.getCdmaEriIconMode();
868 }
869
870 /**
871 * Returns the CDMA ERI text,
872 */
873 public String getCdmaEriText() {
874 return mPhone.getCdmaEriText();
875 }
876
877 /**
878 * Returns true if CDMA provisioning needs to run.
879 */
880 public boolean needsOtaServiceProvisioning() {
881 return mPhone.needsOtaServiceProvisioning();
882 }
883
884 /**
885 * Returns the unread count of voicemails
886 */
887 public int getVoiceMessageCount() {
888 return mPhone.getVoiceMessageCount();
889 }
890
891 /**
892 * Returns the data network type
893 *
894 * @Deprecated to be removed Q3 2013 use {@link #getDataNetworkType}.
895 */
896 @Override
897 public int getNetworkType() {
898 return mPhone.getServiceState().getDataNetworkType();
899 }
900
901 /**
902 * Returns the data network type
903 */
904 @Override
905 public int getDataNetworkType() {
906 return mPhone.getServiceState().getDataNetworkType();
907 }
908
909 /**
910 * Returns the data network type
911 */
912 @Override
913 public int getVoiceNetworkType() {
914 return mPhone.getServiceState().getVoiceNetworkType();
915 }
916
917 /**
918 * @return true if a ICC card is present
919 */
920 public boolean hasIccCard() {
921 return mPhone.getIccCard().hasIccCard();
922 }
923
924 /**
925 * Return if the current radio is LTE on CDMA. This
926 * is a tri-state return value as for a period of time
927 * the mode may be unknown.
928 *
929 * @return {@link Phone#LTE_ON_CDMA_UNKNOWN}, {@link Phone#LTE_ON_CDMA_FALSE}
930 * or {@link PHone#LTE_ON_CDMA_TRUE}
931 */
932 public int getLteOnCdmaMode() {
933 return mPhone.getLteOnCdmaMode();
934 }
935}