Merge "Clean copy from the System Setting lib"
diff --git a/Android.mk b/Android.mk
index be929f1..4846ea1 100644
--- a/Android.mk
+++ b/Android.mk
@@ -46,6 +46,7 @@
--extra-packages android.support.v14.preference
LOCAL_PACKAGE_NAME := TeleService
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_CERTIFICATE := platform
LOCAL_PRIVILEGED_MODULE := true
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index dbe7382..f86cdef 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -79,10 +79,10 @@
<protected-broadcast android:name= "com.android.phone.vvm.ACTION_VISUAL_VOICEMAIL_SERVICE_EVENT" />
<protected-broadcast android:name= "com.android.internal.telephony.CARRIER_VVM_PACKAGE_INSTALLED" />
<protected-broadcast android:name= "com.android.cellbroadcastreceiver.GET_LATEST_CB_AREA_INFO" />
- <protected-broadcast android:name= "android.intent.action.CURRENT_CARRIER_IDENTITY_CHANGED" />
<protected-broadcast android:name= "android.telephony.action.SIM_CARD_STATE_CHANGED" />
<protected-broadcast android:name= "android.telephony.action.SIM_APPLICATION_STATE_CHANGED" />
<protected-broadcast android:name= "android.telephony.action.SIM_SLOT_STATUS_CHANGED" />
+ <protected-broadcast android:name= "android.telephony.action.SUBSCRIPTION_CARRIER_IDENTITY_CHANGED" />
<uses-permission android:name="android.permission.BROADCAST_STICKY" />
<uses-permission android:name="android.permission.CALL_PHONE" />
@@ -153,9 +153,9 @@
<uses-permission android:name="android.permission.BIND_CARRIER_SERVICES" />
<!-- BIND_CARRIER_MESSAGING_SERVICE has been deprecated in favor of BIND_CARRIER_SERVICES. -->
<uses-permission android:name="android.permission.BIND_CARRIER_MESSAGING_SERVICE" />
- <uses-permission android:name="com.android.permission.BIND_EUICC_SERVICE" />
+ <uses-permission android:name="android.permission.BIND_EUICC_SERVICE" />
<uses-permission android:name="com.android.permission.BIND_TELEPHONY_NETWORK_SERVICE" />
- <uses-permission android:name="com.android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS" />
+ <uses-permission android:name="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS" />
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
@@ -181,6 +181,7 @@
<uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY" />
<uses-permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY" />
<uses-permission android:name="android.permission.BIND_TELEPHONY_DATA_SERVICE" />
+ <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
<application android:name="PhoneApp"
android:persistent="true"
@@ -240,105 +241,6 @@
</intent-filter>
</activity>
- <activity android:name="OutgoingCallBroadcaster"
- android:enabled="false"
- android:theme="@style/OutgoingCallBroadcasterTheme"
- android:permission="android.permission.CALL_PHONE"
- android:screenOrientation="nosensor"
- android:configChanges="orientation|screenSize|keyboardHidden"
- android:excludeFromRecents="true">
- <!-- CALL action intent filters, for the various ways
- of initiating an outgoing call. -->
- <intent-filter>
- <action android:name="android.intent.action.CALL" />
- <category android:name="android.intent.category.DEFAULT" />
- <data android:scheme="tel" />
- </intent-filter>
- <intent-filter android:icon="@drawable/ic_launcher_sip_call">
- <action android:name="android.intent.action.CALL" />
- <category android:name="android.intent.category.DEFAULT" />
- <data android:scheme="sip" />
- </intent-filter>
- <intent-filter>
- <action android:name="android.intent.action.CALL" />
- <category android:name="android.intent.category.DEFAULT" />
- <data android:scheme="voicemail" />
- </intent-filter>
- <intent-filter>
- <action android:name="android.intent.action.CALL" />
- <category android:name="android.intent.category.DEFAULT" />
- <data android:mimeType="vnd.android.cursor.item/phone" />
- <data android:mimeType="vnd.android.cursor.item/phone_v2" />
- <data android:mimeType="vnd.android.cursor.item/person" />
- </intent-filter>
- </activity>
-
- <activity-alias android:name="EmergencyOutgoingCallBroadcaster"
- android:enabled="false"
- android:targetActivity="OutgoingCallBroadcaster"
- android:permission="android.permission.CALL_PRIVILEGED">
- <intent-filter android:priority="1000">
- <action android:name="android.intent.action.CALL_EMERGENCY" />
- <category android:name="android.intent.category.DEFAULT" />
- <data android:scheme="tel" />
- </intent-filter>
- <intent-filter android:icon="@drawable/ic_launcher_sip_call"
- android:priority="1000">
- <action android:name="android.intent.action.CALL_EMERGENCY" />
- <category android:name="android.intent.category.DEFAULT" />
- <data android:scheme="sip" />
- </intent-filter>
- <intent-filter android:priority="1000">
- <action android:name="android.intent.action.CALL_EMERGENCY" />
- <category android:name="android.intent.category.DEFAULT" />
- <data android:scheme="voicemail" />
- </intent-filter>
- <intent-filter android:priority="1000">
- <action android:name="android.intent.action.CALL_EMERGENCY" />
- <category android:name="android.intent.category.DEFAULT" />
- <data android:mimeType="vnd.android.cursor.item/phone" />
- <data android:mimeType="vnd.android.cursor.item/person" />
- </intent-filter>
- </activity-alias>
-
- <activity-alias android:name="PrivilegedOutgoingCallBroadcaster"
- android:enabled="false"
- android:targetActivity="OutgoingCallBroadcaster"
- android:screenOrientation="nosensor"
- android:permission="android.permission.CALL_PRIVILEGED">
- <intent-filter android:priority="1000">
- <action android:name="android.intent.action.CALL_PRIVILEGED" />
- <category android:name="android.intent.category.DEFAULT" />
- <data android:scheme="tel" />
- </intent-filter>
- <intent-filter android:icon="@drawable/ic_launcher_sip_call"
- android:priority="1000">
- <action android:name="android.intent.action.CALL_PRIVILEGED" />
- <category android:name="android.intent.category.DEFAULT" />
- <data android:scheme="sip" />
- </intent-filter>
- <intent-filter android:priority="1000">
- <action android:name="android.intent.action.CALL_PRIVILEGED" />
- <category android:name="android.intent.category.DEFAULT" />
- <data android:scheme="voicemail" />
- </intent-filter>
- <intent-filter android:priority="1000">
- <action android:name="android.intent.action.CALL_PRIVILEGED" />
- <category android:name="android.intent.category.DEFAULT" />
- <data android:mimeType="vnd.android.cursor.item/phone" />
- <data android:mimeType="vnd.android.cursor.item/phone_v2" />
- <data android:mimeType="vnd.android.cursor.item/person" />
- </intent-filter>
- </activity-alias>
-
- <receiver android:name="ProcessOutgoingCallTest" android:exported="false"
- android:enabled="false">
- <intent-filter android:priority="1">
- <action android:name="android.intent.action.NEW_OUTGOING_CALL" />
- <category android:name="android.intent.category.DEFAULT" />
- </intent-filter>
- </receiver>
-
<!-- "Mobile network settings" screen, used on both
non-voice-capable tablets and regular phone devices. -->
<activity android:name="MobileNetworkSettings"
@@ -583,13 +485,6 @@
<!-- End SIP -->
- <activity android:name="ErrorDialogActivity"
- android:configChanges="orientation|screenSize|keyboardHidden"
- android:excludeFromRecents="true"
- android:launchMode="singleInstance"
- android:theme="@style/Empty">
- </activity>
-
<activity android:name="MMIDialogActivity"
android:configChanges="orientation|screenSize|keyboardHidden"
android:excludeFromRecents="true"
@@ -631,6 +526,7 @@
<action android:name="android.telecom.ConnectionService" />
</intent-filter>
</service>
+
<provider
android:name="PhoneSearchIndexablesProvider"
android:authorities="com.android.phone"
diff --git a/OWNERS b/OWNERS
index e6fad06..f190411 100644
--- a/OWNERS
+++ b/OWNERS
@@ -6,5 +6,6 @@
rgreenwalt@google.com
tgunn@google.com
refuhoo@google.com
-sanketpadawe@google.com
-mpq@google.com
\ No newline at end of file
+mpq@google.com
+jminjie@google.com
+shuoq@google.com
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 56dd753..72be3b2 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1165,6 +1165,11 @@
<string name="hac_mode_title">Hearing aids</string>
<string name="hac_mode_summary">Turn on hearing aid compatibility</string>
+ <!-- RTT settings: used to turn on/off Real-Time Text, an accessibility feature similar to
+ TTY that allows users to send text over a phone call. -->
+ <string name="rtt_mode_title">RTT</string>
+ <string name="rtt_mode_summary">Turn RTT on or off</string>
+
<!-- Service option entries. -->
<string-array name="tty_mode_entries">
<item>TTY Off</item>
@@ -1599,7 +1604,6 @@
<string name="clh_callFailed_protocol_Error_unspecified_txt">Protocol error, unspecified</string>
<!-- In-call screen: call failure reason (Cause Number 127) -->
<string name="clh_callFailed_interworking_unspecified_txt">Interworking, unspecified</string>
-
<!-- Call settings screen, setting option name -->
<string name="labelCallBarring">Call barring</string>
<!-- Call barring settings screen, setting summary text when a call barring option is activated -->
@@ -1666,4 +1670,57 @@
<string name="call_barring_settings">Call barring settings</string>
<!-- Call barring settings screen, deactivate all call barring settings -->
<string name="call_barring_deactivate_all_no_password">Deactivate all call barring settings?</string>
+ <!-- Message displayed to the user when an outgoing call is deflected. This means that the
+ party the user is calling has chosen to send the call to another phone number. -->
+ <string name="supp_service_notification_call_deflected">Call deflected.</string>
+ <!-- Message displayed to the user when an outgoing call is forwarded to another number.
+ This happens because the party the user is calling has call forwarding active. -->
+ <string name="supp_service_notification_call_forwarded">Call forwarded.</string>
+ <!-- Message displayed to the user when an outgoing call is waiting. This happens when the
+ party the user is calling is already in another call. -->
+ <string name="supp_service_notification_call_waiting">Call is waiting.</string>
+ <!-- Message displayed to the user when they have chosen to block their phone number for an
+ outgoing call, but the network has rejected that request. -->
+ <string name="supp_service_clir_suppression_rejected">Number blocking is rejected.</string>
+ <!-- Message displayed to the user to inform them that the call is to or from a number which is
+ part of a closed user group. A closed user group is a network feature which restricts
+ calls on a device to members of the closed user group. -->
+ <string name="supp_service_closed_user_group_call">Closed user group call.</string>
+ <!-- Message displayed to the user when incoming call barring is active. This means that the
+ user has enabled the network feature which prevents all incoming calls. -->
+ <string name="supp_service_incoming_calls_barred">Incoming calls barred.</string>
+ <!-- Message displayed to the user when outgoing call barring is active. This means that the
+ user has enabled the network feature which prevents all outgoing calls. -->
+ <string name="supp_service_outgoing_calls_barred">Outgoing calls barred.</string>
+ <!-- Message displayed to the user to indicate that call forwarding is active. -->
+ <string name="supp_service_call_forwarding_active">Call forwarding active.</string>
+ <!-- Message displayed to the user when they receive multiple incoming calls at the same time
+ and one of them is forwarded to the network. Phones can't handle multiple incoming calls
+ so the network will typically forward one of the calls to voicemail or another number
+ defined by the user. -->
+ <string name="supp_service_additional_call_forwarded">Additional call forwarded.</string>
+ <!-- Message displayed to the user to indicate that a call has been successfully transferred
+ to another phone number. -->
+ <string name="supp_service_additional_ect_connected">Explicit call transfer complete.</string>
+ <!-- Message displayed to the user to indicate that the call is in the process of being
+ transferred to another phone number.-->
+ <string name="supp_service_additional_ect_connecting">Explicit call transfer in progress.</string>
+ <!-- Message displayed to the user to indicate that the remote party has put the user
+ on hold. -->
+ <string name="supp_service_call_on_hold">Call on hold.</string>
+ <!-- Message displayed to the user to indicate that the remote party has taken the user
+ off hold. -->
+ <string name="supp_service_call_resumed">Call resumed.</string>
+ <!-- Message displayed to the user to indicate that an incoming call was deflected from another
+ number. This means that the call originated as a result of the original caller choosing
+ to forward the call to the current user rather than answering it themselves. -->
+ <string name="supp_service_deflected_call">Call was deflected.</string>
+ <!-- Message displayed to the user to indicate that an incoming call was forwarded from another
+ number. -->
+ <string name="supp_service_forwarded_call">Forwarded call.</string>
+ <!-- Message displayed to the user to indicate that they are joining a conference call. -->
+ <string name="supp_service_conference_call">Joining conference call.</string>
+ <!-- Message displayed to the user to indicate that a held call has been released /
+ disconnected. -->
+ <string name="supp_service_held_call_released">Held call has been released.</string>
</resources>
diff --git a/res/xml/accessibility_settings.xml b/res/xml/accessibility_settings.xml
index 2c007f6..e674e74 100644
--- a/res/xml/accessibility_settings.xml
+++ b/res/xml/accessibility_settings.xml
@@ -32,4 +32,10 @@
android:persistent="true"
android:summary="@string/hac_mode_summary"/>
+ <SwitchPreference
+ android:key="button_rtt_key"
+ android:title="@string/rtt_mode_title"
+ android:persistent="true"
+ android:summary="@string/rtt_mode_summary"/>
+
</PreferenceScreen>
diff --git a/sip/src/com/android/services/telephony/sip/SipConnectionService.java b/sip/src/com/android/services/telephony/sip/SipConnectionService.java
index a5f48d3..27be9b4 100644
--- a/sip/src/com/android/services/telephony/sip/SipConnectionService.java
+++ b/sip/src/com/android/services/telephony/sip/SipConnectionService.java
@@ -37,6 +37,7 @@
import com.android.internal.telephony.CallStateException;
import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.PhoneInternalInterface;
import com.android.internal.telephony.sip.SipPhone;
import com.android.services.telephony.DisconnectCauseUtil;
@@ -258,7 +259,9 @@
try {
com.android.internal.telephony.Connection originalConnection =
- phone.dial(number, request.getVideoState());
+ phone.dial(number, new PhoneInternalInterface.DialArgs.Builder<>()
+ .setVideoState(request.getVideoState())
+ .build());
return originalConnection;
} catch (CallStateException e) {
log("startCallWithPhone, exception: " + e);
diff --git a/src/com/android/phone/CLIRListPreference.java b/src/com/android/phone/CLIRListPreference.java
index 939caf0..f5d14b8 100644
--- a/src/com/android/phone/CLIRListPreference.java
+++ b/src/com/android/phone/CLIRListPreference.java
@@ -52,6 +52,7 @@
mPhone = phone;
mTcpListener = listener;
if (!skipReading) {
+ Log.i(LOG_TAG, "init: requesting CLIR");
mPhone.getOutgoingCallerIdDisplay(mHandler.obtainMessage(MyHandler.MESSAGE_GET_CLIR,
MyHandler.MESSAGE_GET_CLIR, MyHandler.MESSAGE_GET_CLIR));
if (mTcpListener != null) {
@@ -135,20 +136,19 @@
}
clirArray = null;
if (ar.exception != null) {
- if (DBG) Log.d(LOG_TAG, "handleGetCLIRResponse: ar.exception="+ar.exception);
+ Log.i(LOG_TAG, "handleGetCLIRResponse: ar.exception=" + ar.exception);
mTcpListener.onException(CLIRListPreference.this, (CommandException) ar.exception);
} else if (ar.userObj instanceof Throwable) {
+ Log.i(LOG_TAG, "handleGetCLIRResponse: ar.throwable=" + ar.userObj);
mTcpListener.onError(CLIRListPreference.this, RESPONSE_ERROR);
} else {
int clirArray[] = (int[]) ar.result;
if (clirArray.length != 2) {
mTcpListener.onError(CLIRListPreference.this, RESPONSE_ERROR);
} else {
- if (DBG) {
- Log.d(LOG_TAG, "handleGetCLIRResponse: CLIR successfully queried,"
+ Log.i(LOG_TAG, "handleGetCLIRResponse: CLIR successfully queried,"
+ " clirArray[0]=" + clirArray[0]
+ ", clirArray[1]=" + clirArray[1]);
- }
handleGetCLIRResult(clirArray);
}
}
diff --git a/src/com/android/phone/CallController.java b/src/com/android/phone/CallController.java
deleted file mode 100644
index a5d340c..0000000
--- a/src/com/android/phone/CallController.java
+++ /dev/null
@@ -1,671 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.phone;
-
-import com.android.internal.telephony.CallManager;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneConstants;
-import com.android.phone.CallGatewayManager.RawGatewayInfo;
-import com.android.phone.Constants.CallStatusCode;
-
-import android.content.ComponentName;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.Message;
-import android.os.SystemProperties;
-import android.provider.CallLog.Calls;
-import android.telecom.PhoneAccount;
-import android.telephony.PhoneNumberUtils;
-import android.telephony.ServiceState;
-import android.util.Log;
-
-/**
- * Phone app module in charge of "call control".
- *
- * This is a singleton object which acts as the interface to the telephony layer
- * (and other parts of the Android framework) for all user-initiated telephony
- * functionality, like making outgoing calls.
- *
- * This functionality includes things like:
- * - actually running the placeCall() method and handling errors or retries
- * - running the whole "emergency call in airplane mode" sequence
- * - running the state machine of MMI sequences
- * - restoring/resetting mute and speaker state when a new call starts
- * - updating the prox sensor wake lock state
- * - resolving what the voicemail: intent should mean (and making the call)
- *
- * The single CallController instance stays around forever; it's not tied
- * to the lifecycle of any particular Activity (like the InCallScreen).
- * There's also no implementation of onscreen UI here (that's all in InCallScreen).
- *
- * Note that this class does not handle asynchronous events from the telephony
- * layer, like reacting to an incoming call; see CallNotifier for that. This
- * class purely handles actions initiated by the user, like outgoing calls.
- */
-public class CallController extends Handler {
- private static final String TAG = "CallController";
- private static final boolean DBG =
- (PhoneGlobals.DBG_LEVEL >= 1) && (SystemProperties.getInt("ro.debuggable", 0) == 1);
- // Do not check in with VDBG = true, since that may write PII to the system log.
- private static final boolean VDBG = false;
-
- /** The singleton CallController instance. */
- private static CallController sInstance;
-
- final private PhoneGlobals mApp;
- final private CallManager mCM;
- final private CallLogger mCallLogger;
- final private CallGatewayManager mCallGatewayManager;
-
- /** Helper object for emergency calls in some rare use cases. Created lazily. */
- private EmergencyCallHelper mEmergencyCallHelper;
-
-
- //
- // Message codes; see handleMessage().
- //
-
- private static final int THREEWAY_CALLERINFO_DISPLAY_DONE = 1;
-
-
- //
- // Misc constants.
- //
-
- // Amount of time the UI should display "Dialing" when initiating a CDMA
- // 3way call. (See comments on the THRWAY_ACTIVE case in
- // placeCallInternal() for more info.)
- private static final int THREEWAY_CALLERINFO_DISPLAY_TIME = 3000; // msec
-
-
- /**
- * Initialize the singleton CallController instance.
- *
- * This is only done once, at startup, from PhoneApp.onCreate().
- * From then on, the CallController instance is available via the
- * PhoneApp's public "callController" field, which is why there's no
- * getInstance() method here.
- */
- /* package */ static CallController init(PhoneGlobals app, CallLogger callLogger,
- CallGatewayManager callGatewayManager) {
- synchronized (CallController.class) {
- if (sInstance == null) {
- sInstance = new CallController(app, callLogger, callGatewayManager);
- } else {
- Log.wtf(TAG, "init() called multiple times! sInstance = " + sInstance);
- }
- return sInstance;
- }
- }
-
- /**
- * Private constructor (this is a singleton).
- * @see init()
- */
- private CallController(PhoneGlobals app, CallLogger callLogger,
- CallGatewayManager callGatewayManager) {
- if (DBG) log("CallController constructor: app = " + app);
- mApp = app;
- mCM = app.mCM;
- mCallLogger = callLogger;
- mCallGatewayManager = callGatewayManager;
- }
-
- @Override
- public void handleMessage(Message msg) {
- if (VDBG) log("handleMessage: " + msg);
- switch (msg.what) {
-
- case THREEWAY_CALLERINFO_DISPLAY_DONE:
- if (DBG) log("THREEWAY_CALLERINFO_DISPLAY_DONE...");
-
- if (mApp.cdmaPhoneCallState.getCurrentCallState()
- == CdmaPhoneCallState.PhoneCallState.THRWAY_ACTIVE) {
- // Reset the mThreeWayCallOrigStateDialing state
- mApp.cdmaPhoneCallState.setThreeWayCallOrigState(false);
-
- // TODO: Remove this code.
- //mApp.getCallModeler().setCdmaOutgoing3WayCall(null);
- }
- break;
-
- default:
- Log.wtf(TAG, "handleMessage: unexpected code: " + msg);
- break;
- }
- }
-
- //
- // Outgoing call sequence
- //
-
- /**
- * Initiate an outgoing call.
- *
- * Here's the most typical outgoing call sequence:
- *
- * (1) OutgoingCallBroadcaster receives a CALL intent and sends the
- * NEW_OUTGOING_CALL broadcast
- *
- * (2) The broadcast finally reaches OutgoingCallReceiver, which stashes
- * away a copy of the original CALL intent and launches
- * SipCallOptionHandler
- *
- * (3) SipCallOptionHandler decides whether this is a PSTN or SIP call (and
- * in some cases brings up a dialog to let the user choose), and
- * ultimately calls CallController.placeCall() (from the
- * setResultAndFinish() method) with the stashed-away intent from step
- * (2) as the "intent" parameter.
- *
- * (4) Here in CallController.placeCall() we read the phone number or SIP
- * address out of the intent and actually initiate the call, and
- * simultaneously launch the InCallScreen to display the in-call UI.
- *
- * (5) We handle various errors by directing the InCallScreen to
- * display error messages or dialogs (via the InCallUiState
- * "pending call status code" flag), and in some cases we also
- * sometimes continue working in the background to resolve the
- * problem (like in the case of an emergency call while in
- * airplane mode). Any time that some onscreen indication to the
- * user needs to change, we update the "status dialog" info in
- * the inCallUiState and (re)launch the InCallScreen to make sure
- * it's visible.
- */
- public void placeCall(Intent intent) {
- log("placeCall()... intent = " + intent);
- if (VDBG) log(" extras = " + intent.getExtras());
-
- // TODO: Do we need to hold a wake lock while this method runs?
- // Or did we already acquire one somewhere earlier
- // in this sequence (like when we first received the CALL intent?)
-
- if (intent == null) {
- Log.wtf(TAG, "placeCall: called with null intent");
- throw new IllegalArgumentException("placeCall: called with null intent");
- }
-
- String action = intent.getAction();
- Uri uri = intent.getData();
- if (uri == null) {
- Log.wtf(TAG, "placeCall: intent had no data");
- throw new IllegalArgumentException("placeCall: intent had no data");
- }
-
- String scheme = uri.getScheme();
- String number = PhoneNumberUtils.getNumberFromIntent(intent, mApp);
- if (VDBG) {
- log("- action: " + action);
- log("- uri: " + uri);
- log("- scheme: " + scheme);
- log("- number: " + number);
- }
-
- // This method should only be used with the various flavors of CALL
- // intents. (It doesn't make sense for any other action to trigger an
- // outgoing call!)
- if (!(Intent.ACTION_CALL.equals(action)
- || Intent.ACTION_CALL_EMERGENCY.equals(action)
- || Intent.ACTION_CALL_PRIVILEGED.equals(action))) {
- Log.wtf(TAG, "placeCall: unexpected intent action " + action);
- throw new IllegalArgumentException("Unexpected action: " + action);
- }
-
- CallStatusCode status = placeCallInternal(intent);
-
- switch (status) {
- // Call was placed successfully:
- case SUCCESS:
- case EXITED_ECM:
- if (DBG) log("==> placeCall(): success from placeCallInternal(): " + status);
- break;
-
- default:
- // Any other status code is a failure.
- log("==> placeCall(): failure code from placeCallInternal(): " + status);
- // Handle the various error conditions that can occur when
- // initiating an outgoing call, typically by directing the
- // InCallScreen to display a diagnostic message (via the
- // "pending call status code" flag.)
- handleOutgoingCallError(status);
- break;
- }
-
- // Finally, regardless of whether we successfully initiated the
- // outgoing call or not, force the InCallScreen to come to the
- // foreground.
- //
- // (For successful calls the the user will just see the normal
- // in-call UI. Or if there was an error, the InCallScreen will
- // notice the InCallUiState pending call status code flag and display an
- // error indication instead.)
- }
-
- /**
- * Actually make a call to whomever the intent tells us to.
- *
- * Note that there's no need to explicitly update (or refresh) the
- * in-call UI at any point in this method, since a fresh InCallScreen
- * instance will be launched automatically after we return (see
- * placeCall() above.)
- *
- * @param intent the CALL intent describing whom to call
- * @return CallStatusCode.SUCCESS if we successfully initiated an
- * outgoing call. If there was some kind of failure, return one of
- * the other CallStatusCode codes indicating what went wrong.
- */
- private CallStatusCode placeCallInternal(Intent intent) {
- if (DBG) log("placeCallInternal()... intent = " + intent);
-
- // TODO: This method is too long. Break it down into more
- // manageable chunks.
-
- final Uri uri = intent.getData();
- final String scheme = (uri != null) ? uri.getScheme() : null;
- String number;
- Phone phone = null;
-
- // Check the current ServiceState to make sure it's OK
- // to even try making a call.
- CallStatusCode okToCallStatus = checkIfOkToInitiateOutgoingCall(
- mCM.getServiceState());
-
- // TODO: Streamline the logic here. Currently, the code is
- // unchanged from its original form in InCallScreen.java. But we
- // should fix a couple of things:
- // - Don't call checkIfOkToInitiateOutgoingCall() more than once
- // - Wrap the try/catch for VoiceMailNumberMissingException
- // around *only* the call that can throw that exception.
-
- try {
- number = PhoneUtils.getInitialNumber(intent);
- if (VDBG) log("- actual number to dial: '" + number + "'");
-
- // find the phone first
- // TODO Need a way to determine which phone to place the call
- // It could be determined by SIP setting, i.e. always,
- // or by number, i.e. for international,
- // or by user selection, i.e., dialog query,
- // or any of combinations
- String sipPhoneUri = intent.getStringExtra(
- OutgoingCallBroadcaster.EXTRA_SIP_PHONE_URI);
- ComponentName thirdPartyCallComponent = (ComponentName) intent.getParcelableExtra(
- OutgoingCallBroadcaster.EXTRA_THIRD_PARTY_CALL_COMPONENT);
- phone = PhoneUtils.pickPhoneBasedOnNumber(mCM, scheme, number, sipPhoneUri,
- thirdPartyCallComponent);
- if (VDBG) log("- got Phone instance: " + phone + ", class = " + phone.getClass());
-
- // update okToCallStatus based on new phone
- okToCallStatus = checkIfOkToInitiateOutgoingCall(
- phone.getServiceState().getState());
-
- } catch (PhoneUtils.VoiceMailNumberMissingException ex) {
- // If the call status is NOT in an acceptable state, it
- // may effect the way the voicemail number is being
- // retrieved. Mask the VoiceMailNumberMissingException
- // with the underlying issue of the phone state.
- if (okToCallStatus != CallStatusCode.SUCCESS) {
- if (DBG) log("Voicemail number not reachable in current SIM card state.");
- return okToCallStatus;
- }
- if (DBG) log("VoiceMailNumberMissingException from getInitialNumber()");
- return CallStatusCode.VOICEMAIL_NUMBER_MISSING;
- }
-
- if (number == null) {
- Log.w(TAG, "placeCall: couldn't get a phone number from Intent " + intent);
- return CallStatusCode.NO_PHONE_NUMBER_SUPPLIED;
- }
-
-
- // Sanity-check that ACTION_CALL_EMERGENCY is used if and only if
- // this is a call to an emergency number
- // (This is just a sanity-check; this policy *should* really be
- // enforced in OutgoingCallBroadcaster.onCreate(), which is the
- // main entry point for the CALL and CALL_* intents.)
- boolean isEmergencyNumber = PhoneNumberUtils.isLocalEmergencyNumber(mApp, number);
- boolean isPotentialEmergencyNumber =
- PhoneNumberUtils.isPotentialLocalEmergencyNumber(mApp, number);
- boolean isEmergencyIntent = Intent.ACTION_CALL_EMERGENCY.equals(intent.getAction());
-
- if (isPotentialEmergencyNumber && !isEmergencyIntent) {
- Log.e(TAG, "Non-CALL_EMERGENCY Intent " + intent
- + " attempted to call potential emergency number " + number
- + ".");
- return CallStatusCode.CALL_FAILED;
- } else if (!isPotentialEmergencyNumber && isEmergencyIntent) {
- Log.e(TAG, "Received CALL_EMERGENCY Intent " + intent
- + " with non-potential-emergency number " + number
- + " -- failing call.");
- return CallStatusCode.CALL_FAILED;
- }
-
- // If we're trying to call an emergency number, then it's OK to
- // proceed in certain states where we'd otherwise bring up
- // an error dialog:
- // - If we're in EMERGENCY_ONLY mode, then (obviously) you're allowed
- // to dial emergency numbers.
- // - If we're OUT_OF_SERVICE, we still attempt to make a call,
- // since the radio will register to any available network.
-
- if (isEmergencyNumber
- && ((okToCallStatus == CallStatusCode.EMERGENCY_ONLY)
- || (okToCallStatus == CallStatusCode.OUT_OF_SERVICE))) {
- if (DBG) log("placeCall: Emergency number detected with status = " + okToCallStatus);
- okToCallStatus = CallStatusCode.SUCCESS;
- if (DBG) log("==> UPDATING status to: " + okToCallStatus);
- }
-
- if (okToCallStatus != CallStatusCode.SUCCESS) {
- // If this is an emergency call, launch the EmergencyCallHelperService
- // to turn on the radio and retry the call.
- if (isEmergencyNumber && (okToCallStatus == CallStatusCode.POWER_OFF)) {
- Log.i(TAG, "placeCall: Trying to make emergency call while POWER_OFF!");
-
- // If needed, lazily instantiate an EmergencyCallHelper instance.
- synchronized (this) {
- if (mEmergencyCallHelper == null) {
- mEmergencyCallHelper = new EmergencyCallHelper(this);
- }
- }
-
- // ...and kick off the "emergency call from airplane mode" sequence.
- mEmergencyCallHelper.startEmergencyCallFromAirplaneModeSequence(number);
-
- // Finally, return CallStatusCode.SUCCESS right now so
- // that the in-call UI will remain visible (in order to
- // display the progress indication.)
- // TODO: or maybe it would be more clear to return a whole
- // new CallStatusCode called "TURNING_ON_RADIO" here.
- // That way, we'd update inCallUiState.progressIndication from
- // the handleOutgoingCallError() method, rather than here.
- return CallStatusCode.SUCCESS;
- } else {
- // Otherwise, just return the (non-SUCCESS) status code
- // back to our caller.
- if (DBG) log("==> placeCallInternal(): non-success status: " + okToCallStatus);
-
- // Log failed call.
- // Note: Normally, many of these values we gather from the Connection object but
- // since no such object is created for unconnected calls, we have to build them
- // manually.
- // TODO: Try to restructure code so that we can handle failure-
- // condition call logging in a single place (placeCall()) that also has access to
- // the number we attempted to dial (not placeCall()).
- mCallLogger.logCall(null /* callerInfo */, number, 0 /* presentation */,
- Calls.OUTGOING_TYPE, System.currentTimeMillis(), 0 /* duration */);
-
- return okToCallStatus;
- }
- }
-
- // We have a valid number, so try to actually place a call:
- // make sure we pass along the intent's URI which is a
- // reference to the contact. We may have a provider gateway
- // phone number to use for the outgoing call.
- Uri contactUri = intent.getData();
-
- // If a gateway is used, extract the data here and pass that into placeCall.
- final RawGatewayInfo rawGatewayInfo = mCallGatewayManager.getRawGatewayInfo(intent, number);
-
- // Watch out: PhoneUtils.placeCall() returns one of the
- // CALL_STATUS_* constants, not a CallStatusCode enum value.
- int callStatus = PhoneUtils.placeCall(mApp,
- phone,
- number,
- contactUri,
- (isEmergencyNumber || isEmergencyIntent),
- rawGatewayInfo,
- mCallGatewayManager);
-
- switch (callStatus) {
- case PhoneUtils.CALL_STATUS_DIALED:
- if (VDBG) log("placeCall: PhoneUtils.placeCall() succeeded for regular call '"
- + number + "'.");
-
-
- // TODO(OTASP): still need more cleanup to simplify the mApp.cdma*State objects:
- // - Rather than checking inCallUiState.inCallScreenMode, the
- // code here could also check for
- // app.getCdmaOtaInCallScreenUiState() returning NORMAL.
- // - But overall, app.inCallUiState.inCallScreenMode and
- // app.cdmaOtaInCallScreenUiState.state are redundant.
- // Combine them.
-
- boolean voicemailUriSpecified = scheme != null &&
- scheme.equals(PhoneAccount.SCHEME_VOICEMAIL);
- // Check for an obscure ECM-related scenario: If the phone
- // is currently in ECM (Emergency callback mode) and we
- // dial a non-emergency number, that automatically
- // *cancels* ECM. So warn the user about it.
- // (See InCallScreen.showExitingECMDialog() for more info.)
- boolean exitedEcm = false;
- if (PhoneUtils.isPhoneInEcm(phone) && !isEmergencyNumber) {
- Log.i(TAG, "About to exit ECM because of an outgoing non-emergency call");
- exitedEcm = true; // this will cause us to return EXITED_ECM from this method
- }
-
- if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) {
- // Start the timer for 3 Way CallerInfo
- if (mApp.cdmaPhoneCallState.getCurrentCallState()
- == CdmaPhoneCallState.PhoneCallState.THRWAY_ACTIVE) {
-
- // This is a "CDMA 3-way call", which means that you're dialing a
- // 2nd outgoing call while a previous call is already in progress.
- //
- // Due to the limitations of CDMA this call doesn't actually go
- // through the DIALING/ALERTING states, so we can't tell for sure
- // when (or if) it's actually answered. But we want to show
- // *some* indication of what's going on in the UI, so we "fake it"
- // by displaying the "Dialing" state for 3 seconds.
-
- // Set the mThreeWayCallOrigStateDialing state to true
- mApp.cdmaPhoneCallState.setThreeWayCallOrigState(true);
-
- // Schedule the "Dialing" indication to be taken down in 3 seconds:
- sendEmptyMessageDelayed(THREEWAY_CALLERINFO_DISPLAY_DONE,
- THREEWAY_CALLERINFO_DISPLAY_TIME);
- }
- }
-
- // Success!
- if (exitedEcm) {
- return CallStatusCode.EXITED_ECM;
- } else {
- return CallStatusCode.SUCCESS;
- }
-
- case PhoneUtils.CALL_STATUS_DIALED_MMI:
- if (DBG) log("placeCall: specified number was an MMI code: '" + number + "'.");
- // The passed-in number was an MMI code, not a regular phone number!
- // This isn't really a failure; the Dialer may have deliberately
- // fired an ACTION_CALL intent to dial an MMI code, like for a
- // USSD call.
- //
- // Presumably an MMI_INITIATE message will come in shortly
- // (and we'll bring up the "MMI Started" dialog), or else
- // an MMI_COMPLETE will come in (which will take us to a
- // different Activity; see PhoneUtils.displayMMIComplete()).
- return CallStatusCode.DIALED_MMI;
-
- case PhoneUtils.CALL_STATUS_FAILED:
- Log.w(TAG, "placeCall: PhoneUtils.placeCall() FAILED for number '"
- + number + "'.");
- // We couldn't successfully place the call; there was some
- // failure in the telephony layer.
-
- // Log failed call.
- mCallLogger.logCall(null /* callerInfo */, number, 0 /* presentation */,
- Calls.OUTGOING_TYPE, System.currentTimeMillis(), 0 /* duration */);
-
- return CallStatusCode.CALL_FAILED;
-
- default:
- Log.wtf(TAG, "placeCall: unknown callStatus " + callStatus
- + " from PhoneUtils.placeCall() for number '" + number + "'.");
- return CallStatusCode.SUCCESS; // Try to continue anyway...
- }
- }
-
- /**
- * Checks the current ServiceState to make sure it's OK
- * to try making an outgoing call to the specified number.
- *
- * @return CallStatusCode.SUCCESS if it's OK to try calling the specified
- * number. If not, like if the radio is powered off or we have no
- * signal, return one of the other CallStatusCode codes indicating what
- * the problem is.
- */
- private CallStatusCode checkIfOkToInitiateOutgoingCall(int state) {
- if (VDBG) log("checkIfOkToInitiateOutgoingCall: ServiceState = " + state);
-
- switch (state) {
- case ServiceState.STATE_IN_SERVICE:
- // Normal operation. It's OK to make outgoing calls.
- return CallStatusCode.SUCCESS;
-
- case ServiceState.STATE_POWER_OFF:
- // Radio is explictly powered off.
- return CallStatusCode.POWER_OFF;
-
- case ServiceState.STATE_EMERGENCY_ONLY:
- // The phone is registered, but locked. Only emergency
- // numbers are allowed.
- // Note that as of Android 2.0 at least, the telephony layer
- // does not actually use ServiceState.STATE_EMERGENCY_ONLY,
- // mainly since there's no guarantee that the radio/RIL can
- // make this distinction. So in practice the
- // CallStatusCode.EMERGENCY_ONLY state and the string
- // "incall_error_emergency_only" are totally unused.
- return CallStatusCode.EMERGENCY_ONLY;
-
- case ServiceState.STATE_OUT_OF_SERVICE:
- // No network connection.
- return CallStatusCode.OUT_OF_SERVICE;
-
- default:
- throw new IllegalStateException("Unexpected ServiceState: " + state);
- }
- }
-
-
-
- /**
- * Handles the various error conditions that can occur when initiating
- * an outgoing call.
- *
- * Most error conditions are "handled" by simply displaying an error
- * message to the user.
- *
- * @param status one of the CallStatusCode error codes.
- */
- private void handleOutgoingCallError(CallStatusCode status) {
- if (DBG) log("handleOutgoingCallError(): status = " + status);
- final Intent intent = new Intent(mApp, ErrorDialogActivity.class);
- int errorMessageId = -1;
- switch (status) {
- case SUCCESS:
- // This case shouldn't happen; you're only supposed to call
- // handleOutgoingCallError() if there was actually an error!
- Log.wtf(TAG, "handleOutgoingCallError: SUCCESS isn't an error");
- break;
-
- case CALL_FAILED:
- // We couldn't successfully place the call; there was some
- // failure in the telephony layer.
- // TODO: Need UI spec for this failure case; for now just
- // show a generic error.
- errorMessageId = R.string.incall_error_call_failed;
- break;
- case POWER_OFF:
- // Radio is explictly powered off, presumably because the
- // device is in airplane mode.
- //
- // TODO: For now this UI is ultra-simple: we simply display
- // a message telling the user to turn off airplane mode.
- // But it might be nicer for the dialog to offer the option
- // to turn the radio on right there (and automatically retry
- // the call once network registration is complete.)
- errorMessageId = R.string.incall_error_power_off;
- break;
- case EMERGENCY_ONLY:
- // Only emergency numbers are allowed, but we tried to dial
- // a non-emergency number.
- // (This state is currently unused; see comments above.)
- errorMessageId = R.string.incall_error_emergency_only;
- break;
- case OUT_OF_SERVICE:
- // No network connection.
- errorMessageId = R.string.incall_error_out_of_service;
- break;
- case NO_PHONE_NUMBER_SUPPLIED:
- // The supplied Intent didn't contain a valid phone number.
- // (This is rare and should only ever happen with broken
- // 3rd-party apps.) For now just show a generic error.
- errorMessageId = R.string.incall_error_no_phone_number_supplied;
- break;
-
- case VOICEMAIL_NUMBER_MISSING:
- // Bring up the "Missing Voicemail Number" dialog, which
- // will ultimately take us to some other Activity (or else
- // just bail out of this activity.)
-
- // Send a request to the InCallScreen to display the
- // "voicemail missing" dialog when it (the InCallScreen)
- // comes to the foreground.
- intent.putExtra(ErrorDialogActivity.SHOW_MISSING_VOICEMAIL_NO_DIALOG_EXTRA, true);
- break;
-
- case DIALED_MMI:
- // Our initial phone number was actually an MMI sequence.
- // There's no real "error" here, but we do bring up the
- // a Toast (as requested of the New UI paradigm).
- //
- // In-call MMIs do not trigger the normal MMI Initiate
- // Notifications, so we should notify the user here.
- // Otherwise, the code in PhoneUtils.java should handle
- // user notifications in the form of Toasts or Dialogs.
- //
- // TODO: Rather than launching a toast from here, it would
- // be cleaner to just set a pending call status code here,
- // and then let the InCallScreen display the toast...
- final Intent mmiIntent = new Intent(mApp, MMIDialogActivity.class);
- mmiIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
- Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
- mApp.startActivity(mmiIntent);
- return;
- default:
- Log.wtf(TAG, "handleOutgoingCallError: unexpected status code " + status);
- // Show a generic "call failed" error.
- errorMessageId = R.string.incall_error_call_failed;
- break;
- }
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
- if (errorMessageId != -1) {
- intent.putExtra(ErrorDialogActivity.ERROR_MESSAGE_ID_EXTRA, errorMessageId);
- }
- mApp.startActivity(intent);
- }
-
- //
- // Debugging
- //
-
- private static void log(String msg) {
- Log.d(TAG, msg);
- }
-}
diff --git a/src/com/android/phone/CallFeaturesSetting.java b/src/com/android/phone/CallFeaturesSetting.java
index 5a228a8..8da980c 100644
--- a/src/com/android/phone/CallFeaturesSetting.java
+++ b/src/com/android/phone/CallFeaturesSetting.java
@@ -101,6 +101,7 @@
private SwitchPreference mButtonAutoRetry;
private PreferenceScreen mVoicemailSettingsScreen;
private SwitchPreference mEnableVideoCalling;
+ private Preference mButtonWifiCalling;
/*
* Click Listeners, handle click based on objects attached to UI.
@@ -194,7 +195,14 @@
public void onCallStateChanged(int state, String incomingNumber) {
if (DBG) log("PhoneStateListener onCallStateChanged: state is " + state);
if (mEnableVideoCalling != null) {
- mEnableVideoCalling.setEnabled(state == TelephonyManager.CALL_STATE_IDLE);
+ // Use TelephonyManager#getCallStete instead of 'state' parameter because it needs
+ // to check the current state of all phone calls.
+ TelephonyManager telephonyManager =
+ (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
+ mEnableVideoCalling.setEnabled(
+ telephonyManager.getCallState() == TelephonyManager.CALL_STATE_IDLE);
+ mButtonWifiCalling.setEnabled(
+ telephonyManager.getCallState() == TelephonyManager.CALL_STATE_IDLE);
}
}
};
@@ -234,6 +242,8 @@
mButtonAutoRetry = (SwitchPreference) findPreference(BUTTON_RETRY_KEY);
mEnableVideoCalling = (SwitchPreference) findPreference(ENABLE_VIDEO_CALLING_KEY);
+ mButtonWifiCalling = findPreference(getResources().getString(
+ R.string.wifi_calling_settings_key));
PersistableBundle carrierConfig =
PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
@@ -304,9 +314,6 @@
/* tm.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE); */
}
- Preference wifiCallingSettings = findPreference(
- getResources().getString(R.string.wifi_calling_settings_key));
-
final PhoneAccountHandle simCallManager = mTelecomManager.getSimCallManager();
if (simCallManager != null) {
Intent intent = PhoneAccountSettingsFragment.buildPhoneAccountConfigureIntent(
@@ -315,17 +322,17 @@
PackageManager pm = mPhone.getContext().getPackageManager();
List<ResolveInfo> resolutions = pm.queryIntentActivities(intent, 0);
if (!resolutions.isEmpty()) {
- wifiCallingSettings.setTitle(resolutions.get(0).loadLabel(pm));
- wifiCallingSettings.setSummary(null);
- wifiCallingSettings.setIntent(intent);
+ mButtonWifiCalling.setTitle(resolutions.get(0).loadLabel(pm));
+ mButtonWifiCalling.setSummary(null);
+ mButtonWifiCalling.setIntent(intent);
} else {
- prefSet.removePreference(wifiCallingSettings);
+ prefSet.removePreference(mButtonWifiCalling);
}
} else {
- prefSet.removePreference(wifiCallingSettings);
+ prefSet.removePreference(mButtonWifiCalling);
}
} else if (!mImsMgr.isWfcEnabledByPlatform() || !mImsMgr.isWfcProvisionedOnDevice()) {
- prefSet.removePreference(wifiCallingSettings);
+ prefSet.removePreference(mButtonWifiCalling);
} else {
int resId = com.android.internal.R.string.wifi_calling_off_summary;
if (mImsMgr.isWfcEnabledByUser()) {
@@ -345,19 +352,19 @@
if (DBG) log("Unexpected WFC mode value: " + wfcMode);
}
}
- wifiCallingSettings.setSummary(resId);
+ mButtonWifiCalling.setSummary(resId);
}
try {
if (mImsMgr.getImsServiceState() != ImsFeature.STATE_READY) {
log("Feature state not ready so remove vt and wfc settings for "
+ " phone =" + mPhone.getPhoneId());
- prefSet.removePreference(wifiCallingSettings);
+ prefSet.removePreference(mButtonWifiCalling);
prefSet.removePreference(mEnableVideoCalling);
}
} catch (ImsException ex) {
log("Exception when trying to get ImsServiceStatus: " + ex);
- prefSet.removePreference(wifiCallingSettings);
+ prefSet.removePreference(mButtonWifiCalling);
prefSet.removePreference(mEnableVideoCalling);
}
}
diff --git a/src/com/android/phone/EmergencyCallHelper.java b/src/com/android/phone/EmergencyCallHelper.java
deleted file mode 100644
index 74ce088..0000000
--- a/src/com/android/phone/EmergencyCallHelper.java
+++ /dev/null
@@ -1,513 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.phone;
-
-import com.android.internal.telephony.CallManager;
-import com.android.internal.telephony.Connection;
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneConstants;
-
-import android.content.Context;
-import android.content.Intent;
-import android.os.AsyncResult;
-import android.os.Handler;
-import android.os.Message;
-import android.os.PowerManager;
-import android.os.UserHandle;
-import android.provider.Settings;
-import android.telephony.DisconnectCause;
-import android.telephony.ServiceState;
-import android.util.Log;
-
-
-/**
- * Helper class for the {@link CallController} that implements special
- * behavior related to emergency calls. Specifically, this class handles
- * the case of the user trying to dial an emergency number while the radio
- * is off (i.e. the device is in airplane mode), by forcibly turning the
- * radio back on, waiting for it to come up, and then retrying the
- * emergency call.
- *
- * This class is instantiated lazily (the first time the user attempts to
- * make an emergency call from airplane mode) by the the
- * {@link CallController} singleton.
- */
-public class EmergencyCallHelper extends Handler {
- private static final String TAG = "EmergencyCallHelper";
- private static final boolean DBG = false;
-
- // Number of times to retry the call, and time between retry attempts.
- public static final int MAX_NUM_RETRIES = 6;
- public static final long TIME_BETWEEN_RETRIES = 5000; // msec
-
- // Timeout used with our wake lock (just as a safety valve to make
- // sure we don't hold it forever).
- public static final long WAKE_LOCK_TIMEOUT = 5 * 60 * 1000; // 5 minutes in msec
-
- // Handler message codes; see handleMessage()
- private static final int START_SEQUENCE = 1;
- private static final int SERVICE_STATE_CHANGED = 2;
- private static final int DISCONNECT = 3;
- private static final int RETRY_TIMEOUT = 4;
-
- private CallController mCallController;
- private PhoneGlobals mApp;
- private CallManager mCM;
- private String mNumber; // The emergency number we're trying to dial
- private int mNumRetriesSoFar;
-
- // Wake lock we hold while running the whole sequence
- private PowerManager.WakeLock mPartialWakeLock;
-
- public EmergencyCallHelper(CallController callController) {
- if (DBG) log("EmergencyCallHelper constructor...");
- mCallController = callController;
- mApp = PhoneGlobals.getInstance();
- mCM = mApp.mCM;
- }
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case START_SEQUENCE:
- startSequenceInternal(msg);
- break;
- case SERVICE_STATE_CHANGED:
- onServiceStateChanged(msg);
- break;
- case DISCONNECT:
- onDisconnect(msg);
- break;
- case RETRY_TIMEOUT:
- onRetryTimeout();
- break;
- default:
- Log.wtf(TAG, "handleMessage: unexpected message: " + msg);
- break;
- }
- }
-
- /**
- * Starts the "emergency call from airplane mode" sequence.
- *
- * This is the (single) external API of the EmergencyCallHelper class.
- * This method is called from the CallController placeCall() sequence
- * if the user dials a valid emergency number, but the radio is
- * powered-off (presumably due to airplane mode.)
- *
- * This method kicks off the following sequence:
- * - Power on the radio
- * - Listen for the service state change event telling us the radio has come up
- * - Then launch the emergency call
- * - Retry if the call fails with an OUT_OF_SERVICE error
- * - Retry if we've gone 5 seconds without any response from the radio
- * - Finally, clean up any leftover state (progress UI, wake locks, etc.)
- *
- * This method is safe to call from any thread, since it simply posts
- * a message to the EmergencyCallHelper's handler (thus ensuring that
- * the rest of the sequence is entirely serialized, and runs only on
- * the handler thread.)
- *
- * This method does *not* force the in-call UI to come up; our caller
- * is responsible for doing that (presumably by calling
- * PhoneApp.displayCallScreen().)
- */
- public void startEmergencyCallFromAirplaneModeSequence(String number) {
- if (DBG) log("startEmergencyCallFromAirplaneModeSequence('" + number + "')...");
- Message msg = obtainMessage(START_SEQUENCE, number);
- sendMessage(msg);
- }
-
- /**
- * Actual implementation of startEmergencyCallFromAirplaneModeSequence(),
- * guaranteed to run on the handler thread.
- * @see #startEmergencyCallFromAirplaneModeSequence
- */
- private void startSequenceInternal(Message msg) {
- if (DBG) log("startSequenceInternal(): msg = " + msg);
-
- // First of all, clean up any state (including mPartialWakeLock!)
- // left over from a prior emergency call sequence.
- // This ensures that we'll behave sanely if another
- // startEmergencyCallFromAirplaneModeSequence() comes in while
- // we're already in the middle of the sequence.
- cleanup();
-
- mNumber = (String) msg.obj;
- if (DBG) log("- startSequenceInternal: Got mNumber: '" + mNumber + "'");
-
- mNumRetriesSoFar = 0;
-
- // Wake lock to make sure the processor doesn't go to sleep midway
- // through the emergency call sequence.
- PowerManager pm = (PowerManager) mApp.getSystemService(Context.POWER_SERVICE);
- mPartialWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
- // Acquire with a timeout, just to be sure we won't hold the wake
- // lock forever even if a logic bug (in this class) causes us to
- // somehow never call cleanup().
- if (DBG) log("- startSequenceInternal: acquiring wake lock");
- mPartialWakeLock.acquire(WAKE_LOCK_TIMEOUT);
-
- // No need to check the current service state here, since the only
- // reason the CallController would call this method in the first
- // place is if the radio is powered-off.
- //
- // So just go ahead and turn the radio on.
-
- powerOnRadio(); // We'll get an onServiceStateChanged() callback
- // when the radio successfully comes up.
-
- // Next step: when the SERVICE_STATE_CHANGED event comes in,
- // we'll retry the call; see placeEmergencyCall();
- // But also, just in case, start a timer to make sure we'll retry
- // the call even if the SERVICE_STATE_CHANGED event never comes in
- // for some reason.
- startRetryTimer();
-
- // (Our caller is responsible for calling mApp.displayCallScreen().)
- }
-
- /**
- * Handles the SERVICE_STATE_CHANGED event.
- *
- * (Normally this event tells us that the radio has finally come
- * up. In that case, it's now safe to actually place the
- * emergency call.)
- */
- private void onServiceStateChanged(Message msg) {
- ServiceState state = (ServiceState) ((AsyncResult) msg.obj).result;
- if (DBG) log("onServiceStateChanged()... new state = " + state);
-
- // Possible service states:
- // - STATE_IN_SERVICE // Normal operation
- // - STATE_OUT_OF_SERVICE // Still searching for an operator to register to,
- // // or no radio signal
- // - STATE_EMERGENCY_ONLY // Phone is locked; only emergency numbers are allowed
- // - STATE_POWER_OFF // Radio is explicitly powered off (airplane mode)
-
- // Once we reach either STATE_IN_SERVICE or STATE_EMERGENCY_ONLY,
- // it's finally OK to place the emergency call.
- boolean okToCall = (state.getState() == ServiceState.STATE_IN_SERVICE)
- || (state.getState() == ServiceState.STATE_EMERGENCY_ONLY);
-
- if (okToCall) {
- // Woo hoo! It's OK to actually place the call.
- if (DBG) log("onServiceStateChanged: ok to call!");
-
- // Deregister for the service state change events.
- unregisterForServiceStateChanged();
-
- placeEmergencyCall();
- } else {
- // The service state changed, but we're still not ready to call yet.
- // (This probably was the transition from STATE_POWER_OFF to
- // STATE_OUT_OF_SERVICE, which happens immediately after powering-on
- // the radio.)
- //
- // So just keep waiting; we'll probably get to either
- // STATE_IN_SERVICE or STATE_EMERGENCY_ONLY very shortly.
- // (Or even if that doesn't happen, we'll at least do another retry
- // when the RETRY_TIMEOUT event fires.)
- if (DBG) log("onServiceStateChanged: not ready to call yet, keep waiting...");
- }
- }
-
- /**
- * Handles a DISCONNECT event from the telephony layer.
- *
- * Even after we successfully place an emergency call (after powering
- * on the radio), it's still possible for the call to fail with the
- * disconnect cause OUT_OF_SERVICE. If so, schedule a retry.
- */
- private void onDisconnect(Message msg) {
- Connection conn = (Connection) ((AsyncResult) msg.obj).result;
- int cause = conn.getDisconnectCause();
- if (DBG) log("onDisconnect: connection '" + conn
- + "', addr '" + conn.getAddress()
- + "', cause = " + DisconnectCause.toString(cause));
-
- if (cause == DisconnectCause.OUT_OF_SERVICE) {
- // Wait a bit more and try again (or just bail out totally if
- // we've had too many failures.)
- if (DBG) log("- onDisconnect: OUT_OF_SERVICE, need to retry...");
- scheduleRetryOrBailOut();
- } else {
- // Any other disconnect cause means we're done.
- // Either the emergency call succeeded *and* ended normally,
- // or else there was some error that we can't retry. In either
- // case, just clean up our internal state.)
-
- if (DBG) log("==> Disconnect event; clean up...");
- cleanup();
-
- // Nothing else to do here. If the InCallScreen was visible,
- // it would have received this disconnect event too (so it'll
- // show the "Call ended" state and finish itself without any
- // help from us.)
- }
- }
-
- /**
- * Handles the retry timer expiring.
- */
- private void onRetryTimeout() {
- PhoneConstants.State phoneState = mCM.getState();
- int serviceState = mCM.getDefaultPhone().getServiceState().getState();
- if (DBG) log("onRetryTimeout(): phone state " + phoneState
- + ", service state " + serviceState
- + ", mNumRetriesSoFar = " + mNumRetriesSoFar);
-
- // - If we're actually in a call, we've succeeded.
- //
- // - Otherwise, if the radio is now on, that means we successfully got
- // out of airplane mode but somehow didn't get the service state
- // change event. In that case, try to place the call.
- //
- // - If the radio is still powered off, try powering it on again.
-
- if (phoneState == PhoneConstants.State.OFFHOOK) {
- if (DBG) log("- onRetryTimeout: Call is active! Cleaning up...");
- cleanup();
- return;
- }
-
- if (serviceState != ServiceState.STATE_POWER_OFF) {
- // Woo hoo -- we successfully got out of airplane mode.
-
- // Deregister for the service state change events; we don't need
- // these any more now that the radio is powered-on.
- unregisterForServiceStateChanged();
-
- placeEmergencyCall(); // If the call fails, placeEmergencyCall()
- // will schedule a retry.
- } else {
- // Uh oh; we've waited the full TIME_BETWEEN_RETRIES and the
- // radio is still not powered-on. Try again...
-
- if (DBG) log("- Trying (again) to turn on the radio...");
- powerOnRadio(); // Again, we'll (hopefully) get an onServiceStateChanged()
- // callback when the radio successfully comes up.
-
- // ...and also set a fresh retry timer (or just bail out
- // totally if we've had too many failures.)
- scheduleRetryOrBailOut();
- }
- }
-
- /**
- * Attempt to power on the radio (i.e. take the device out
- * of airplane mode.)
- *
- * Additionally, start listening for service state changes;
- * we'll eventually get an onServiceStateChanged() callback
- * when the radio successfully comes up.
- */
- private void powerOnRadio() {
- if (DBG) log("- powerOnRadio()...");
-
- // We're about to turn on the radio, so arrange to be notified
- // when the sequence is complete.
- registerForServiceStateChanged();
-
- // If airplane mode is on, we turn it off the same way that the
- // Settings activity turns it off.
- if (Settings.Global.getInt(mApp.getContentResolver(),
- Settings.Global.AIRPLANE_MODE_ON, 0) > 0) {
- if (DBG) log("==> Turning off airplane mode...");
-
- // Change the system setting
- Settings.Global.putInt(mApp.getContentResolver(),
- Settings.Global.AIRPLANE_MODE_ON, 0);
-
- // Post the intent
- Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
- intent.putExtra("state", false);
- mApp.sendBroadcastAsUser(intent, UserHandle.ALL);
- } else {
- // Otherwise, for some strange reason the radio is off
- // (even though the Settings database doesn't think we're
- // in airplane mode.) In this case just turn the radio
- // back on.
- if (DBG) log("==> (Apparently) not in airplane mode; manually powering radio on...");
- mCM.getDefaultPhone().setRadioPower(true);
- }
- }
-
- /**
- * Actually initiate the outgoing emergency call.
- * (We do this once the radio has successfully been powered-up.)
- *
- * If the call succeeds, we're done.
- * If the call fails, schedule a retry of the whole sequence.
- */
- private void placeEmergencyCall() {
- if (DBG) log("placeEmergencyCall()...");
-
- // Place an outgoing call to mNumber.
- // Note we call PhoneUtils.placeCall() directly; we don't want any
- // of the behavior from CallController.placeCallInternal() here.
- // (Specifically, we don't want to start the "emergency call from
- // airplane mode" sequence from the beginning again!)
-
- registerForDisconnect(); // Get notified when this call disconnects
-
- if (DBG) log("- placing call to '" + mNumber + "'...");
- int callStatus = PhoneUtils.placeCall(mApp,
- mCM.getDefaultPhone(),
- mNumber,
- null, // contactUri
- true); // isEmergencyCall
- if (DBG) log("- PhoneUtils.placeCall() returned status = " + callStatus);
-
- boolean success;
- // Note PhoneUtils.placeCall() returns one of the CALL_STATUS_*
- // constants, not a CallStatusCode enum value.
- switch (callStatus) {
- case PhoneUtils.CALL_STATUS_DIALED:
- success = true;
- break;
-
- case PhoneUtils.CALL_STATUS_DIALED_MMI:
- case PhoneUtils.CALL_STATUS_FAILED:
- default:
- // Anything else is a failure, and we'll need to retry.
- Log.w(TAG, "placeEmergencyCall(): placeCall() failed: callStatus = " + callStatus);
- success = false;
- break;
- }
-
- if (success) {
- if (DBG) log("==> Success from PhoneUtils.placeCall()!");
- // Ok, the emergency call is (hopefully) under way.
-
- // We're not done yet, though, so don't call cleanup() here.
- // (It's still possible that this call will fail, and disconnect
- // with cause==OUT_OF_SERVICE. If so, that will trigger a retry
- // from the onDisconnect() method.)
- } else {
- if (DBG) log("==> Failure.");
- // Wait a bit more and try again (or just bail out totally if
- // we've had too many failures.)
- scheduleRetryOrBailOut();
- }
- }
-
- /**
- * Schedules a retry in response to some failure (either the radio
- * failing to power on, or a failure when trying to place the call.)
- * Or, if we've hit the retry limit, bail out of this whole sequence
- * and display a failure message to the user.
- */
- private void scheduleRetryOrBailOut() {
- mNumRetriesSoFar++;
- if (DBG) log("scheduleRetryOrBailOut()... mNumRetriesSoFar is now " + mNumRetriesSoFar);
-
- if (mNumRetriesSoFar > MAX_NUM_RETRIES) {
- Log.w(TAG, "scheduleRetryOrBailOut: hit MAX_NUM_RETRIES; giving up...");
- cleanup();
- } else {
- if (DBG) log("- Scheduling another retry...");
- startRetryTimer();
- }
- }
-
- /**
- * Clean up when done with the whole sequence: either after
- * successfully placing *and* ending the emergency call, or after
- * bailing out because of too many failures.
- *
- * The exact cleanup steps are:
- * - Take down any progress UI (and also ask the in-call UI to refresh itself,
- * if it's still visible)
- * - Double-check that we're not still registered for any telephony events
- * - Clean up any extraneous handler messages (like retry timeouts) still in the queue
- * - Make sure we're not still holding any wake locks
- *
- * Basically this method guarantees that there will be no more
- * activity from the EmergencyCallHelper until the CallController
- * kicks off the whole sequence again with another call to
- * startEmergencyCallFromAirplaneModeSequence().
- *
- * Note we don't call this method simply after a successful call to
- * placeCall(), since it's still possible the call will disconnect
- * very quickly with an OUT_OF_SERVICE error.
- */
- private void cleanup() {
- if (DBG) log("cleanup()...");
-
- unregisterForServiceStateChanged();
- unregisterForDisconnect();
- cancelRetryTimer();
-
- // Release / clean up the wake lock
- if (mPartialWakeLock != null) {
- if (mPartialWakeLock.isHeld()) {
- if (DBG) log("- releasing wake lock");
- mPartialWakeLock.release();
- }
- mPartialWakeLock = null;
- }
- }
-
- private void startRetryTimer() {
- removeMessages(RETRY_TIMEOUT);
- sendEmptyMessageDelayed(RETRY_TIMEOUT, TIME_BETWEEN_RETRIES);
- }
-
- private void cancelRetryTimer() {
- removeMessages(RETRY_TIMEOUT);
- }
-
- private void registerForServiceStateChanged() {
- // Unregister first, just to make sure we never register ourselves
- // twice. (We need this because Phone.registerForServiceStateChanged()
- // does not prevent multiple registration of the same handler.)
- Phone phone = mCM.getDefaultPhone();
- phone.unregisterForServiceStateChanged(this); // Safe even if not currently registered
- phone.registerForServiceStateChanged(this, SERVICE_STATE_CHANGED, null);
- }
-
- private void unregisterForServiceStateChanged() {
- // This method is safe to call even if we haven't set mPhone yet.
- Phone phone = mCM.getDefaultPhone();
- if (phone != null) {
- phone.unregisterForServiceStateChanged(this); // Safe even if unnecessary
- }
- removeMessages(SERVICE_STATE_CHANGED); // Clean up any pending messages too
- }
-
- private void registerForDisconnect() {
- // Note: no need to unregister first, since
- // CallManager.registerForDisconnect() automatically prevents
- // multiple registration of the same handler.
- mCM.registerForDisconnect(this, DISCONNECT, null);
- }
-
- private void unregisterForDisconnect() {
- mCM.unregisterForDisconnect(this); // Safe even if not currently registered
- removeMessages(DISCONNECT); // Clean up any pending messages too
- }
-
-
- //
- // Debugging
- //
-
- private static void log(String msg) {
- Log.d(TAG, msg);
- }
-}
diff --git a/src/com/android/phone/EmergencyCallbackModeExitDialog.java b/src/com/android/phone/EmergencyCallbackModeExitDialog.java
index 765c52d..9d43d60 100644
--- a/src/com/android/phone/EmergencyCallbackModeExitDialog.java
+++ b/src/com/android/phone/EmergencyCallbackModeExitDialog.java
@@ -17,9 +17,9 @@
package com.android.phone;
import android.app.Activity;
+import android.app.AlertDialog;
import android.app.Dialog;
import android.app.ProgressDialog;
-import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -28,7 +28,6 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
-import android.content.res.Resources;
import android.os.AsyncResult;
import android.os.Bundle;
import android.os.CountDownTimer;
@@ -36,12 +35,10 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
-import android.os.SystemProperties;
import android.util.Log;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.TelephonyIntents;
-import com.android.internal.telephony.TelephonyProperties;
/**
* Displays dialog that enables users to exit Emergency Callback Mode
@@ -80,12 +77,12 @@
mPhone = PhoneGlobals.getInstance().getPhoneInEcm();
// Check if phone is in Emergency Callback Mode. If not, exit.
- final boolean isInEcm = mPhone.isInEcm();
- Log.i(TAG, "ECMModeExitDialog launched - isInEcm: " + isInEcm + " phone:" + mPhone);
- if (mPhone == null || !isInEcm) {
+ if (mPhone == null || !mPhone.isInEcm()) {
+ Log.i(TAG, "ECMModeExitDialog launched - isInEcm: false" + " phone:" + mPhone);
finish();
return;
}
+ Log.i(TAG, "ECMModeExitDialog launched - isInEcm: true" + " phone:" + mPhone);
mHandler = new Handler();
diff --git a/src/com/android/phone/ErrorDialogActivity.java b/src/com/android/phone/ErrorDialogActivity.java
deleted file mode 100644
index bf09376..0000000
--- a/src/com/android/phone/ErrorDialogActivity.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.phone;
-
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.os.Bundle;
-import android.util.Log;
-
-import com.android.phone.settings.VoicemailSettingsActivity;
-
-/**
- * Used to display an error dialog from within the Telephony service when an outgoing call fails
- */
-public class ErrorDialogActivity extends Activity {
- private static final String TAG = ErrorDialogActivity.class.getSimpleName();
-
- public static final String SHOW_MISSING_VOICEMAIL_NO_DIALOG_EXTRA = "show_missing_voicemail";
- public static final String ERROR_MESSAGE_ID_EXTRA = "error_message_id";
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
-
- final boolean showVoicemailDialog = getIntent().getBooleanExtra(
- SHOW_MISSING_VOICEMAIL_NO_DIALOG_EXTRA, false);
-
- if (showVoicemailDialog) {
- showMissingVoicemailErrorDialog();
- } else {
- final int error = getIntent().getIntExtra(ERROR_MESSAGE_ID_EXTRA, -1);
- if (error == -1) {
- Log.e(TAG, "ErrorDialogActivity called with no error type extra.");
- finish();
- }
- showGenericErrorDialog(error);
- }
- }
-
- private void showGenericErrorDialog(int resid) {
- final CharSequence msg = getResources().getText(resid);
-
- final DialogInterface.OnClickListener clickListener;
-
- final DialogInterface.OnCancelListener cancelListener;
- clickListener = new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- finish();
- }
- };
- cancelListener = new DialogInterface.OnCancelListener() {
- @Override
- public void onCancel(DialogInterface dialog) {
- finish();
- }
- };
-
- final AlertDialog errorDialog = new AlertDialog.Builder(this)
- .setMessage(msg).setPositiveButton(R.string.ok, clickListener)
- .setOnCancelListener(cancelListener).create();
-
- errorDialog.show();
- }
-
- private void showMissingVoicemailErrorDialog() {
- final AlertDialog missingVoicemailDialog = new AlertDialog.Builder(this)
- .setTitle(R.string.no_vm_number)
- .setMessage(R.string.no_vm_number_msg)
- .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- dontAddVoiceMailNumber();
- }})
- .setNegativeButton(R.string.add_vm_number_str, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- addVoiceMailNumberPanel(dialog);
- }})
- .setOnCancelListener(new DialogInterface.OnCancelListener() {
- @Override
- public void onCancel(DialogInterface dialog) {
- dontAddVoiceMailNumber();
- }}).show();
- }
-
-
- private void addVoiceMailNumberPanel(DialogInterface dialog) {
- if (dialog != null) {
- dialog.dismiss();
- }
-
- // navigate to the Voicemail setting in the Call Settings activity.
- Intent intent = new Intent(VoicemailSettingsActivity.ACTION_ADD_VOICEMAIL);
- intent.setClass(this, VoicemailSettingsActivity.class);
- startActivity(intent);
- finish();
- }
-
- private void dontAddVoiceMailNumber() {
- finish();
- }
-}
diff --git a/src/com/android/phone/LocationAccessPolicy.java b/src/com/android/phone/LocationAccessPolicy.java
deleted file mode 100644
index 6f2a5ec..0000000
--- a/src/com/android/phone/LocationAccessPolicy.java
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.phone;
-
-import android.Manifest;
-import android.annotation.NonNull;
-import android.annotation.UserIdInt;
-import android.app.ActivityManager;
-import android.app.AppOpsManager;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.content.pm.UserInfo;
-import android.os.Build;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.provider.Settings;
-
-import java.util.List;
-
-/**
- * Helper for performing location access checks.
- */
-final class LocationAccessPolicy {
-
- private LocationAccessPolicy() {
- /* do nothing - hide ctor */
- }
-
- /**
- * API to determine if the caller has permissions to get cell location.
- *
- * @param pkgName Package name of the application requesting access
- * @param uid The uid of the package
- * @param message Message to add to the exception if no location permission
- * @return boolean true or false if permissions is granted
- */
- static boolean canAccessCellLocation(@NonNull Context context, @NonNull String pkgName,
- int uid, String message) throws SecurityException {
- context.getSystemService(AppOpsManager.class).checkPackage(uid, pkgName);
- // We always require the location permission and also require the
- // location mode to be on for non-legacy apps. Legacy apps are
- // required to be in the foreground to at least mitigate the case
- // where a legacy app the user is not using tracks their location.
-
- // Grating ACCESS_FINE_LOCATION to an app automatically grants it ACCESS_COARSE_LOCATION.
- context.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION, message);
- final int opCode = AppOpsManager.permissionToOpCode(
- Manifest.permission.ACCESS_COARSE_LOCATION);
- if (opCode != AppOpsManager.OP_NONE && context.getSystemService(AppOpsManager.class)
- .noteOp(opCode, uid, pkgName) != AppOpsManager.MODE_ALLOWED) {
- return false;
- }
- if (!isLocationModeEnabled(context, UserHandle.getUserId(uid))
- && !isLegacyForeground(context, pkgName)) {
- return false;
- }
- // If the user or profile is current, permission is granted.
- // Otherwise, uid must have INTERACT_ACROSS_USERS_FULL permission.
- return isCurrentProfile(context, uid) || checkInteractAcrossUsersFull(context);
- }
-
- private static boolean isLocationModeEnabled(@NonNull Context context, @UserIdInt int userId) {
- return Settings.Secure.getIntForUser(context.getContentResolver(),
- Settings.Secure.LOCATION_MODE, Settings.Secure.LOCATION_MODE_OFF, userId)
- != Settings.Secure.LOCATION_MODE_OFF;
- }
-
- private static boolean isLegacyForeground(@NonNull Context context, @NonNull String pkgName) {
- return isLegacyVersion(context, pkgName) && isForegroundApp(context, pkgName);
- }
-
- private static boolean isLegacyVersion(@NonNull Context context, @NonNull String pkgName) {
- try {
- if (context.getPackageManager().getApplicationInfo(pkgName, 0)
- .targetSdkVersion <= Build.VERSION_CODES.O) {
- return true;
- }
- } catch (PackageManager.NameNotFoundException e) {
- // In case of exception, assume known app (more strict checking)
- // Note: This case will never happen since checkPackage is
- // called to verify validity before checking app's version.
- }
- return false;
- }
-
- private static boolean isForegroundApp(@NonNull Context context, @NonNull String pkgName) {
- final ActivityManager am = context.getSystemService(ActivityManager.class);
- final List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(1);
- if (!tasks.isEmpty()) {
- return pkgName.equals(tasks.get(0).topActivity.getPackageName());
- }
- return false;
- }
-
- private static boolean checkInteractAcrossUsersFull(@NonNull Context context) {
- return context.checkCallingOrSelfPermission(
- android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
- == PackageManager.PERMISSION_GRANTED;
- }
-
- private static boolean isCurrentProfile(@NonNull Context context, int uid) {
- final int currentUser = ActivityManager.getCurrentUser();
- final int callingUserId = UserHandle.getUserId(uid);
- if (callingUserId == currentUser) {
- return true;
- } else {
- List<UserInfo> userProfiles = context.getSystemService(
- UserManager.class).getProfiles(currentUser);
- for (UserInfo user: userProfiles) {
- if (user.id == callingUserId) {
- return true;
- }
- }
- }
- return false;
- }
-}
diff --git a/src/com/android/phone/MMIDialogActivity.java b/src/com/android/phone/MMIDialogActivity.java
index 4afa6be..c9be2ac 100644
--- a/src/com/android/phone/MMIDialogActivity.java
+++ b/src/com/android/phone/MMIDialogActivity.java
@@ -25,7 +25,6 @@
import android.os.Message;
import android.telephony.SubscriptionManager;
import android.util.Log;
-import android.widget.Toast;
import com.android.internal.telephony.CallManager;
import com.android.internal.telephony.MmiCode;
@@ -56,6 +55,11 @@
int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
mPhone = PhoneGlobals.getPhone(subId);
+ if (mPhone == null) {
+ Log.w(TAG, "onCreate: invalid subscription id (" + subId + ") lead to null"
+ + " Phone.");
+ finish();
+ }
mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
diff --git a/src/com/android/phone/MobileNetworkSettings.java b/src/com/android/phone/MobileNetworkSettings.java
index 96309ad..5dc57dd 100644
--- a/src/com/android/phone/MobileNetworkSettings.java
+++ b/src/com/android/phone/MobileNetworkSettings.java
@@ -264,7 +264,7 @@
private boolean mIsGlobalCdma;
private boolean mUnavailable;
- private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
+ private class PhoneCallStateListener extends PhoneStateListener {
/*
* Enable/disable the 'Enhanced 4G LTE Mode' when in/out of a call
* and depending on TTY mode and TTY support over VoLTE.
@@ -275,16 +275,31 @@
public void onCallStateChanged(int state, String incomingNumber) {
if (DBG) log("PhoneStateListener.onCallStateChanged: state=" + state);
- int subId = mPhone != null
- ? mPhone.getSubId() : SubscriptionManager.INVALID_SUBSCRIPTION_ID;
- PersistableBundle carrierConfig =
- PhoneGlobals.getInstance().getCarrierConfigForSubId(subId);
- boolean enabled = is4gLtePrefEnabled(carrierConfig);
- Preference pref = getPreferenceScreen().findPreference(BUTTON_4G_LTE_KEY);
- if (pref != null) pref.setEnabled(enabled && hasActiveSubscriptions());
-
+ updateEnhanced4gLteState();
+ updateWiFiCallState();
+ updateVideoCallState();
}
- };
+
+ /*
+ * Listen to different subId if mPhone is updated.
+ */
+ protected void updatePhone() {
+ int newSubId = (mPhone != null
+ && SubscriptionManager.isValidSubscriptionId(mPhone.getSubId()))
+ ? mPhone.getSubId()
+ : SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+
+ // Now, listen to new subId if it's valid.
+ mTelephonyManager.listen(this, PhoneStateListener.LISTEN_NONE);
+
+ mSubId = newSubId;
+ if (SubscriptionManager.isValidSubscriptionId(mSubId)) {
+ mTelephonyManager.listen(this, PhoneStateListener.LISTEN_CALL_STATE);
+ }
+ }
+ }
+
+ private final PhoneCallStateListener mPhoneStateListener = new PhoneCallStateListener();
/**
* Service connection code for the NetworkQueryService.
@@ -576,7 +591,6 @@
int phoneId = SubscriptionManager.getPhoneId(sir.getSubscriptionId());
if (SubscriptionManager.isValidPhoneId(phoneId)) {
mPhone = PhoneFactory.getPhone(phoneId);
- mImsMgr = ImsManager.getInstance(getContext(), phoneId);
}
}
if (mPhone == null) {
@@ -586,11 +600,14 @@
Log.i(LOG_TAG, "updatePhone:- slotId=" + slotId + " sir=" + sir);
mImsMgr = ImsManager.getInstance(mPhone.getContext(), mPhone.getPhoneId());
+ mTelephonyManager = new TelephonyManager(mPhone.getContext(), mPhone.getSubId());
if (mImsMgr == null) {
log("updatePhone :: Could not get ImsManager instance!");
} else if (DBG) {
log("updatePhone :: mImsMgr=" + mImsMgr);
}
+
+ mPhoneStateListener.updatePhone();
}
private TabHost.TabContentFactory mEmptyTabContent = new TabHost.TabContentFactory() {
@@ -1044,7 +1061,6 @@
* but you do need to remember that this all needs to work when subscriptions
* change dynamically such as when hot swapping sims.
*/
- boolean canChange4glte = is4gLtePrefEnabled(carrierConfig);
boolean useVariant4glteTitle = carrierConfig.getBoolean(
CarrierConfigManager.KEY_ENHANCED_4G_LTE_TITLE_VARIANT_BOOL);
int enhanced4glteModeTitleId = useVariant4glteTitle ?
@@ -1054,7 +1070,6 @@
mButtonPreferredNetworkMode.setEnabled(hasActiveSubscriptions);
mButtonEnabledNetworks.setEnabled(hasActiveSubscriptions);
mButton4glte.setTitle(enhanced4glteModeTitleId);
- mButton4glte.setEnabled(hasActiveSubscriptions && canChange4glte);
mLteDataServicePref.setEnabled(hasActiveSubscriptions);
Preference ps;
ps = findPreference(BUTTON_CELL_BROADCAST_SETTINGS);
@@ -1268,7 +1283,8 @@
}
private boolean is4gLtePrefEnabled(PersistableBundle carrierConfig) {
- return (mTelephonyManager.getCallState() == TelephonyManager.CALL_STATE_IDLE)
+ return (mTelephonyManager.getCallState(mPhone.getSubId())
+ == TelephonyManager.CALL_STATE_IDLE)
&& mImsMgr != null
&& mImsMgr.isNonTtyOrTtyOnVolteEnabled()
&& carrierConfig.getBoolean(
@@ -1658,6 +1674,8 @@
mCallingCategory.removePreference(mWiFiCallingPref);
} else {
mCallingCategory.addPreference(mWiFiCallingPref);
+ mWiFiCallingPref.setEnabled(mTelephonyManager.getCallState(mPhone.getSubId())
+ == TelephonyManager.CALL_STATE_IDLE && hasActiveSubscriptions());
}
}
@@ -1678,7 +1696,8 @@
CarrierConfigManager.KEY_HIDE_ENHANCED_4G_LTE_BOOL))) {
getPreferenceScreen().removePreference(mButton4glte);
} else {
- // NOTE: Buttons will be enabled/disabled in mPhoneStateListener
+ mButton4glte.setEnabled(is4gLtePrefEnabled(carrierConfig)
+ && hasActiveSubscriptions());
boolean enh4glteMode = mImsMgr.isEnhanced4gLteModeSettingEnabledByUser()
&& mImsMgr.isNonTtyOrTtyOnVolteEnabled();
mButton4glte.setChecked(enh4glteMode);
@@ -1708,7 +1727,8 @@
mVideoCallingPref.setEnabled(false);
mVideoCallingPref.setChecked(false);
} else {
- mVideoCallingPref.setEnabled(true);
+ mVideoCallingPref.setEnabled(mTelephonyManager.getCallState(mPhone.getSubId())
+ == TelephonyManager.CALL_STATE_IDLE && hasActiveSubscriptions());
mVideoCallingPref.setChecked(mImsMgr.isVtEnabledByUser());
mVideoCallingPref.setOnPreferenceChangeListener(this);
}
diff --git a/src/com/android/phone/OutgoingCallBroadcaster.java b/src/com/android/phone/OutgoingCallBroadcaster.java
deleted file mode 100644
index 6bb1388..0000000
--- a/src/com/android/phone/OutgoingCallBroadcaster.java
+++ /dev/null
@@ -1,687 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.phone;
-
-import android.Manifest;
-import android.app.Activity;
-import android.app.ActivityManager;
-import android.app.AlertDialog;
-import android.app.AppOpsManager;
-import android.app.Dialog;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.os.RemoteException;
-import android.os.SystemProperties;
-import android.os.UserHandle;
-import android.telecom.PhoneAccount;
-import android.telephony.PhoneNumberUtils;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.View;
-import android.widget.ProgressBar;
-
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.PhoneConstants;
-import com.android.internal.telephony.TelephonyCapabilities;
-
-/**
- * OutgoingCallBroadcaster receives CALL and CALL_PRIVILEGED Intents, and broadcasts the
- * ACTION_NEW_OUTGOING_CALL intent. ACTION_NEW_OUTGOING_CALL is an ordered broadcast intent which
- * contains the phone number being dialed. Applications can use this intent to (1) see which numbers
- * are being dialed, (2) redirect a call (change the number being dialed), or (3) prevent a call
- * from being placed.
- *
- * After the other applications have had a chance to see the
- * ACTION_NEW_OUTGOING_CALL intent, it finally reaches the
- * {@link OutgoingCallReceiver}, which passes the (possibly modified)
- * intent on to the {@link SipCallOptionHandler}, which will
- * ultimately start the call using the CallController.placeCall() API.
- *
- * Calls where no number is present (like for a CDMA "empty flash" or a nonexistent voicemail
- * number) are exempt from being broadcast.
- * Calls to emergency numbers are still broadcast for informative purposes. The call is placed
- * prior to sending ACTION_NEW_OUTGOING_CALL and cannot be redirected nor prevented.
- */
-public class OutgoingCallBroadcaster extends Activity
- implements DialogInterface.OnClickListener, DialogInterface.OnCancelListener {
-
- private static final String TAG = "OutgoingCallBroadcaster";
- private static final boolean DBG =
- (PhoneGlobals.DBG_LEVEL >= 1) && (SystemProperties.getInt("ro.debuggable", 0) == 1);
- // Do not check in with VDBG = true, since that may write PII to the system log.
- private static final boolean VDBG = false;
-
- public static final String ACTION_SIP_SELECT_PHONE = "com.android.phone.SIP_SELECT_PHONE";
- public static final String EXTRA_ALREADY_CALLED = "android.phone.extra.ALREADY_CALLED";
- public static final String EXTRA_ORIGINAL_URI = "android.phone.extra.ORIGINAL_URI";
- public static final String EXTRA_NEW_CALL_INTENT = "android.phone.extra.NEW_CALL_INTENT";
- public static final String EXTRA_SIP_PHONE_URI = "android.phone.extra.SIP_PHONE_URI";
- public static final String EXTRA_ACTUAL_NUMBER_TO_DIAL =
- "android.phone.extra.ACTUAL_NUMBER_TO_DIAL";
- public static final String EXTRA_THIRD_PARTY_CALL_COMPONENT =
- "android.phone.extra.THIRD_PARTY_CALL_COMPONENT";
-
- /**
- * Identifier for intent extra for sending an empty Flash message for
- * CDMA networks. This message is used by the network to simulate a
- * press/depress of the "hookswitch" of a landline phone. Aka "empty flash".
- *
- * TODO: Receiving an intent extra to tell the phone to send this flash is a
- * temporary measure. To be replaced with an external ITelephony call in the future.
- * TODO: Keep in sync with the string defined in TwelveKeyDialer.java in Contacts app
- * until this is replaced with the ITelephony API.
- */
- public static final String EXTRA_SEND_EMPTY_FLASH =
- "com.android.phone.extra.SEND_EMPTY_FLASH";
-
- // Dialog IDs
- private static final int DIALOG_NOT_VOICE_CAPABLE = 1;
-
- /** Note message codes < 100 are reserved for the PhoneApp. */
- private static final int EVENT_OUTGOING_CALL_TIMEOUT = 101;
- private static final int EVENT_DELAYED_FINISH = 102;
-
- private static final int OUTGOING_CALL_TIMEOUT_THRESHOLD = 2000; // msec
- private static final int DELAYED_FINISH_TIME = 2000; // msec
-
- /**
- * ProgressBar object with "spinner" style, which will be shown if we take more than
- * {@link #EVENT_OUTGOING_CALL_TIMEOUT} msec to handle the incoming Intent.
- */
- private ProgressBar mWaitingSpinner;
- private final Handler mHandler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- if (msg.what == EVENT_OUTGOING_CALL_TIMEOUT) {
- Log.i(TAG, "Outgoing call takes too long. Showing the spinner.");
- mWaitingSpinner.setVisibility(View.VISIBLE);
- } else if (msg.what == EVENT_DELAYED_FINISH) {
- finish();
- } else {
- Log.wtf(TAG, "Unknown message id: " + msg.what);
- }
- }
- };
-
- /**
- * Starts the delayed finish() of OutgoingCallBroadcaster in order to give the UI
- * some time to start up.
- */
- private void startDelayedFinish() {
- mHandler.sendEmptyMessageDelayed(EVENT_DELAYED_FINISH, DELAYED_FINISH_TIME);
- }
-
- /**
- * OutgoingCallReceiver finishes NEW_OUTGOING_CALL broadcasts, starting
- * the InCallScreen if the broadcast has not been canceled, possibly with
- * a modified phone number and optional provider info (uri + package name + remote views.)
- */
- public class OutgoingCallReceiver extends BroadcastReceiver {
- private static final String TAG = "OutgoingCallReceiver";
-
- @Override
- public void onReceive(Context context, Intent intent) {
- mHandler.removeMessages(EVENT_OUTGOING_CALL_TIMEOUT);
- final boolean isAttemptingCall = doReceive(context, intent);
- if (DBG) Log.v(TAG, "OutgoingCallReceiver is going to finish the Activity itself.");
-
- // We cannot finish the activity immediately here because it would cause the temporary
- // black screen of OutgoingBroadcaster to go away and we need it to stay up until the
- // UI (in a different process) has time to come up.
- // However, if we know we are not attemping a call, we need to finish the activity
- // immediately so that subsequent CALL intents will retrigger a new
- // OutgoingCallReceiver. see b/10857203
- if (isAttemptingCall) {
- startDelayedFinish();
- } else {
- finish();
- }
- }
-
-
- /**
- * Handes receipt of ordered new_outgoing_call intent. Verifies that the return from the
- * ordered intent is valid.
- * @return true if the call is being attempted; false if we are canceling the call.
- */
- public boolean doReceive(Context context, Intent intent) {
- if (DBG) Log.v(TAG, "doReceive: " + intent);
-
- boolean alreadyCalled;
- String number;
- String originalUri;
-
- alreadyCalled = intent.getBooleanExtra(
- OutgoingCallBroadcaster.EXTRA_ALREADY_CALLED, false);
- if (alreadyCalled) {
- if (DBG) Log.v(TAG, "CALL already placed -- returning.");
- return false;
- }
-
- // Once the NEW_OUTGOING_CALL broadcast is finished, the resultData
- // is used as the actual number to call. (If null, no call will be
- // placed.)
-
- number = getResultData();
- if (VDBG) Log.v(TAG, "- got number from resultData: '" + number + "'");
-
- final PhoneGlobals app = PhoneGlobals.getInstance();
- final Phone phone = PhoneGlobals.getPhone();
-
- if (number == null) {
- if (DBG) Log.v(TAG, "CALL cancelled (null number), returning...");
- return false;
- } else if (TelephonyCapabilities.supportsOtasp(phone)
- && (phone.getState() != PhoneConstants.State.IDLE)
- && (phone.isOtaSpNumber(number))) {
- if (DBG) Log.v(TAG, "Call is active, a 2nd OTA call cancelled -- returning.");
- return false;
- } else if (PhoneNumberUtils.isPotentialLocalEmergencyNumber(context, number)) {
- // Just like 3rd-party apps aren't allowed to place emergency
- // calls via the ACTION_CALL intent, we also don't allow 3rd
- // party apps to use the NEW_OUTGOING_CALL broadcast to rewrite
- // an outgoing call into an emergency number.
- Log.w(TAG, "Cannot modify outgoing call to emergency number " + number + ".");
- return false;
- }
-
- originalUri = intent.getStringExtra(
- OutgoingCallBroadcaster.EXTRA_ORIGINAL_URI);
- if (originalUri == null) {
- Log.e(TAG, "Intent is missing EXTRA_ORIGINAL_URI -- returning.");
- return false;
- }
-
- Uri uri = Uri.parse(originalUri);
-
- // We already called convertKeypadLettersToDigits() and
- // stripSeparators() way back in onCreate(), before we sent out the
- // NEW_OUTGOING_CALL broadcast. But we need to do it again here
- // too, since the number might have been modified/rewritten during
- // the broadcast (and may now contain letters or separators again.)
- number = PhoneNumberUtils.convertKeypadLettersToDigits(number);
- number = PhoneNumberUtils.stripSeparators(number);
-
- if (DBG) Log.v(TAG, "doReceive: proceeding with call...");
- if (VDBG) Log.v(TAG, "- uri: " + uri);
- if (VDBG) Log.v(TAG, "- actual number to dial: '" + number + "'");
-
- startSipCallOptionHandler(context, intent, uri, number);
-
- return true;
- }
- }
-
- /**
- * Launch the SipCallOptionHandler, which is the next step(*) in the
- * outgoing-call sequence after the outgoing call broadcast is
- * complete.
- *
- * (*) We now know exactly what phone number we need to dial, so the next
- * step is for the SipCallOptionHandler to decide which Phone type (SIP
- * or PSTN) should be used. (Depending on the user's preferences, this
- * decision may also involve popping up a dialog to ask the user to
- * choose what type of call this should be.)
- *
- * @param context used for the startActivity() call
- *
- * @param intent the intent from the previous step of the outgoing-call
- * sequence. Normally this will be the NEW_OUTGOING_CALL broadcast intent
- * that came in to the OutgoingCallReceiver, although it can also be the
- * original ACTION_CALL intent that started the whole sequence (in cases
- * where we don't do the NEW_OUTGOING_CALL broadcast at all, like for
- * emergency numbers or SIP addresses).
- *
- * @param uri the data URI from the original CALL intent, presumably either
- * a tel: or sip: URI. For tel: URIs, note that the scheme-specific part
- * does *not* necessarily have separators and keypad letters stripped (so
- * we might see URIs like "tel:(650)%20555-1234" or "tel:1-800-GOOG-411"
- * here.)
- *
- * @param number the actual number (or SIP address) to dial. This is
- * guaranteed to be either a PSTN phone number with separators stripped
- * out and keypad letters converted to digits (like "16505551234"), or a
- * raw SIP address (like "user@example.com").
- */
- private void startSipCallOptionHandler(Context context, Intent intent,
- Uri uri, String number) {
- // TODO: Remove this code.
- }
-
- /**
- * This method is the single point of entry for the CALL intent, which is used (by built-in
- * apps like Contacts / Dialer, as well as 3rd-party apps) to initiate an outgoing voice call.
- *
- *
- */
- @Override
- protected void onCreate(Bundle icicle) {
- super.onCreate(icicle);
- setContentView(R.layout.outgoing_call_broadcaster);
- mWaitingSpinner = (ProgressBar) findViewById(R.id.spinner);
-
- Intent intent = getIntent();
- if (DBG) {
- final Configuration configuration = getResources().getConfiguration();
- Log.v(TAG, "onCreate: this = " + this + ", icicle = " + icicle);
- Log.v(TAG, " - getIntent() = " + intent);
- Log.v(TAG, " - configuration = " + configuration);
- }
-
- if (icicle != null) {
- // A non-null icicle means that this activity is being
- // re-initialized after previously being shut down.
- //
- // In practice this happens very rarely (because the lifetime
- // of this activity is so short!), but it *can* happen if the
- // framework detects a configuration change at exactly the
- // right moment; see bug 2202413.
- //
- // In this case, do nothing. Our onCreate() method has already
- // run once (with icicle==null the first time), which means
- // that the NEW_OUTGOING_CALL broadcast for this new call has
- // already been sent.
- Log.i(TAG, "onCreate: non-null icicle! "
- + "Bailing out, not sending NEW_OUTGOING_CALL broadcast...");
-
- // No need to finish() here, since the OutgoingCallReceiver from
- // our original instance will do that. (It'll actually call
- // finish() on our original instance, which apparently works fine
- // even though the ActivityManager has already shut that instance
- // down. And note that if we *do* call finish() here, that just
- // results in an "ActivityManager: Duplicate finish request"
- // warning when the OutgoingCallReceiver runs.)
-
- return;
- }
-
- processIntent(intent);
-
- // isFinishing() return false when 1. broadcast is still ongoing, or 2. dialog is being
- // shown. Otherwise finish() is called inside processIntent(), is isFinishing() here will
- // return true.
- if (DBG) Log.v(TAG, "At the end of onCreate(). isFinishing(): " + isFinishing());
- }
-
- /**
- * Interprets a given Intent and starts something relevant to the Intent.
- *
- * This method will handle three kinds of actions:
- *
- * - CALL (action for usual outgoing voice calls)
- * - CALL_PRIVILEGED (can come from built-in apps like contacts / voice dialer / bluetooth)
- * - CALL_EMERGENCY (from the EmergencyDialer that's reachable from the lockscreen.)
- *
- * The exact behavior depends on the intent's data:
- *
- * - The most typical is a tel: URI, which we handle by starting the
- * NEW_OUTGOING_CALL broadcast. That broadcast eventually triggers
- * the sequence OutgoingCallReceiver -> SipCallOptionHandler ->
- * InCallScreen.
- *
- * - Or, with a sip: URI we skip the NEW_OUTGOING_CALL broadcast and
- * go directly to SipCallOptionHandler, which then leads to the
- * InCallScreen.
- *
- * - voicemail: URIs take the same path as regular tel: URIs.
- *
- * Other special cases:
- *
- * - Outgoing calls are totally disallowed on non-voice-capable
- * devices (see handleNonVoiceCapable()).
- *
- * - A CALL intent with the EXTRA_SEND_EMPTY_FLASH extra (and
- * presumably no data at all) means "send an empty flash" (which
- * is only meaningful on CDMA devices while a call is already
- * active.)
- *
- */
- private void processIntent(Intent intent) {
- if (DBG) {
- Log.v(TAG, "processIntent() = " + intent + ", thread: " + Thread.currentThread());
- }
- final Configuration configuration = getResources().getConfiguration();
-
- // Outgoing phone calls are only allowed on "voice-capable" devices.
- if (!PhoneGlobals.sVoiceCapable) {
- Log.i(TAG, "This device is detected as non-voice-capable device.");
- handleNonVoiceCapable(intent);
- return;
- }
-
- String action = intent.getAction();
- String number = PhoneNumberUtils.getNumberFromIntent(intent, this);
- // Check the number, don't convert for sip uri
- // TODO put uriNumber under PhoneNumberUtils
- if (number != null) {
- if (!PhoneNumberUtils.isUriNumber(number)) {
- number = PhoneNumberUtils.convertKeypadLettersToDigits(number);
- number = PhoneNumberUtils.stripSeparators(number);
- }
- } else {
- Log.w(TAG, "The number obtained from Intent is null.");
- }
-
- AppOpsManager appOps = (AppOpsManager)getSystemService(Context.APP_OPS_SERVICE);
- int launchedFromUid;
- String launchedFromPackage;
- try {
- launchedFromUid = ActivityManager.getService().getLaunchedFromUid(
- getActivityToken());
- launchedFromPackage = ActivityManager.getService().getLaunchedFromPackage(
- getActivityToken());
- } catch (RemoteException e) {
- launchedFromUid = -1;
- launchedFromPackage = null;
- }
- if (appOps.noteOpNoThrow(AppOpsManager.OP_CALL_PHONE, launchedFromUid, launchedFromPackage)
- != AppOpsManager.MODE_ALLOWED) {
- Log.w(TAG, "Rejecting call from uid " + launchedFromUid + " package "
- + launchedFromPackage);
- finish();
- return;
- }
-
- // If true, this flag will indicate that the current call is a special kind
- // of call (most likely an emergency number) that 3rd parties aren't allowed
- // to intercept or affect in any way. (In that case, we start the call
- // immediately rather than going through the NEW_OUTGOING_CALL sequence.)
- boolean callNow;
-
- if (getClass().getName().equals(intent.getComponent().getClassName())) {
- // If we were launched directly from the OutgoingCallBroadcaster,
- // not one of its more privileged aliases, then make sure that
- // only the non-privileged actions are allowed.
- if (!Intent.ACTION_CALL.equals(intent.getAction())) {
- Log.w(TAG, "Attempt to deliver non-CALL action; forcing to CALL");
- intent.setAction(Intent.ACTION_CALL);
- }
- }
-
- // Check whether or not this is an emergency number, in order to
- // enforce the restriction that only the CALL_PRIVILEGED and
- // CALL_EMERGENCY intents are allowed to make emergency calls.
- //
- // (Note that the ACTION_CALL check below depends on the result of
- // isPotentialLocalEmergencyNumber() rather than just plain
- // isLocalEmergencyNumber(), to be 100% certain that we *don't*
- // allow 3rd party apps to make emergency calls by passing in an
- // "invalid" number like "9111234" that isn't technically an
- // emergency number but might still result in an emergency call
- // with some networks.)
- final boolean isExactEmergencyNumber =
- (number != null) && PhoneNumberUtils.isLocalEmergencyNumber(this, number);
- final boolean isPotentialEmergencyNumber =
- (number != null) && PhoneNumberUtils.isPotentialLocalEmergencyNumber(this, number);
- if (VDBG) {
- Log.v(TAG, " - Checking restrictions for number '" + number + "':");
- Log.v(TAG, " isExactEmergencyNumber = " + isExactEmergencyNumber);
- Log.v(TAG, " isPotentialEmergencyNumber = " + isPotentialEmergencyNumber);
- }
-
- /* Change CALL_PRIVILEGED into CALL or CALL_EMERGENCY as needed. */
- // TODO: This code is redundant with some code in InCallScreen: refactor.
- if (Intent.ACTION_CALL_PRIVILEGED.equals(action)) {
- // We're handling a CALL_PRIVILEGED intent, so we know this request came
- // from a trusted source (like the built-in dialer.) So even a number
- // that's *potentially* an emergency number can safely be promoted to
- // CALL_EMERGENCY (since we *should* allow you to dial "91112345" from
- // the dialer if you really want to.)
- if (isPotentialEmergencyNumber) {
- Log.i(TAG, "ACTION_CALL_PRIVILEGED is used while the number is a potential"
- + " emergency number. Use ACTION_CALL_EMERGENCY as an action instead.");
- action = Intent.ACTION_CALL_EMERGENCY;
- } else {
- action = Intent.ACTION_CALL;
- }
- if (DBG) Log.v(TAG, " - updating action from CALL_PRIVILEGED to " + action);
- intent.setAction(action);
- }
-
- if (Intent.ACTION_CALL.equals(action)) {
- if (isPotentialEmergencyNumber) {
- Log.w(TAG, "Cannot call potential emergency number '" + number
- + "' with CALL Intent " + intent + ".");
- Log.i(TAG, "Launching default dialer instead...");
-
- Intent invokeFrameworkDialer = new Intent();
-
- // TwelveKeyDialer is in a tab so we really want
- // DialtactsActivity. Build the intent 'manually' to
- // use the java resolver to find the dialer class (as
- // opposed to a Context which look up known android
- // packages only)
- final Resources resources = getResources();
- invokeFrameworkDialer.setClassName(
- resources.getString(R.string.ui_default_package),
- resources.getString(R.string.dialer_default_class));
- invokeFrameworkDialer.setAction(Intent.ACTION_DIAL);
- invokeFrameworkDialer.setData(intent.getData());
- if (DBG) Log.v(TAG, "onCreate(): calling startActivity for Dialer: "
- + invokeFrameworkDialer);
- startActivity(invokeFrameworkDialer);
- finish();
- return;
- }
- callNow = false;
- } else if (Intent.ACTION_CALL_EMERGENCY.equals(action)) {
- // ACTION_CALL_EMERGENCY case: this is either a CALL_PRIVILEGED
- // intent that we just turned into a CALL_EMERGENCY intent (see
- // above), or else it really is an CALL_EMERGENCY intent that
- // came directly from some other app (e.g. the EmergencyDialer
- // activity built in to the Phone app.)
- // Make sure it's at least *possible* that this is really an
- // emergency number.
- if (!isPotentialEmergencyNumber) {
- Log.w(TAG, "Cannot call non-potential-emergency number " + number
- + " with EMERGENCY_CALL Intent " + intent + "."
- + " Finish the Activity immediately.");
- finish();
- return;
- }
- callNow = true;
- } else {
- Log.e(TAG, "Unhandled Intent " + intent + ". Finish the Activity immediately.");
- finish();
- return;
- }
-
- // Make sure the screen is turned on. This is probably the right
- // thing to do, and more importantly it works around an issue in the
- // activity manager where we will not launch activities consistently
- // when the screen is off (since it is trying to keep them paused
- // and has... issues).
- //
- // Also, this ensures the device stays awake while doing the following
- // broadcast; technically we should be holding a wake lock here
- // as well.
- PhoneGlobals.getInstance().wakeUpScreen();
-
- // If number is null, we're probably trying to call a non-existent voicemail number,
- // send an empty flash or something else is fishy. Whatever the problem, there's no
- // number, so there's no point in allowing apps to modify the number.
- if (TextUtils.isEmpty(number)) {
- if (intent.getBooleanExtra(EXTRA_SEND_EMPTY_FLASH, false)) {
- Log.i(TAG, "onCreate: SEND_EMPTY_FLASH...");
- PhoneUtils.sendEmptyFlash(PhoneGlobals.getPhone());
- finish();
- return;
- } else {
- Log.i(TAG, "onCreate: null or empty number, setting callNow=true...");
- callNow = true;
- }
- }
-
- if (callNow) {
- // This is a special kind of call (most likely an emergency number)
- // that 3rd parties aren't allowed to intercept or affect in any way.
- // So initiate the outgoing call immediately.
-
- Log.i(TAG, "onCreate(): callNow case! Calling placeCall(): " + intent);
-
- // Initiate the outgoing call, and simultaneously launch the
- // InCallScreen to display the in-call UI:
- PhoneGlobals.getInstance().callController.placeCall(intent);
-
- // Note we do *not* "return" here, but instead continue and
- // send the ACTION_NEW_OUTGOING_CALL broadcast like for any
- // other outgoing call. (But when the broadcast finally
- // reaches the OutgoingCallReceiver, we'll know not to
- // initiate the call again because of the presence of the
- // EXTRA_ALREADY_CALLED extra.)
- }
-
- // For now, SIP calls will be processed directly without a
- // NEW_OUTGOING_CALL broadcast.
- //
- // TODO: In the future, though, 3rd party apps *should* be allowed to
- // intercept outgoing calls to SIP addresses as well. To do this, we should
- // (1) update the NEW_OUTGOING_CALL intent documentation to explain this
- // case, and (2) pass the outgoing SIP address by *not* overloading the
- // EXTRA_PHONE_NUMBER extra, but instead using a new separate extra to hold
- // the outgoing SIP address. (Be sure to document whether it's a URI or just
- // a plain address, whether it could be a tel: URI, etc.)
- Uri uri = intent.getData();
- String scheme = uri.getScheme();
- if (PhoneAccount.SCHEME_SIP.equals(scheme) || PhoneNumberUtils.isUriNumber(number)) {
- Log.i(TAG, "The requested number was detected as SIP call.");
- startSipCallOptionHandler(this, intent, uri, number);
- finish();
- return;
-
- // TODO: if there's ever a way for SIP calls to trigger a
- // "callNow=true" case (see above), we'll need to handle that
- // case here too (most likely by just doing nothing at all.)
- }
-
- Intent broadcastIntent = new Intent(Intent.ACTION_NEW_OUTGOING_CALL);
- if (number != null) {
- broadcastIntent.putExtra(Intent.EXTRA_PHONE_NUMBER, number);
- }
- CallGatewayManager.checkAndCopyPhoneProviderExtras(intent, broadcastIntent);
- broadcastIntent.putExtra(EXTRA_ALREADY_CALLED, callNow);
- broadcastIntent.putExtra(EXTRA_ORIGINAL_URI, uri.toString());
- // Need to raise foreground in-call UI as soon as possible while allowing 3rd party app
- // to intercept the outgoing call.
- broadcastIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
- if (DBG) Log.v(TAG, " - Broadcasting intent: " + broadcastIntent + ".");
-
- // Set a timer so that we can prepare for unexpected delay introduced by the broadcast.
- // If it takes too much time, the timer will show "waiting" spinner.
- // This message will be removed when OutgoingCallReceiver#onReceive() is called before the
- // timeout.
- mHandler.sendEmptyMessageDelayed(EVENT_OUTGOING_CALL_TIMEOUT,
- OUTGOING_CALL_TIMEOUT_THRESHOLD);
- sendOrderedBroadcastAsUser(broadcastIntent, UserHandle.SYSTEM,
- android.Manifest.permission.PROCESS_OUTGOING_CALLS,
- AppOpsManager.OP_PROCESS_OUTGOING_CALLS,
- new OutgoingCallReceiver(),
- null, // scheduler
- Activity.RESULT_OK, // initialCode
- number, // initialData: initial value for the result data
- null); // initialExtras
- }
-
- @Override
- protected void onStop() {
- // Clean up (and dismiss if necessary) any managed dialogs.
- //
- // We don't do this in onPause() since we can be paused/resumed
- // due to orientation changes (in which case we don't want to
- // disturb the dialog), but we *do* need it here in onStop() to be
- // sure we clean up if the user hits HOME while the dialog is up.
- //
- // Note it's safe to call removeDialog() even if there's no dialog
- // associated with that ID.
- removeDialog(DIALOG_NOT_VOICE_CAPABLE);
-
- super.onStop();
- }
-
- /**
- * Handle the specified CALL or CALL_* intent on a non-voice-capable
- * device.
- *
- * This method may launch a different intent (if there's some useful
- * alternative action to take), or otherwise display an error dialog,
- * and in either case will finish() the current activity when done.
- */
- private void handleNonVoiceCapable(Intent intent) {
- if (DBG) Log.v(TAG, "handleNonVoiceCapable: handling " + intent
- + " on non-voice-capable device...");
-
- // Just show a generic "voice calling not supported" dialog.
- showDialog(DIALOG_NOT_VOICE_CAPABLE);
- // ...and we'll eventually finish() when the user dismisses
- // or cancels the dialog.
- }
-
- @Override
- protected Dialog onCreateDialog(int id) {
- Dialog dialog;
- switch(id) {
- case DIALOG_NOT_VOICE_CAPABLE:
- dialog = new AlertDialog.Builder(this)
- .setTitle(R.string.not_voice_capable)
- .setIconAttribute(android.R.attr.alertDialogIcon)
- .setPositiveButton(android.R.string.ok, this)
- .setOnCancelListener(this)
- .create();
- break;
- default:
- Log.w(TAG, "onCreateDialog: unexpected ID " + id);
- dialog = null;
- break;
- }
- return dialog;
- }
-
- /** DialogInterface.OnClickListener implementation */
- @Override
- public void onClick(DialogInterface dialog, int id) {
- // DIALOG_NOT_VOICE_CAPABLE is the only dialog we ever use (so far
- // at least), and its only button is "OK".
- finish();
- }
-
- /** DialogInterface.OnCancelListener implementation */
- @Override
- public void onCancel(DialogInterface dialog) {
- // DIALOG_NOT_VOICE_CAPABLE is the only dialog we ever use (so far
- // at least), and canceling it is just like hitting "OK".
- finish();
- }
-
- /**
- * Implement onConfigurationChanged() purely for debugging purposes,
- * to make sure that the android:configChanges element in our manifest
- * is working properly.
- */
- @Override
- public void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- if (DBG) Log.v(TAG, "onConfigurationChanged: newConfig = " + newConfig);
- }
-}
diff --git a/src/com/android/phone/PhoneGlobals.java b/src/com/android/phone/PhoneGlobals.java
index 98b94cc..2d0b99d 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -134,9 +134,6 @@
private static PhoneGlobals sMe;
- // A few important fields we expose to the rest of the package
- // directly (rather than thru set/get methods) for efficiency.
- CallController callController;
CallManager mCM;
CallNotifier notifier;
CallerInfoCache callerInfoCache;
@@ -326,11 +323,6 @@
callGatewayManager = CallGatewayManager.getInstance();
- // Create the CallController singleton, which is the interface
- // to the telephony layer for user-initiated telephony functionality
- // (like making outgoing calls.)
- callController = CallController.init(this, callLogger, callGatewayManager);
-
// Create the CallerInfoCache singleton, which remembers custom ring tone and
// send-to-voicemail settings.
//
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
old mode 100644
new mode 100755
index 253c5f9..b70f9eb
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -55,6 +55,7 @@
import android.telephony.CellInfo;
import android.telephony.ClientRequestStats;
import android.telephony.IccOpenLogicalChannelResponse;
+import android.telephony.LocationAccessPolicy;
import android.telephony.ModemActivityInfo;
import android.telephony.NeighboringCellInfo;
import android.telephony.NetworkScanRequest;
@@ -74,6 +75,7 @@
import android.telephony.ims.aidl.IImsMmTelFeature;
import android.telephony.ims.aidl.IImsRcsFeature;
import android.telephony.ims.aidl.IImsRegistration;
+import android.telephony.ims.stub.ImsRegistrationImplBase;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
@@ -100,6 +102,7 @@
import com.android.internal.telephony.RIL;
import com.android.internal.telephony.RILConstants;
import com.android.internal.telephony.SubscriptionController;
+import com.android.internal.telephony.TelephonyPermissions;
import com.android.internal.telephony.TelephonyProperties;
import com.android.internal.telephony.euicc.EuiccConnector;
import com.android.internal.telephony.uicc.IccIoResult;
@@ -108,6 +111,7 @@
import com.android.internal.telephony.uicc.UiccCard;
import com.android.internal.telephony.uicc.UiccCardApplication;
import com.android.internal.telephony.uicc.UiccController;
+import com.android.internal.telephony.uicc.UiccProfile;
import com.android.internal.telephony.uicc.UiccSlot;
import com.android.internal.telephony.util.VoicemailNotificationSettingsUtil;
import com.android.internal.util.HexDump;
@@ -623,7 +627,7 @@
uiccCard = getUiccCardFromRequest(request);
if (uiccCard == null) {
loge("iccCloseLogicalChannel: No UICC");
- request.result = new IccIoResult(0x6F, 0, (byte[])null);
+ request.result = false;
synchronized (request) {
request.notifyAll();
}
@@ -1252,7 +1256,8 @@
@Override
public boolean isOffhookForSubscriber(int subId, String callingPackage) {
- if (!canReadPhoneState(callingPackage, "isOffhookForSubscriber")) {
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+ mApp, callingPackage, "isOffhookForSubscriber")) {
return false;
}
@@ -1271,7 +1276,8 @@
@Override
public boolean isRingingForSubscriber(int subId, String callingPackage) {
- if (!canReadPhoneState(callingPackage, "isRingingForSubscriber")) {
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+ mApp, callingPackage, "isRingingForSubscriber")) {
return false;
}
@@ -1290,7 +1296,8 @@
@Override
public boolean isIdleForSubscriber(int subId, String callingPackage) {
- if (!canReadPhoneState(callingPackage, "isIdleForSubscriber")) {
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+ mApp, callingPackage, "isIdleForSubscriber")) {
return false;
}
@@ -1465,7 +1472,8 @@
@Override
public boolean isRadioOnForSubscriber(int subId, String callingPackage) {
- if (!canReadPhoneState(callingPackage, "isRadioOnForSubscriber")) {
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+ mApp, callingPackage, "isRadioOnForSubscriber")) {
return false;
}
return isRadioOnForSubscriber(subId);
@@ -1652,8 +1660,10 @@
@Override
public Bundle getCellLocation(String callingPackage) {
+ mPhone.getContext().getSystemService(AppOpsManager.class)
+ .checkPackage(Binder.getCallingUid(), callingPackage);
if (!LocationAccessPolicy.canAccessCellLocation(mPhone.getContext(),
- callingPackage, Binder.getCallingUid(), "getCellLocation")) {
+ callingPackage, Binder.getCallingUid(),Binder.getCallingPid())) {
return null;
}
@@ -1720,8 +1730,10 @@
@Override
@SuppressWarnings("unchecked")
public List<NeighboringCellInfo> getNeighboringCellInfo(String callingPackage) {
+ mPhone.getContext().getSystemService(AppOpsManager.class)
+ .checkPackage(Binder.getCallingUid(), callingPackage);
if (!LocationAccessPolicy.canAccessCellLocation(mPhone.getContext(),
- callingPackage, Binder.getCallingUid(), "getNeighboringCellInfo")) {
+ callingPackage, Binder.getCallingUid(), Binder.getCallingPid())) {
return null;
}
@@ -1748,8 +1760,10 @@
@Override
public List<CellInfo> getAllCellInfo(String callingPackage) {
+ mPhone.getContext().getSystemService(AppOpsManager.class)
+ .checkPackage(Binder.getCallingUid(), callingPackage);
if (!LocationAccessPolicy.canAccessCellLocation(mPhone.getContext(),
- callingPackage, Binder.getCallingUid(), "getAllCellInfo")) {
+ callingPackage, Binder.getCallingUid(), Binder.getCallingPid())) {
return null;
}
@@ -1772,16 +1786,18 @@
@Override
public String getImeiForSlot(int slotIndex, String callingPackage) {
- if (!canReadPhoneState(callingPackage, "getImeiForSlot")) {
- return null;
- }
- Phone phone = PhoneFactory.getPhone(slotIndex);
- return phone == null ? null : phone.getImei();
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+ mApp, callingPackage, "getImeiForSlot")) {
+ return null;
+ }
+ Phone phone = PhoneFactory.getPhone(slotIndex);
+ return phone == null ? null : phone.getImei();
}
@Override
public String getMeidForSlot(int slotIndex, String callingPackage) {
- if (!canReadPhoneState(callingPackage, "getMeidForSlot")) {
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+ mApp, callingPackage, "getMeidForSlot")) {
return null;
}
Phone phone = PhoneFactory.getPhone(slotIndex);
@@ -1790,11 +1806,12 @@
@Override
public String getDeviceSoftwareVersionForSlot(int slotIndex, String callingPackage) {
- if (!canReadPhoneState(callingPackage, "getDeviceSoftwareVersionForSlot")) {
- return null;
- }
- Phone phone = PhoneFactory.getPhone(slotIndex);
- return phone == null ? null : phone.getDeviceSvn();
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+ mApp, callingPackage, "getDeviceSoftwareVersionForSlot")) {
+ return null;
+ }
+ Phone phone = PhoneFactory.getPhone(slotIndex);
+ return phone == null ? null : phone.getDeviceSvn();
}
@Override
@@ -1823,35 +1840,6 @@
}
/**
- * Make sure either system app or the caller has carrier privilege.
- *
- * @throws SecurityException if the caller does not have the required permission/privilege
- */
- private void enforceModifyPermissionOrCarrierPrivilege(int subId) {
- int permission = mApp.checkCallingOrSelfPermission(
- android.Manifest.permission.MODIFY_PHONE_STATE);
- if (permission == PackageManager.PERMISSION_GRANTED) {
- return;
- }
-
- log("No modify permission, check carrier privilege next.");
- enforceCarrierPrivilege(subId);
- }
-
- /**
- * Make sure the caller has carrier privilege.
- *
- * @throws SecurityException if the caller does not have the required permission
- */
- private void enforceCarrierPrivilege(int subId) {
- if (getCarrierPrivilegeStatus(subId) !=
- TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
- loge("No Carrier Privilege.");
- throw new SecurityException("No Carrier Privilege.");
- }
- }
-
- /**
* Make sure the caller has the CALL_PHONE permission.
*
* @throws SecurityException if the caller does not have the required permission
@@ -1911,7 +1899,8 @@
@Override
public int getCdmaEriIconIndexForSubscriber(int subId, String callingPackage) {
- if (!canReadPhoneState(callingPackage, "getCdmaEriIconIndexForSubscriber")) {
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+ mApp, callingPackage, "getCdmaEriIconIndexForSubscriber")) {
return -1;
}
final Phone phone = getPhone(subId);
@@ -1934,7 +1923,8 @@
@Override
public int getCdmaEriIconModeForSubscriber(int subId, String callingPackage) {
- if (!canReadPhoneState(callingPackage, "getCdmaEriIconModeForSubscriber")) {
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+ mApp, callingPackage, "getCdmaEriIconModeForSubscriber")) {
return -1;
}
final Phone phone = getPhone(subId);
@@ -1955,7 +1945,8 @@
@Override
public String getCdmaEriTextForSubscriber(int subId, String callingPackage) {
- if (!canReadPhoneState(callingPackage, "getCdmaEriIconTextForSubscriber")) {
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+ mApp, callingPackage, "getCdmaEriIconTextForSubscriber")) {
return null;
}
final Phone phone = getPhone(subId);
@@ -1971,7 +1962,8 @@
*/
@Override
public String getCdmaMdn(int subId) {
- enforceModifyPermissionOrCarrierPrivilege(subId);
+ TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
+ mApp, subId, "getCdmaMdn");
final Phone phone = getPhone(subId);
if (mPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA && phone != null) {
return phone.getLine1Number();
@@ -1985,7 +1977,8 @@
*/
@Override
public String getCdmaMin(int subId) {
- enforceModifyPermissionOrCarrierPrivilege(subId);
+ TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
+ mApp, subId, "getCdmaMin");
final Phone phone = getPhone(subId);
if (phone != null && phone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) {
return phone.getCdmaMin();
@@ -2006,7 +1999,7 @@
*/
@Override
public boolean setVoiceMailNumber(int subId, String alphaTag, String number) {
- enforceCarrierPrivilege(subId);
+ TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(subId, "setVoiceMailNumber");
Boolean success = (Boolean) sendRequest(CMD_SET_VOICEMAIL_NUMBER,
new Pair<String, String>(alphaTag, number), new Integer(subId));
return success;
@@ -2029,7 +2022,8 @@
@Override
public String getVisualVoicemailPackageName(String callingPackage, int subId) {
mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
- if (!canReadPhoneState(callingPackage, "getVisualVoicemailPackageName")) {
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+ mApp, callingPackage, "getVisualVoicemailPackageName")) {
return null;
}
return RemoteVvmTaskManager.getRemotePackage(mPhone.getContext(), subId).getPackageName();
@@ -2093,7 +2087,8 @@
*/
@Override
public void setVoiceActivationState(int subId, int activationState) {
- enforceModifyPermissionOrCarrierPrivilege(subId);
+ TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
+ mApp, subId, "setVoiceActivationState");
final Phone phone = getPhone(subId);
if (phone != null) {
phone.setVoiceActivationState(activationState);
@@ -2107,7 +2102,8 @@
*/
@Override
public void setDataActivationState(int subId, int activationState) {
- enforceModifyPermissionOrCarrierPrivilege(subId);
+ TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
+ mApp, subId, "setDataActivationState");
final Phone phone = getPhone(subId);
if (phone != null) {
phone.setDataActivationState(activationState);
@@ -2121,7 +2117,8 @@
*/
@Override
public int getVoiceActivationState(int subId, String callingPackage) {
- if (!canReadPhoneState(callingPackage, "getVoiceActivationStateForSubscriber")) {
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+ mApp, callingPackage, "getVoiceActivationStateForSubscriber")) {
return TelephonyManager.SIM_ACTIVATION_STATE_UNKNOWN;
}
final Phone phone = getPhone(subId);
@@ -2137,7 +2134,8 @@
*/
@Override
public int getDataActivationState(int subId, String callingPackage) {
- if (!canReadPhoneState(callingPackage, "getDataActivationStateForSubscriber")) {
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+ mApp, callingPackage, "getDataActivationStateForSubscriber")) {
return TelephonyManager.SIM_ACTIVATION_STATE_UNKNOWN;
}
final Phone phone = getPhone(subId);
@@ -2188,7 +2186,8 @@
mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
String defaultDialer = TelecomManager.from(mPhone.getContext()).getDefaultDialerPackage();
if (!TextUtils.equals(callingPackage, defaultDialer)) {
- enforceCarrierPrivilege(getDefaultSubscription());
+ TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(
+ getDefaultSubscription(), "sendDialerSpecialCode");
}
mPhone.sendDialerSpecialCode(inputCode);
}
@@ -2214,7 +2213,8 @@
*/
@Override
public int getNetworkTypeForSubscriber(int subId, String callingPackage) {
- if (!canReadPhoneState(callingPackage, "getNetworkTypeForSubscriber")) {
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+ mApp, callingPackage, "getNetworkTypeForSubscriber")) {
return TelephonyManager.NETWORK_TYPE_UNKNOWN;
}
@@ -2239,7 +2239,8 @@
*/
@Override
public int getDataNetworkTypeForSubscriber(int subId, String callingPackage) {
- if (!canReadPhoneState(callingPackage, "getDataNetworkTypeForSubscriber")) {
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+ mApp, callingPackage, "getDataNetworkTypeForSubscriber")) {
return TelephonyManager.NETWORK_TYPE_UNKNOWN;
}
@@ -2256,7 +2257,8 @@
*/
@Override
public int getVoiceNetworkTypeForSubscriber(int subId, String callingPackage) {
- if (!canReadPhoneState(callingPackage, "getDataNetworkTypeForSubscriber")) {
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+ mApp, callingPackage, "getDataNetworkTypeForSubscriber")) {
return TelephonyManager.NETWORK_TYPE_UNKNOWN;
}
@@ -2306,7 +2308,8 @@
@Override
public int getLteOnCdmaModeForSubscriber(int subId, String callingPackage) {
- if (!canReadPhoneState(callingPackage, "getLteOnCdmaModeForSubscriber")) {
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+ mApp, callingPackage, "getLteOnCdmaModeForSubscriber")) {
return PhoneConstants.LTE_ON_CDMA_UNKNOWN;
}
@@ -2363,7 +2366,8 @@
@Override
public IccOpenLogicalChannelResponse iccOpenLogicalChannel(
int subId, String callingPackage, String aid, int p2) {
- enforceModifyPermissionOrCarrierPrivilege(subId);
+ TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
+ mApp, subId, "iccOpenLogicalChannel");
if (TextUtils.equals(ISDR_AID, aid)) {
// Only allows LPA to open logical channel to ISD-R.
@@ -2386,7 +2390,8 @@
@Override
public boolean iccCloseLogicalChannel(int subId, int channel) {
- enforceModifyPermissionOrCarrierPrivilege(subId);
+ TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
+ mApp, subId, "iccCloseLogicalChannel");
if (DBG) log("iccCloseLogicalChannel: subId=" + subId + " chnl=" + channel);
if (channel < 0) {
@@ -2400,7 +2405,8 @@
@Override
public String iccTransmitApduLogicalChannel(int subId, int channel, int cla,
int command, int p1, int p2, int p3, String data) {
- enforceModifyPermissionOrCarrierPrivilege(subId);
+ TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
+ mApp, subId, "iccTransmitApduLogicalChannel");
if (DBG) {
log("iccTransmitApduLogicalChannel: subId=" + subId + " chnl=" + channel +
@@ -2428,7 +2434,8 @@
@Override
public String iccTransmitApduBasicChannel(int subId, String callingPackage, int cla,
int command, int p1, int p2, int p3, String data) {
- enforceModifyPermissionOrCarrierPrivilege(subId);
+ TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
+ mApp, subId, "iccTransmitApduBasicChannel");
if (command == SELECT_COMMAND && p1 == SELECT_P1 && p2 == SELECT_P2 && p3 == SELECT_P3
&& TextUtils.equals(ISDR_AID, data)) {
@@ -2464,7 +2471,8 @@
@Override
public byte[] iccExchangeSimIO(int subId, int fileID, int command, int p1, int p2, int p3,
String filePath) {
- enforceModifyPermissionOrCarrierPrivilege(subId);
+ TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
+ mApp, subId, "iccExchangeSimIO");
if (DBG) {
log("Exchange SIM_IO " + subId + ":" + fileID + ":" + command + " " +
@@ -2517,7 +2525,8 @@
@Override
public String sendEnvelopeWithStatus(int subId, String content) {
- enforceModifyPermissionOrCarrierPrivilege(subId);
+ TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
+ mApp, subId, "sendEnvelopeWithStatus");
IccIoResult response = (IccIoResult)sendRequest(CMD_SEND_ENVELOPE, content, subId);
if (response.payload == null) {
@@ -2540,7 +2549,8 @@
*/
@Override
public String nvReadItem(int itemID) {
- enforceModifyPermissionOrCarrierPrivilege(getDefaultSubscription());
+ TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
+ mApp, getDefaultSubscription(), "nvReadItem");
if (DBG) log("nvReadItem: item " + itemID);
String value = (String) sendRequest(CMD_NV_READ_ITEM, itemID);
if (DBG) log("nvReadItem: item " + itemID + " is \"" + value + '"');
@@ -2557,7 +2567,8 @@
*/
@Override
public boolean nvWriteItem(int itemID, String itemValue) {
- enforceModifyPermissionOrCarrierPrivilege(getDefaultSubscription());
+ TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
+ mApp, getDefaultSubscription(), "nvWriteItem");
if (DBG) log("nvWriteItem: item " + itemID + " value \"" + itemValue + '"');
Boolean success = (Boolean) sendRequest(CMD_NV_WRITE_ITEM,
new Pair<Integer, String>(itemID, itemValue));
@@ -2574,7 +2585,8 @@
*/
@Override
public boolean nvWriteCdmaPrl(byte[] preferredRoamingList) {
- enforceModifyPermissionOrCarrierPrivilege(getDefaultSubscription());
+ TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
+ mApp, getDefaultSubscription(), "nvWriteCdmaPrl");
if (DBG) log("nvWriteCdmaPrl: value: " + HexDump.toHexString(preferredRoamingList));
Boolean success = (Boolean) sendRequest(CMD_NV_WRITE_CDMA_PRL, preferredRoamingList);
if (DBG) log("nvWriteCdmaPrl: " + (success ? "ok" : "fail"));
@@ -2590,7 +2602,8 @@
*/
@Override
public boolean nvResetConfig(int resetType) {
- enforceModifyPermissionOrCarrierPrivilege(getDefaultSubscription());
+ TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
+ mApp, getDefaultSubscription(), "nvResetConfig");
if (DBG) log("nvResetConfig: type " + resetType);
Boolean success = (Boolean) sendRequest(CMD_NV_RESET_CONFIG, resetType);
if (DBG) log("nvResetConfig: type " + resetType + ' ' + (success ? "ok" : "fail"));
@@ -2607,7 +2620,8 @@
}
public String[] getPcscfAddress(String apnType, String callingPackage) {
- if (!canReadPhoneState(callingPackage, "getPcscfAddress")) {
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+ mApp, callingPackage, "getPcscfAddress")) {
return new String[0];
}
@@ -2673,6 +2687,14 @@
return PhoneFactory.getImsResolver().getImsConfig(slotId, feature);
}
+ /**
+ * @return true if emergency calling is available on IMS, false if it should fallback to CS.
+ */
+ public boolean isEmergencyMmTelAvailable(int slotId) {
+ enforceModifyPermission();
+ return PhoneFactory.getImsResolver().isEmergencyMmTelAvailable(slotId);
+ }
+
public void setImsRegistrationState(boolean registered) {
enforceModifyPermission();
mPhone.setImsRegistrationState(registered);
@@ -2684,7 +2706,8 @@
*/
@Override
public void setNetworkSelectionModeAutomatic(int subId) {
- enforceModifyPermissionOrCarrierPrivilege(subId);
+ TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
+ mApp, subId, "setNetworkSelectionModeAutomatic");
if (DBG) log("setNetworkSelectionModeAutomatic: subId " + subId);
sendRequest(CMD_SET_NETWORK_SELECTION_MODE_AUTOMATIC, null, subId);
}
@@ -2695,7 +2718,8 @@
@Override
public boolean setNetworkSelectionModeManual(int subId, String operatorNumeric,
boolean persistSelection) {
- enforceModifyPermissionOrCarrierPrivilege(subId);
+ TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
+ mApp, subId, "setNetworkSelectionModeManual");
OperatorInfo operator = new OperatorInfo(
/* operatorAlphaLong */ "",
/* operatorAlphaShort */ "",
@@ -2711,7 +2735,8 @@
*/
@Override
public CellNetworkScanResult getCellNetworkScanResults(int subId) {
- enforceModifyPermissionOrCarrierPrivilege(subId);
+ TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
+ mApp, subId, "getCellNetworkScanResults");
if (DBG) log("getCellNetworkScanResults: subId " + subId);
CellNetworkScanResult result = (CellNetworkScanResult) sendRequest(
CMD_PERFORM_NETWORK_SCAN, null, subId);
@@ -2730,7 +2755,8 @@
@Override
public int requestNetworkScan(int subId, NetworkScanRequest request, Messenger messenger,
IBinder binder) {
- enforceModifyPermissionOrCarrierPrivilege(subId);
+ TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
+ mApp, subId, "requestNetworkScan");
return mNetworkScanRequestTracker.startNetworkScan(
request, messenger, binder, getPhone(subId));
}
@@ -2743,7 +2769,8 @@
*/
@Override
public void stopNetworkScan(int subId, int scanId) {
- enforceModifyPermissionOrCarrierPrivilege(subId);
+ TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
+ mApp, subId, "stopNetworkScan");
mNetworkScanRequestTracker.stopNetworkScan(scanId);
}
@@ -2755,7 +2782,8 @@
*/
@Override
public int getCalculatedPreferredNetworkType(String callingPackage) {
- if (!canReadPhoneState(callingPackage, "getCalculatedPreferredNetworkType")) {
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+ mApp, callingPackage, "getCalculatedPreferredNetworkType")) {
return RILConstants.PREFERRED_NETWORK_MODE;
}
@@ -2770,7 +2798,8 @@
*/
@Override
public int getPreferredNetworkType(int subId) {
- enforceModifyPermissionOrCarrierPrivilege(subId);
+ TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
+ mApp, subId, "getPreferredNetworkType");
if (DBG) log("getPreferredNetworkType");
int[] result = (int[]) sendRequest(CMD_GET_PREFERRED_NETWORK_TYPE, null, subId);
int networkType = (result != null ? result[0] : -1);
@@ -2787,7 +2816,8 @@
*/
@Override
public boolean setPreferredNetworkType(int subId, int networkType) {
- enforceModifyPermissionOrCarrierPrivilege(subId);
+ TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
+ mApp, subId, "setPreferredNetworkType");
if (DBG) log("setPreferredNetworkType: subId " + subId + " type " + networkType);
Boolean success = (Boolean) sendRequest(CMD_SET_PREFERRED_NETWORK_TYPE, networkType, subId);
if (DBG) log("setPreferredNetworkType: " + (success ? "ok" : "fail"));
@@ -2827,7 +2857,8 @@
*/
@Override
public void setUserDataEnabled(int subId, boolean enable) {
- enforceModifyPermissionOrCarrierPrivilege(subId);
+ TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
+ mApp, subId, "setUserDataEnabled");
int phoneId = mSubscriptionController.getPhoneId(subId);
if (DBG) log("setUserDataEnabled: subId=" + subId + " phoneId=" + phoneId);
Phone phone = PhoneFactory.getPhone(phoneId);
@@ -2870,7 +2901,8 @@
mApp.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_NETWORK_STATE,
null);
} catch (Exception e) {
- enforceModifyPermissionOrCarrierPrivilege(subId);
+ TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
+ mApp, subId, "isUserDataEnabled");
}
int phoneId = mSubscriptionController.getPhoneId(subId);
if (DBG) log("isUserDataEnabled: subId=" + subId + " phoneId=" + phoneId);
@@ -2901,7 +2933,8 @@
mApp.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_NETWORK_STATE,
null);
} catch (Exception e) {
- enforceModifyPermissionOrCarrierPrivilege(subId);
+ TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
+ mApp, subId, "isDataEnabled");
}
int phoneId = mSubscriptionController.getPhoneId(subId);
if (DBG) log("isDataEnabled: subId=" + subId + " phoneId=" + phoneId);
@@ -2933,6 +2966,22 @@
}
@Override
+ public int getCarrierPrivilegeStatusForUid(int subId, int uid) {
+ final Phone phone = getPhone(subId);
+ if (phone == null) {
+ loge("getCarrierPrivilegeStatus: Invalid subId");
+ return TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
+ }
+ UiccProfile profile =
+ UiccController.getInstance().getUiccProfileForPhone(phone.getPhoneId());
+ if (profile == null) {
+ loge("getCarrierPrivilegeStatus: No UICC");
+ return TelephonyManager.CARRIER_PRIVILEGE_STATUS_RULES_NOT_LOADED;
+ }
+ return profile.getCarrierPrivilegeStatusForUid(phone.getContext().getPackageManager(), uid);
+ }
+
+ @Override
public int checkCarrierPrivilegesForPackage(String pkgName) {
if (TextUtils.isEmpty(pkgName))
return TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
@@ -3031,7 +3080,8 @@
@Override
public boolean setLine1NumberForDisplayForSubscriber(int subId, String alphaTag,
String number) {
- enforceCarrierPrivilege(subId);
+ TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(
+ subId, "setLine1NumberForDisplayForSubscriber");
final String iccId = getIccId(subId);
final Phone phone = getPhone(subId);
@@ -3077,7 +3127,8 @@
@Override
public String getLine1NumberForDisplay(int subId, String callingPackage) {
// This is open to apps with WRITE_SMS.
- if (!canReadPhoneNumber(callingPackage, "getLine1NumberForDisplay")) {
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneNumber(
+ mApp, callingPackage, "getLine1NumberForDisplay")) {
if (DBG_MERGE) log("getLine1NumberForDisplay returning null due to permission");
return null;
}
@@ -3097,7 +3148,8 @@
@Override
public String getLine1AlphaTagForDisplay(int subId, String callingPackage) {
- if (!canReadPhoneState(callingPackage, "getLine1AlphaTagForDisplay")) {
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+ mApp, callingPackage, "getLine1AlphaTagForDisplay")) {
return null;
}
@@ -3111,7 +3163,8 @@
@Override
public String[] getMergedSubscriberIds(String callingPackage) {
- if (!canReadPhoneState(callingPackage, "getMergedSubscriberIds")) {
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+ mApp, callingPackage, "getMergedSubscriberIds")) {
return null;
}
final Context context = mPhone.getContext();
@@ -3184,7 +3237,8 @@
@Override
public boolean setOperatorBrandOverride(int subId, String brand) {
- enforceCarrierPrivilege(subId);
+ TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(
+ subId, "setOperatorBrandOverride");
final Phone phone = getPhone(subId);
return phone == null ? false : phone.setOperatorBrandOverride(brand);
}
@@ -3193,7 +3247,7 @@
public boolean setRoamingOverride(int subId, List<String> gsmRoamingList,
List<String> gsmNonRoamingList, List<String> cdmaRoamingList,
List<String> cdmaNonRoamingList) {
- enforceCarrierPrivilege(subId);
+ TelephonyPermissions.enforceCallingOrSelfCarrierPrivilege(subId, "setRoamingOverride");
final Phone phone = getPhone(subId);
if (phone == null) {
return false;
@@ -3213,7 +3267,8 @@
@Override
public int getRadioAccessFamily(int phoneId, String callingPackage) {
- if (!canReadPhoneState(callingPackage, "getRadioAccessFamily")) {
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+ mApp, callingPackage, "getRadioAccessFamily")) {
return RadioAccessFamily.RAF_UNKNOWN;
}
@@ -3228,7 +3283,8 @@
@Override
public boolean isVideoCallingEnabled(String callingPackage) {
- if (!canReadPhoneState(callingPackage, "isVideoCallingEnabled")) {
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+ mApp, callingPackage, "isVideoCallingEnabled")) {
return false;
}
@@ -3265,6 +3321,14 @@
return mPhone.getContext().getResources().getBoolean(R.bool.hac_enabled);
}
+ public boolean isRttSupported() {
+ boolean isCarrierSupported =
+ mApp.getCarrierConfig().getBoolean(CarrierConfigManager.KEY_RTT_SUPPORTED_BOOL);
+ boolean isDeviceSupported =
+ mPhone.getContext().getResources().getBoolean(R.bool.config_support_rtt);
+ return isCarrierSupported && isDeviceSupported;
+ }
+
/**
* Returns the unique device ID of phone, for example, the IMEI for
* GSM and the MEID for CDMA phones. Return null if device ID is not available.
@@ -3274,7 +3338,8 @@
*/
@Override
public String getDeviceId(String callingPackage) {
- if (!canReadPhoneState(callingPackage, "getDeviceId")) {
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+ mApp, callingPackage, "getDeviceId")) {
return null;
}
@@ -3286,22 +3351,13 @@
}
}
- /*
- * {@hide}
- * Returns the IMS Registration Status
- */
- @Override
- public boolean isImsRegistered() {
- return mPhone.isImsRegistered();
- }
-
/**
* {@hide}
* Returns the IMS Registration Status on a particular subid
*
* @param subId
*/
- public boolean isImsRegisteredForSubscriber(int subId) {
+ public boolean isImsRegistered(int subId) {
Phone phone = getPhone(subId);
if (phone != null) {
return phone.isImsRegistered();
@@ -3315,93 +3371,53 @@
return PhoneUtils.getSubIdForPhoneAccount(phoneAccount);
}
- /*
- * {@hide}
- * Returns the IMS Registration Status
+ /**
+ * @return the VoWiFi calling availability.
*/
- public boolean isWifiCallingAvailable() {
- return mPhone.isWifiCallingEnabled();
- }
-
- /*
- * {@hide}
- * Returns the IMS Registration Status
- */
- public boolean isVolteAvailable() {
- return mPhone.isVolteEnabled();
- }
-
- /*
- * {@hide} Returns the IMS Registration Status
- */
- public boolean isVideoTelephonyAvailable() {
- return mPhone.isVideoEnabled();
- }
-
- private boolean canReadPhoneState(String callingPackage, String message) {
- try {
- mApp.enforceCallingOrSelfPermission(
- android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, message);
-
- // SKIP checking for run-time permission since caller or self has PRIVILEDGED permission
- return true;
- } catch (SecurityException e) {
- mApp.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_STATE,
- message);
- }
-
- if (mAppOps.noteOp(AppOpsManager.OP_READ_PHONE_STATE, Binder.getCallingUid(),
- callingPackage) != AppOpsManager.MODE_ALLOWED) {
+ public boolean isWifiCallingAvailable(int subId) {
+ Phone phone = getPhone(subId);
+ if (phone != null) {
+ return phone.isWifiCallingEnabled();
+ } else {
return false;
}
-
- return true;
}
/**
- * Besides READ_PHONE_STATE, WRITE_SMS and READ_SMS also allow apps to get phone numbers.
+ * @return the VoLTE availability.
*/
- private boolean canReadPhoneNumber(String callingPackage, String message) {
- // Default SMS app can always read it.
- if (mAppOps.noteOp(AppOpsManager.OP_WRITE_SMS,
- Binder.getCallingUid(), callingPackage) == AppOpsManager.MODE_ALLOWED) {
- return true;
+ public boolean isVolteAvailable(int subId) {
+ Phone phone = getPhone(subId);
+ if (phone != null) {
+ return phone.isVolteEnabled();
+ } else {
+ return false;
}
+ }
- try {
- return canReadPhoneState(callingPackage, message);
- } catch (SecurityException readPhoneStateSecurityException) {
+ /**
+ * @return the VT calling availability.
+ */
+ public boolean isVideoTelephonyAvailable(int subId) {
+ Phone phone = getPhone(subId);
+ if (phone != null) {
+ return phone.isVideoEnabled();
+ } else {
+ return false;
}
- // Can be read with READ_SMS too.
- try {
- mApp.enforceCallingOrSelfPermission(android.Manifest.permission.READ_SMS, message);
- int opCode = mAppOps.permissionToOpCode(android.Manifest.permission.READ_SMS);
- if (opCode != AppOpsManager.OP_NONE) {
- return mAppOps.noteOp(opCode, Binder.getCallingUid(), callingPackage)
- == AppOpsManager.MODE_ALLOWED;
- } else {
- return true;
- }
- } catch (SecurityException readSmsSecurityException) {
- }
- // Can be read with READ_PHONE_NUMBERS too.
- try {
- mApp.enforceCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_NUMBERS,
- message);
- int opCode = mAppOps.permissionToOpCode(android.Manifest.permission.READ_PHONE_NUMBERS);
- if (opCode != AppOpsManager.OP_NONE) {
- return mAppOps.noteOp(opCode, Binder.getCallingUid(), callingPackage)
- == AppOpsManager.MODE_ALLOWED;
- } else {
- return true;
- }
- } catch (SecurityException readPhoneNumberSecurityException) {
- }
+ }
- throw new SecurityException(message + ": Neither user " + Binder.getCallingUid() +
- " nor current process has" + android.Manifest.permission.READ_PHONE_STATE +
- ", " + android.Manifest.permission.READ_SMS + ", or " +
- android.Manifest.permission.READ_PHONE_NUMBERS);
+ /**
+ * @return the IMS registration technology for the MMTEL feature. Valid return values are
+ * defined in {@link ImsRegistrationImplBase}.
+ */
+ public @ImsRegistrationImplBase.ImsRegistrationTech int getImsRegTechnologyForMmTel(int subId) {
+ Phone phone = getPhone(subId);
+ if (phone != null) {
+ return phone.getImsRegistrationTech();
+ } else {
+ return ImsRegistrationImplBase.REGISTRATION_TECH_NONE;
+ }
}
@Override
@@ -3534,7 +3550,8 @@
@Override
public ServiceState getServiceStateForSubscriber(int subId, String callingPackage) {
- if (!canReadPhoneState(callingPackage, "getServiceStateForSubscriber")) {
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+ mApp, callingPackage, "getServiceStateForSubscriber")) {
return null;
}
@@ -3581,8 +3598,9 @@
mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
if (!TextUtils.equals(callingPackage,
TelecomManager.from(mPhone.getContext()).getDefaultDialerPackage())) {
- enforceModifyPermissionOrCarrierPrivilege(
- PhoneUtils.getSubIdForPhoneAccountHandle(phoneAccountHandle));
+ TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
+ mApp, PhoneUtils.getSubIdForPhoneAccountHandle(phoneAccountHandle),
+ "setVoicemailRingtoneUri");
}
Phone phone = PhoneUtils.getPhoneForPhoneAccountHandle(phoneAccountHandle);
if (phone == null){
@@ -3625,8 +3643,9 @@
mAppOps.checkPackage(Binder.getCallingUid(), callingPackage);
if (!TextUtils.equals(callingPackage,
TelecomManager.from(mPhone.getContext()).getDefaultDialerPackage())) {
- enforceModifyPermissionOrCarrierPrivilege(
- PhoneUtils.getSubIdForPhoneAccountHandle(phoneAccountHandle));
+ TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
+ mApp, PhoneUtils.getSubIdForPhoneAccountHandle(phoneAccountHandle),
+ "setVoicemailVibrationEnabled");
}
Phone phone = PhoneUtils.getPhoneForPhoneAccountHandle(phoneAccountHandle);
@@ -3749,7 +3768,8 @@
*/
@Override
public List<TelephonyHistogram> getTelephonyHistograms() {
- enforceModifyPermissionOrCarrierPrivilege(getDefaultSubscription());
+ TelephonyPermissions.enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
+ mApp, getDefaultSubscription(), "getTelephonyHistograms");
return RIL.getTelephonyRILTimingHistograms();
}
@@ -3916,7 +3936,8 @@
*/
@Override
public List<ClientRequestStats> getClientRequestStats(String callingPackage, int subId) {
- if (!canReadPhoneState(callingPackage, "getClientRequestStats")) {
+ if (!TelephonyPermissions.checkCallingOrSelfReadPhoneState(
+ mApp, callingPackage, "getClientRequestStats")) {
return null;
}
@@ -4035,7 +4056,8 @@
}
- infos[i] = new UiccSlotInfo(slot.isActive(), slot.isEuicc(), cardId, cardState);
+ infos[i] = new UiccSlotInfo(
+ slot.isActive(), slot.isEuicc(), cardId, cardState, slot.getPhoneId());
}
return infos;
}
@@ -4045,4 +4067,16 @@
enforceModifyPermission();
return (Boolean) sendRequest(CMD_SWITCH_SLOTS, physicalSlots);
}
+
+ @Override
+ public void setRadioIndicationUpdateMode(int subId, int filters, int mode) {
+ enforceModifyPermission();
+ final Phone phone = getPhone(subId);
+ if (phone == null) {
+ loge("setRadioIndicationUpdateMode fails with invalid subId: " + subId);
+ return;
+ }
+
+ phone.setRadioIndicationUpdateMode(filters, mode);
+ }
}
diff --git a/src/com/android/phone/PhoneUtils.java b/src/com/android/phone/PhoneUtils.java
index 50e7f0a..a153cf7 100644
--- a/src/com/android/phone/PhoneUtils.java
+++ b/src/com/android/phone/PhoneUtils.java
@@ -32,7 +32,6 @@
import android.os.Message;
import android.os.PersistableBundle;
import android.os.RemoteException;
-import android.os.SystemProperties;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.VideoProfile;
@@ -61,7 +60,6 @@
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.TelephonyCapabilities;
-import com.android.internal.telephony.TelephonyProperties;
import com.android.internal.telephony.sip.SipPhone;
import com.android.phone.CallGatewayManager.RawGatewayInfo;
@@ -1117,54 +1115,6 @@
}
/**
- * Given an Intent (which is presumably the ACTION_CALL intent that
- * initiated this outgoing call), figure out the actual phone number we
- * should dial.
- *
- * Note that the returned "number" may actually be a SIP address,
- * if the specified intent contains a sip: URI.
- *
- * This method is basically a wrapper around PhoneUtils.getNumberFromIntent(),
- * except it's also aware of the EXTRA_ACTUAL_NUMBER_TO_DIAL extra.
- * (That extra, if present, tells us the exact string to pass down to the
- * telephony layer. It's guaranteed to be safe to dial: it's either a PSTN
- * phone number with separators and keypad letters stripped out, or a raw
- * unencoded SIP address.)
- *
- * @return the phone number corresponding to the specified Intent, or null
- * if the Intent has no action or if the intent's data is malformed or
- * missing.
- *
- * @throws VoiceMailNumberMissingException if the intent
- * contains a "voicemail" URI, but there's no voicemail
- * number configured on the device.
- */
- public static String getInitialNumber(Intent intent)
- throws PhoneUtils.VoiceMailNumberMissingException {
- if (DBG) log("getInitialNumber(): " + intent);
-
- String action = intent.getAction();
- if (TextUtils.isEmpty(action)) {
- return null;
- }
-
- // If the EXTRA_ACTUAL_NUMBER_TO_DIAL extra is present, get the phone
- // number from there. (That extra takes precedence over the actual data
- // included in the intent.)
- if (intent.hasExtra(OutgoingCallBroadcaster.EXTRA_ACTUAL_NUMBER_TO_DIAL)) {
- String actualNumberToDial =
- intent.getStringExtra(OutgoingCallBroadcaster.EXTRA_ACTUAL_NUMBER_TO_DIAL);
- if (DBG) {
- log("==> got EXTRA_ACTUAL_NUMBER_TO_DIAL; returning '"
- + toLogSafePhoneNumber(actualNumberToDial) + "'");
- }
- return actualNumberToDial;
- }
-
- return getNumberFromIntent(PhoneGlobals.getInstance(), intent);
- }
-
- /**
* Gets the phone number to be called from an intent. Requires a Context
* to access the contacts database, and a Phone to access the voicemail
* number.
diff --git a/src/com/android/phone/ProcessOutgoingCallTest.java b/src/com/android/phone/ProcessOutgoingCallTest.java
deleted file mode 100644
index c76fb43..0000000
--- a/src/com/android/phone/ProcessOutgoingCallTest.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.phone;
-
-import android.app.SearchManager;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
-import android.util.Log;
-
-/**
- * ProcessOutgoingCallTest tests {@link OutgoingCallBroadcaster} by performing
- * a couple of simple modifications to outgoing calls, and by printing log
- * messages for each call.
- */
-public class ProcessOutgoingCallTest extends BroadcastReceiver {
- private static final String TAG = "ProcessOutgoingCallTest";
- private static final String AREACODE = "617";
-
- private static final boolean LOGV = false;
-
- private static final boolean REDIRECT_411_TO_GOOG411 = true;
- private static final boolean SEVEN_DIGIT_DIALING = true;
- private static final boolean POUND_POUND_SEARCH = true;
- private static final boolean BLOCK_555 = true;
-
- public void onReceive(Context context, Intent intent) {
- if (intent.getAction().equals(Intent.ACTION_NEW_OUTGOING_CALL)) {
- String number = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
- if (LOGV) Log.v(TAG, "Received intent " + intent + " (number = " + number + ".");
- /* Example of how to redirect calls from one number to another. */
- if (REDIRECT_411_TO_GOOG411 && number.equals("411")) {
- setResultData("18004664411");
- }
-
- /* Example of how to modify the phone number in flight. */
- if (SEVEN_DIGIT_DIALING && number.length() == 7) {
- setResultData(AREACODE + number);
- }
-
- /* Example of how to route a call to another Application. */
- if (POUND_POUND_SEARCH && number.startsWith("##")) {
- Intent newIntent = new Intent(Intent.ACTION_SEARCH);
- newIntent.putExtra(SearchManager.QUERY, number.substring(2));
- newIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- context.startActivity(newIntent);
- setResultData(null);
- }
-
- /* Example of how to deny calls to a particular number.
- * Note that no UI is displayed to the user -- the call simply
- * does not happen. It is the application's responaibility to
- * explain this to the user. */
- int length = number.length();
- if (BLOCK_555 && length >= 7) {
- String exchange = number.substring(length - 7, length - 4);
- Log.v(TAG, "exchange = " + exchange);
- if (exchange.equals("555")) {
- setResultData(null);
- }
- }
- }
- }
-}
diff --git a/src/com/android/phone/TimeConsumingPreferenceActivity.java b/src/com/android/phone/TimeConsumingPreferenceActivity.java
index e626a53..354c4bb 100644
--- a/src/com/android/phone/TimeConsumingPreferenceActivity.java
+++ b/src/com/android/phone/TimeConsumingPreferenceActivity.java
@@ -23,7 +23,7 @@
public class TimeConsumingPreferenceActivity extends PreferenceActivity
implements TimeConsumingPreferenceListener,
DialogInterface.OnCancelListener {
- private static final String LOG_TAG = "TimeConsumingPreferenceActivity";
+ private static final String LOG_TAG = "TimeConsumingPrefActivity";
private final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
private class DismissOnClickListener implements DialogInterface.OnClickListener {
@@ -155,8 +155,7 @@
@Override
public void onStarted(Preference preference, boolean reading) {
if (DBG) dumpState();
- if (DBG) Log.d(LOG_TAG, "onStarted, preference=" + preference.getKey()
- + ", reading=" + reading);
+ Log.i(LOG_TAG, "onStarted, preference=" + preference.getKey() + ", reading=" + reading);
mBusyList.add(preference.getKey());
if (mIsForeground) {
@@ -172,8 +171,7 @@
@Override
public void onFinished(Preference preference, boolean reading) {
if (DBG) dumpState();
- if (DBG) Log.d(LOG_TAG, "onFinished, preference=" + preference.getKey()
- + ", reading=" + reading);
+ Log.i(LOG_TAG, "onFinished, preference=" + preference.getKey() + ", reading=" + reading);
mBusyList.remove(preference.getKey());
if (mBusyList.isEmpty()) {
@@ -189,7 +187,7 @@
@Override
public void onError(Preference preference, int error) {
if (DBG) dumpState();
- if (DBG) Log.d(LOG_TAG, "onError, preference=" + preference.getKey() + ", error=" + error);
+ Log.i(LOG_TAG, "onError, preference=" + preference.getKey() + ", error=" + error);
if (mIsForeground) {
showDialog(error);
diff --git a/src/com/android/phone/settings/AccessibilitySettingsFragment.java b/src/com/android/phone/settings/AccessibilitySettingsFragment.java
index af7d4bf..8ec747c 100644
--- a/src/com/android/phone/settings/AccessibilitySettingsFragment.java
+++ b/src/com/android/phone/settings/AccessibilitySettingsFragment.java
@@ -24,8 +24,10 @@
import android.preference.PreferenceFragment;
import android.preference.PreferenceScreen;
import android.provider.Settings;
+import android.telecom.TelecomManager;
import android.telephony.CarrierConfigManager;
import android.telephony.PhoneStateListener;
+import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.util.Log;
@@ -41,6 +43,7 @@
private static final String BUTTON_TTY_KEY = "button_tty_mode_key";
private static final String BUTTON_HAC_KEY = "button_hac_key";
+ private static final String BUTTON_RTT_KEY = "button_rtt_key";
private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
/**
@@ -67,6 +70,7 @@
private TtyModeListPreference mButtonTty;
private SwitchPreference mButtonHac;
+ private SwitchPreference mButtonRtt;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -80,6 +84,7 @@
mButtonTty = (TtyModeListPreference) findPreference(
getResources().getString(R.string.tty_mode_key));
mButtonHac = (SwitchPreference) findPreference(BUTTON_HAC_KEY);
+ mButtonRtt = (SwitchPreference) findPreference(BUTTON_RTT_KEY);
if (PhoneGlobals.getInstance().phoneMgr.isTtyModeSupported()) {
mButtonTty.init();
@@ -96,6 +101,17 @@
getPreferenceScreen().removePreference(mButtonHac);
mButtonHac = null;
}
+
+ if (PhoneGlobals.getInstance().phoneMgr.isRttSupported()) {
+ // TODO: this is going to be a on/off switch for now. Ask UX about how to integrate
+ // this settings with TTY
+ boolean rttOn = Settings.System.getInt(
+ mContext.getContentResolver(), Settings.System.RTT_CALLING_MODE, 0) != 0;
+ mButtonRtt.setChecked(rttOn);
+ } else {
+ getPreferenceScreen().removePreference(mButtonRtt);
+ mButtonRtt = null;
+ }
}
@Override
@@ -129,7 +145,18 @@
hac == SettingsConstants.HAC_ENABLED
? SettingsConstants.HAC_VAL_ON : SettingsConstants.HAC_VAL_OFF);
return true;
+ } else if (preference == mButtonRtt) {
+ Log.i(LOG_TAG, "RTT setting changed -- now " + mButtonRtt.isChecked());
+ int rttMode = mButtonRtt.isChecked() ? 1 : 0;
+ Settings.System.putInt(mContext.getContentResolver(), Settings.System.RTT_CALLING_MODE,
+ rttMode);
+ // Update RTT config with IMS Manager
+ ImsManager imsManager = ImsManager.getInstance(getContext(),
+ SubscriptionManager.getDefaultVoicePhoneId());
+ imsManager.setRttEnabled(mButtonRtt.isChecked());
+ return true;
}
+
return false;
}
diff --git a/src/com/android/services/telephony/ImsConference.java b/src/com/android/services/telephony/ImsConference.java
index 8f0c44a..3cf420a 100644
--- a/src/com/android/services/telephony/ImsConference.java
+++ b/src/com/android/services/telephony/ImsConference.java
@@ -271,10 +271,10 @@
long connectTime = conferenceHost.getOriginalConnection().getConnectTime();
long connectElapsedTime = conferenceHost.getOriginalConnection().getConnectTimeReal();
setConnectionTime(connectTime);
- setConnectionElapsedTime(connectElapsedTime);
+ setConnectionStartElapsedRealTime(connectElapsedTime);
// Set the connectTime in the connection as well.
conferenceHost.setConnectTimeMillis(connectTime);
- conferenceHost.setConnectElapsedTimeMillis(connectElapsedTime);
+ conferenceHost.setConnectionStartElapsedRealTime(connectElapsedTime);
mTelephonyConnectionService = telephonyConnectionService;
setConferenceHost(conferenceHost);
diff --git a/src/com/android/services/telephony/PstnIncomingCallNotifier.java b/src/com/android/services/telephony/PstnIncomingCallNotifier.java
index e007bb4..4dfaf44 100644
--- a/src/com/android/services/telephony/PstnIncomingCallNotifier.java
+++ b/src/com/android/services/telephony/PstnIncomingCallNotifier.java
@@ -36,6 +36,7 @@
import com.android.internal.telephony.cdma.CdmaCallWaitingNotification;
import com.android.internal.telephony.imsphone.ImsExternalCallTracker;
import com.android.internal.telephony.imsphone.ImsExternalConnection;
+import com.android.internal.telephony.imsphone.ImsPhoneConnection;
import com.android.phone.PhoneUtils;
import com.google.common.base.Preconditions;
@@ -243,6 +244,11 @@
extras.putLong(TelecomManager.EXTRA_CALL_CREATED_TIME_MILLIS,
SystemClock.elapsedRealtime());
+ if (connection.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) {
+ if (((ImsPhoneConnection) connection).isRttEnabledForCall()) {
+ extras.putBoolean(TelecomManager.EXTRA_START_CALL_WITH_RTT, true);
+ }
+ }
PhoneAccountHandle handle = findCorrectPhoneAccountHandle();
if (handle == null) {
try {
diff --git a/src/com/android/services/telephony/TelecomAccountRegistry.java b/src/com/android/services/telephony/TelecomAccountRegistry.java
index 17c6049..3fb8d32 100644
--- a/src/com/android/services/telephony/TelecomAccountRegistry.java
+++ b/src/com/android/services/telephony/TelecomAccountRegistry.java
@@ -234,9 +234,7 @@
extras.putBoolean(PhoneAccount.EXTRA_PLAY_CALL_RECORDING_TONE, true);
}
- boolean isDeviceRttSupported = mContext.getResources().getBoolean(
- R.bool.config_support_rtt);
- if (isDeviceRttSupported && isCarrierRttSupported()) {
+ if (PhoneGlobals.getInstance().phoneMgr.isRttSupported()) {
capabilities |= PhoneAccount.CAPABILITY_RTT;
}
@@ -405,12 +403,6 @@
b.getBoolean(CarrierConfigManager.KEY_SUPPORT_VIDEO_CONFERENCE_CALL_BOOL);
}
- private boolean isCarrierRttSupported() {
- PersistableBundle b =
- PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
- return b != null && b.getBoolean(CarrierConfigManager.KEY_RTT_SUPPORTED_BOOL);
- }
-
/**
* Determines from carrier config whether merging of wifi calls is allowed when VoWIFI is
* turned off.
diff --git a/src/com/android/services/telephony/TelephonyConnection.java b/src/com/android/services/telephony/TelephonyConnection.java
index ff06005..0fe6980 100644
--- a/src/com/android/services/telephony/TelephonyConnection.java
+++ b/src/com/android/services/telephony/TelephonyConnection.java
@@ -22,6 +22,7 @@
import android.os.AsyncResult;
import android.os.Bundle;
import android.os.Handler;
+import android.os.Looper;
import android.os.Message;
import android.os.PersistableBundle;
import android.telecom.CallAudioState;
@@ -37,6 +38,7 @@
import android.telephony.PhoneNumberUtils;
import android.telephony.TelephonyManager;
import android.telephony.ims.ImsCallProfile;
+import android.text.TextUtils;
import android.util.Pair;
import com.android.ims.ImsCall;
@@ -95,7 +97,7 @@
private static final int MSG_CDMA_VOICE_PRIVACY_OFF = 16;
private static final int MSG_HANGUP = 17;
- private final Handler mHandler = new Handler() {
+ private final Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
@@ -169,10 +171,7 @@
new ArrayList(Arrays.asList(mSsNotification.history)));
putExtras(lastForwardedNumber);
}
- if (mSsNotification.code
- == SuppServiceNotification.MO_CODE_CALL_FORWARDED) {
- sendConnectionEvent(TelephonyManager.EVENT_CALL_FORWARDED, null);
- }
+ handleSuppServiceNotification(mSsNotification);
}
}
break;
@@ -255,6 +254,115 @@
};
/**
+ * Handles {@link SuppServiceNotification}s pertinent to Telephony.
+ * @param ssn the notification.
+ */
+ private void handleSuppServiceNotification(SuppServiceNotification ssn) {
+ Log.i(this, "handleSuppServiceNotification: type=%d, code=%d", ssn.notificationType,
+ ssn.code);
+ if (ssn.notificationType == SuppServiceNotification.NOTIFICATION_TYPE_CODE_1
+ && ssn.code == SuppServiceNotification.CODE_1_CALL_FORWARDED) {
+ sendConnectionEvent(TelephonyManager.EVENT_CALL_FORWARDED, null);
+ }
+ sendSuppServiceNotificationEvent(ssn.notificationType, ssn.code);
+ }
+
+ /**
+ * Sends a supplementary service notification connection event.
+ * This connection event includes the type and code, as well as a human readable message which
+ * is suitable for display to the user if the UI chooses to do so.
+ * @param type the {@link SuppServiceNotification#type}.
+ * @param code the {@link SuppServiceNotification#code}.
+ */
+ private void sendSuppServiceNotificationEvent(int type, int code) {
+ Bundle extras = new Bundle();
+ extras.putInt(TelephonyManager.EXTRA_NOTIFICATION_TYPE, type);
+ extras.putInt(TelephonyManager.EXTRA_NOTIFICATION_CODE, code);
+ extras.putCharSequence(TelephonyManager.EXTRA_NOTIFICATION_MESSAGE,
+ getSuppServiceMessage(type, code));
+ sendConnectionEvent(TelephonyManager.EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION, extras);
+ }
+
+ /**
+ * Retrieves a human-readable message for a supplementary service notification.
+ * This message is suitable for display to the user.
+ * @param type the code group.
+ * @param code the code.
+ * @return A {@link CharSequence} containing the message, or {@code null} if none defined.
+ */
+ private CharSequence getSuppServiceMessage(int type, int code) {
+ int messageId = -1;
+ if (type == SuppServiceNotification.NOTIFICATION_TYPE_CODE_1) {
+ switch (code) {
+ case SuppServiceNotification.CODE_1_CALL_DEFLECTED:
+ messageId = R.string.supp_service_notification_call_deflected;
+ break;
+ case SuppServiceNotification.CODE_1_CALL_FORWARDED:
+ messageId = R.string.supp_service_notification_call_forwarded;
+ break;
+ case SuppServiceNotification.CODE_1_CALL_IS_WAITING:
+ messageId = R.string.supp_service_notification_call_waiting;
+ break;
+ case SuppServiceNotification.CODE_1_CLIR_SUPPRESSION_REJECTED:
+ messageId = R.string.supp_service_clir_suppression_rejected;
+ break;
+ case SuppServiceNotification.CODE_1_CUG_CALL:
+ messageId = R.string.supp_service_closed_user_group_call;
+ break;
+ case SuppServiceNotification.CODE_1_INCOMING_CALLS_BARRED:
+ messageId = R.string.supp_service_incoming_calls_barred;
+ break;
+ case SuppServiceNotification.CODE_1_OUTGOING_CALLS_BARRED:
+ messageId = R.string.supp_service_outgoing_calls_barred;
+ break;
+ case SuppServiceNotification.CODE_1_SOME_CF_ACTIVE:
+ // Intentional fall through.
+ case SuppServiceNotification.CODE_1_UNCONDITIONAL_CF_ACTIVE:
+ messageId = R.string.supp_service_call_forwarding_active;
+ break;
+ }
+ } else if (type == SuppServiceNotification.NOTIFICATION_TYPE_CODE_2) {
+ switch (code) {
+ case SuppServiceNotification.CODE_2_ADDITIONAL_CALL_FORWARDED:
+ messageId = R.string.supp_service_additional_call_forwarded;
+ break;
+ case SuppServiceNotification.CODE_2_CALL_CONNECTED_ECT:
+ messageId = R.string.supp_service_additional_ect_connected;
+ break;
+ case SuppServiceNotification.CODE_2_CALL_CONNECTING_ECT:
+ messageId = R.string.supp_service_additional_ect_connecting;
+ break;
+ case SuppServiceNotification.CODE_2_CALL_ON_HOLD:
+ messageId = R.string.supp_service_call_on_hold;
+ break;
+ case SuppServiceNotification.CODE_2_CALL_RETRIEVED:
+ messageId = R.string.supp_service_call_resumed;
+ break;
+ case SuppServiceNotification.CODE_2_CUG_CALL:
+ messageId = R.string.supp_service_closed_user_group_call;
+ break;
+ case SuppServiceNotification.CODE_2_DEFLECTED_CALL:
+ messageId = R.string.supp_service_deflected_call;
+ break;
+ case SuppServiceNotification.CODE_2_FORWARDED_CALL:
+ messageId = R.string.supp_service_forwarded_call;
+ break;
+ case SuppServiceNotification.CODE_2_MULTI_PARTY_CALL:
+ messageId = R.string.supp_service_conference_call;
+ break;
+ case SuppServiceNotification.CODE_2_ON_HOLD_CALL_RELEASED:
+ messageId = R.string.supp_service_held_call_released;
+ break;
+ }
+ }
+ if (messageId != -1 && getPhone() != null && getPhone().getContext() != null) {
+ return getPhone().getContext().getText(messageId);
+ } else {
+ return null;
+ }
+ }
+
+ /**
* @return {@code true} if carrier video conferencing is supported, {@code false} otherwise.
*/
public boolean isCarrierVideoConferencingSupported() {
@@ -438,6 +546,7 @@
@Override
public void onRttModifyResponseReceived(int status) {
+ updateConnectionProperties();
if (status == RttModifyStatus.SESSION_MODIFY_REQUEST_SUCCESS) {
sendRttInitiationSuccess();
} else {
@@ -447,8 +556,21 @@
@Override
public void onDisconnect(int cause) {
- Log.i(this, "onDisconnect: cause=%s", DisconnectCause.toString(cause));
- mHandler.obtainMessage(MSG_DISCONNECT);
+ Log.i(this, "onDisconnect: callId=%s, cause=%s", getTelecomCallId(),
+ DisconnectCause.toString(cause));
+ mHandler.obtainMessage(MSG_DISCONNECT).sendToTarget();
+ }
+
+ @Override
+ public void onRttInitiated() {
+ updateConnectionProperties();
+ sendRttInitiationSuccess();
+ }
+
+ @Override
+ public void onRttTerminated() {
+ updateConnectionProperties();
+ sendRttSessionRemotelyTerminated();
}
};
@@ -458,6 +580,7 @@
private boolean mIsStateOverridden = false;
private Call.State mOriginalConnectionState = Call.State.IDLE;
private Call.State mConnectionOverriddenState = Call.State.IDLE;
+ private RttTextStream mRttTextStream = null;
private boolean mWasImsConnection;
@@ -637,6 +760,45 @@
}
@Override
+ public void onDeflect(Uri address) {
+ Log.v(this, "onDeflect");
+ if (mOriginalConnection != null && isValidRingingCall()) {
+ if (address == null) {
+ Log.w(this, "call deflect address uri is null");
+ return;
+ }
+ String scheme = address.getScheme();
+ String deflectNumber = "";
+ String uriString = address.getSchemeSpecificPart();
+ if (!PhoneAccount.SCHEME_VOICEMAIL.equals(scheme)) {
+ if (!PhoneAccount.SCHEME_TEL.equals(scheme)) {
+ Log.w(this, "onDeflect, address scheme is not of type tel instead: " +
+ scheme);
+ return;
+ }
+ if (PhoneNumberUtils.isUriNumber(uriString)) {
+ Log.w(this, "Invalid deflect address. Not a legal PSTN number.");
+ return;
+ }
+ deflectNumber = PhoneNumberUtils.convertAndStrip(uriString);
+ if (TextUtils.isEmpty(deflectNumber)) {
+ Log.w(this, "Empty deflect number obtained from address uri");
+ return;
+ }
+ } else {
+ Log.w(this, "Cannot deflect to voicemail uri");
+ return;
+ }
+
+ try {
+ mOriginalConnection.deflect(deflectNumber);
+ } catch (CallStateException e) {
+ Log.e(this, e, "Failed to deflect call.");
+ }
+ }
+ }
+
+ @Override
public void onReject() {
Log.v(this, "onReject");
if (isValidRingingCall()) {
@@ -686,7 +848,7 @@
@Override
public void onStopRtt() {
- // This is not supported by carriers/vendor yet. No-op for now.
+ Log.i(this, "Stopping RTT currently not supported. Doing nothing.");
}
@Override
@@ -808,6 +970,8 @@
newCapabilities = changeBitmask(newCapabilities, CAPABILITY_CAN_PULL_CALL,
isExternalConnection() && isPullable());
newCapabilities = applyConferenceTerminationCapabilities(newCapabilities);
+ newCapabilities = changeBitmask(newCapabilities, CAPABILITY_SUPPORT_DEFLECT,
+ isImsConnection() && canDeflectImsCalls());
if (getConnectionCapabilities() != newCapabilities) {
setConnectionCapabilities(newCapabilities);
@@ -842,6 +1006,7 @@
mIsCdmaVoicePrivacyEnabled);
newProperties = changeBitmask(newProperties, PROPERTY_ASSISTED_DIALING_USED,
mIsUsingAssistedDialing);
+ newProperties = changeBitmask(newProperties, PROPERTY_IS_RTT, isRtt());
if (getConnectionProperties() != newProperties) {
setConnectionProperties(newProperties);
@@ -1085,6 +1250,17 @@
return PhoneGlobals.getInstance().getCarrierConfigForSubId(phone.getSubId());
}
+ private boolean canDeflectImsCalls() {
+ PersistableBundle b = getCarrierConfig();
+ // Return false if the CarrierConfig is unavailable
+ if (b != null) {
+ return b.getBoolean(
+ CarrierConfigManager.KEY_CARRIER_ALLOW_DEFLECT_IMS_CALL_BOOL) &&
+ isValidRingingCall();
+ }
+ return false;
+ }
+
/**
* Determines if the device will respect the value of the
* {@link CarrierConfigManager#KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL} configuration option.
@@ -1517,6 +1693,15 @@
}
/**
+ * Determines if the current connection has RTT enabled.
+ */
+ private boolean isRtt() {
+ return mOriginalConnection != null
+ && mOriginalConnection.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS
+ && ((ImsPhoneConnection) mOriginalConnection).isRttEnabledForCall();
+ }
+
+ /**
* Determines if the current connection is pullable.
*
* A connection is deemed to be pullable if the original connection capabilities state that it
@@ -1650,6 +1835,14 @@
return false;
}
+ public void setRttTextStream(RttTextStream s) {
+ mRttTextStream = s;
+ }
+
+ public RttTextStream getRttTextStream() {
+ return mRttTextStream;
+ }
+
/**
* For video calls, sets whether this connection supports pausing the outgoing video for the
* call using the {@link android.telecom.VideoProfile#STATE_PAUSED} VideoState.
diff --git a/src/com/android/services/telephony/TelephonyConnectionService.java b/src/com/android/services/telephony/TelephonyConnectionService.java
index 8584d42..6fd481b 100644
--- a/src/com/android/services/telephony/TelephonyConnectionService.java
+++ b/src/com/android/services/telephony/TelephonyConnectionService.java
@@ -52,6 +52,7 @@
import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.imsphone.ImsExternalCallTracker;
import com.android.internal.telephony.imsphone.ImsPhone;
+import com.android.internal.telephony.imsphone.ImsPhoneConnection;
import com.android.phone.MMIDialogActivity;
import com.android.phone.PhoneUtils;
import com.android.phone.R;
@@ -650,6 +651,7 @@
connection.setAddress(handle, PhoneConstants.PRESENTATION_ALLOWED);
connection.setInitializing();
connection.setVideoState(request.getVideoState());
+ connection.setRttTextStream(request.getRttTextStream());
return connection;
}
@@ -699,10 +701,11 @@
int videoState = originalConnection != null ? originalConnection.getVideoState() :
VideoProfile.STATE_AUDIO_ONLY;
- Connection connection =
+ TelephonyConnection connection =
createConnectionFor(phone, originalConnection, false /* isOutgoing */,
request.getAccountHandle(), request.getTelecomCallId(),
request.getAddress(), videoState);
+ handleIncomingRtt(request, originalConnection);
if (connection == null) {
return Connection.createCanceledConnection();
} else {
@@ -710,6 +713,36 @@
}
}
+ private void handleIncomingRtt(ConnectionRequest request,
+ com.android.internal.telephony.Connection originalConnection) {
+ if (originalConnection == null
+ || originalConnection.getPhoneType() != PhoneConstants.PHONE_TYPE_IMS) {
+ if (request.isRequestingRtt()) {
+ Log.w(this, "Requesting RTT on non-IMS call, ignoring");
+ }
+ return;
+ }
+
+ ImsPhoneConnection imsOriginalConnection = (ImsPhoneConnection) originalConnection;
+ if (!request.isRequestingRtt()) {
+ if (imsOriginalConnection.isRttEnabledForCall()) {
+ Log.i(this, "Incoming call requested RTT but was declined");
+ }
+ return;
+ }
+
+ if (!imsOriginalConnection.isRttEnabledForCall()) {
+ if (request.isRequestingRtt()) {
+ Log.w(this, "Incoming call processed as RTT but did not come in as one. Ignoring");
+ }
+ return;
+ }
+
+ Log.i(this, "Setting RTT stream on ImsPhoneConnection");
+ imsOriginalConnection.setCurrentRttTextStream(request.getRttTextStream());
+ imsOriginalConnection.getImsCall().setAnswerWithRtt();
+ }
+
/**
* Called by the {@link ConnectionService} when a newly created {@link Connection} has been
* added to the {@link ConnectionService} and sent to Telecom. Here it is safe to send
@@ -988,7 +1021,11 @@
com.android.internal.telephony.Connection originalConnection = null;
try {
if (phone != null) {
- originalConnection = phone.dial(number, null, videoState, extras);
+ originalConnection = phone.dial(number, new ImsPhone.ImsDialArgs.Builder()
+ .setVideoState(videoState)
+ .setIntentExtras(extras)
+ .setRttTextStream(connection.getRttTextStream())
+ .build());
}
} catch (CallStateException e) {
Log.e(this, e, "placeOutgoingConnection, phone.dial exception: " + e);
diff --git a/testapps/EmbmsServiceTestApp/Android.mk b/testapps/EmbmsServiceTestApp/Android.mk
index d8c4493..29b8112 100644
--- a/testapps/EmbmsServiceTestApp/Android.mk
+++ b/testapps/EmbmsServiceTestApp/Android.mk
@@ -10,6 +10,7 @@
LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, $(res_dirs))
LOCAL_PACKAGE_NAME := EmbmsTestService
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_CERTIFICATE := platform
LOCAL_PRIVILEGED_MODULE := true
diff --git a/testapps/EmbmsServiceTestApp/src/com/android/phone/testapps/embmsmw/EmbmsSampleDownloadService.java b/testapps/EmbmsServiceTestApp/src/com/android/phone/testapps/embmsmw/EmbmsSampleDownloadService.java
index 128793f..7bd0f70 100644
--- a/testapps/EmbmsServiceTestApp/src/com/android/phone/testapps/embmsmw/EmbmsSampleDownloadService.java
+++ b/testapps/EmbmsServiceTestApp/src/com/android/phone/testapps/embmsmw/EmbmsSampleDownloadService.java
@@ -371,11 +371,8 @@
c.onProgressUpdated(request, fileToDownload, 10, 10, 10, 10);
}
// Take a round-trip through the download request serialization to exercise it
- DownloadRequest request1 = new DownloadRequest.Builder(request.getSourceUri())
- .setSubscriptionId(request.getSubscriptionId())
- .setServiceId(request.getFileServiceId())
- .setOpaqueData(request.getOpaqueData())
- .build();
+ DownloadRequest request1 = DownloadRequest.Builder.fromSerializedRequest(
+ request.toByteArray()).build();
Intent downloadResultIntent =
new Intent(VendorUtils.ACTION_DOWNLOAD_RESULT_INTERNAL);
diff --git a/testapps/EmbmsTestDownloadApp/Android.mk b/testapps/EmbmsTestDownloadApp/Android.mk
index 080e5b0..445357c 100644
--- a/testapps/EmbmsTestDownloadApp/Android.mk
+++ b/testapps/EmbmsTestDownloadApp/Android.mk
@@ -14,6 +14,7 @@
LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, $(res_dirs))
LOCAL_PACKAGE_NAME := EmbmsTestDownloadApp
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_CERTIFICATE := platform
LOCAL_MODULE_TAGS := tests
diff --git a/testapps/EmbmsTestDownloadApp/src/com/android/phone/testapps/embmsdownload/DownloadCompletionReceiver.java b/testapps/EmbmsTestDownloadApp/src/com/android/phone/testapps/embmsdownload/DownloadCompletionReceiver.java
index 3c94b76..736b912 100644
--- a/testapps/EmbmsTestDownloadApp/src/com/android/phone/testapps/embmsdownload/DownloadCompletionReceiver.java
+++ b/testapps/EmbmsTestDownloadApp/src/com/android/phone/testapps/embmsdownload/DownloadCompletionReceiver.java
@@ -21,14 +21,12 @@
import android.content.Intent;
import android.net.Uri;
import android.telephony.MbmsDownloadSession;
-import android.telephony.mbms.DownloadRequest;
import android.telephony.mbms.FileInfo;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
-import java.nio.file.StandardCopyOption;
public class DownloadCompletionReceiver extends BroadcastReceiver {
@Override
@@ -41,22 +39,8 @@
}
Uri completedFile = intent.getParcelableExtra(
MbmsDownloadSession.EXTRA_MBMS_COMPLETED_FILE_URI);
- FileInfo completedFileInfo = intent.getParcelableExtra(
- MbmsDownloadSession.EXTRA_MBMS_FILE_INFO);
- DownloadRequest request = intent.getParcelableExtra(
- MbmsDownloadSession.EXTRA_MBMS_DOWNLOAD_REQUEST);
- Path destinationFile = getDestinationFile(context,
- request.getFileServiceId(), completedFileInfo);
- Path sourceFile = FileSystems.getDefault().getPath(completedFile.getPath());
- try {
- Files.move(sourceFile, destinationFile, StandardCopyOption.REPLACE_EXISTING);
- } catch (IOException e) {
- return;
- }
-
- EmbmsTestDownloadApp.getInstance().onDownloadDone(
- Uri.fromFile(destinationFile.toFile()));
+ EmbmsTestDownloadApp.getInstance().onDownloadDone(completedFile);
}
}
diff --git a/testapps/EmbmsTestDownloadApp/src/com/android/phone/testapps/embmsdownload/EmbmsTestDownloadApp.java b/testapps/EmbmsTestDownloadApp/src/com/android/phone/testapps/embmsdownload/EmbmsTestDownloadApp.java
index 4083f67..76baf05 100644
--- a/testapps/EmbmsTestDownloadApp/src/com/android/phone/testapps/embmsdownload/EmbmsTestDownloadApp.java
+++ b/testapps/EmbmsTestDownloadApp/src/com/android/phone/testapps/embmsdownload/EmbmsTestDownloadApp.java
@@ -44,6 +44,7 @@
import android.widget.Toast;
import java.io.File;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -203,7 +204,7 @@
Button bindButton = (Button) findViewById(R.id.bind_button);
bindButton.setOnClickListener((view) -> {
- mDownloadManager = MbmsDownloadSession.create(this, mCallback, mHandler);
+ mDownloadManager = MbmsDownloadSession.create(this, mHandler::post, mCallback);
});
Button setTempFileRootButton = (Button) findViewById(R.id.set_temp_root_button);
@@ -291,26 +292,28 @@
"No DownloadRequest Pending for progress...", Toast.LENGTH_SHORT).show();
return;
}
- mDownloadManager.registerStateCallback(req, new DownloadStateCallback(
- DownloadStateCallback.PROGRESS_UPDATES) {
- @Override
- public void onProgressUpdated(DownloadRequest request, FileInfo fileInfo,
- int currentDownloadSize, int fullDownloadSize, int currentDecodedSize,
- int fullDecodedSize) {
- Toast.makeText(EmbmsTestDownloadApp.this,
- "Progress Updated (" + fileInfo + ") cd: " + currentDecodedSize
- + " fd: " + fullDownloadSize, Toast.LENGTH_SHORT).show();
- }
+ mDownloadManager.registerStateCallback(req, sInstance.getMainThreadHandler()::post,
+ new DownloadStateCallback(DownloadStateCallback.PROGRESS_UPDATES) {
+ @Override
+ public void onProgressUpdated(DownloadRequest request, FileInfo fileInfo,
+ int currentDownloadSize, int fullDownloadSize,
+ int currentDecodedSize, int fullDecodedSize) {
+ Toast.makeText(EmbmsTestDownloadApp.this,
+ "Progress Updated (" + fileInfo + ") cd: " + currentDecodedSize
+ + " fd: " + fullDownloadSize, Toast.LENGTH_SHORT)
+ .show();
+ }
- @Override
- public void onStateUpdated(DownloadRequest request, FileInfo fileInfo,
- @MbmsDownloadSession.DownloadStatus int state) {
- // only registered for state callback, this shouldn't happen!
- Toast.makeText(EmbmsTestDownloadApp.this,
- "State ERROR: received state update for callback that didn't filter it",
- Toast.LENGTH_SHORT).show();
- }
- }, sInstance.getMainThreadHandler());
+ @Override
+ public void onStateUpdated(DownloadRequest request, FileInfo fileInfo,
+ @MbmsDownloadSession.DownloadStatus int state) {
+ // only registered for state callback, this shouldn't happen!
+ Toast.makeText(EmbmsTestDownloadApp.this,
+ "State ERROR: received state update for callback that didn't"
+ + " filter it",
+ Toast.LENGTH_SHORT).show();
+ }
+ });
});
Button registerStateCallback =
@@ -327,26 +330,27 @@
"No DownloadRequest Pending for state...", Toast.LENGTH_SHORT).show();
return;
}
- mDownloadManager.registerStateCallback(req, new DownloadStateCallback(
- DownloadStateCallback.STATE_UPDATES) {
- @Override
- public void onProgressUpdated(DownloadRequest request, FileInfo fileInfo,
- int currentDownloadSize, int fullDownloadSize, int currentDecodedSize,
- int fullDecodedSize) {
- // only registered for state callback, this shouldn't happen!
- Toast.makeText(EmbmsTestDownloadApp.this,
- "Progress ERROR: received progress update for callback that didn't "
- + "filter it", Toast.LENGTH_SHORT).show();
- }
+ mDownloadManager.registerStateCallback(req, sInstance.getMainThreadHandler()::post,
+ new DownloadStateCallback(DownloadStateCallback.STATE_UPDATES) {
+ @Override
+ public void onProgressUpdated(DownloadRequest request, FileInfo fileInfo,
+ int currentDownloadSize, int fullDownloadSize,
+ int currentDecodedSize, int fullDecodedSize) {
+ // only registered for state callback, this shouldn't happen!
+ Toast.makeText(EmbmsTestDownloadApp.this,
+ "Progress ERROR: received progress update for"
+ + " callback that didn't "
+ + "filter it", Toast.LENGTH_SHORT).show();
+ }
- @Override
- public void onStateUpdated(DownloadRequest request, FileInfo fileInfo,
- @MbmsDownloadSession.DownloadStatus int state) {
- Toast.makeText(EmbmsTestDownloadApp.this,
- "State Updated (" + fileInfo + ") state: " + state,
- Toast.LENGTH_SHORT).show();
- }
- }, sInstance.getMainThreadHandler());
+ @Override
+ public void onStateUpdated(DownloadRequest request, FileInfo fileInfo,
+ @MbmsDownloadSession.DownloadStatus int state) {
+ Toast.makeText(EmbmsTestDownloadApp.this,
+ "State Updated (" + fileInfo + ") state: " + state,
+ Toast.LENGTH_SHORT).show();
+ }
+ });
});
Button registerAllCallbacks =
@@ -363,24 +367,26 @@
"No DownloadRequest Pending for state...", Toast.LENGTH_SHORT).show();
return;
}
- mDownloadManager.registerStateCallback(req, new DownloadStateCallback() {
- @Override
- public void onProgressUpdated(DownloadRequest request, FileInfo fileInfo,
- int currentDownloadSize, int fullDownloadSize, int currentDecodedSize,
- int fullDecodedSize) {
- Toast.makeText(EmbmsTestDownloadApp.this,
- "Progress Updated (" + fileInfo + ") cd: " + currentDecodedSize
- + " fd: " + fullDownloadSize, Toast.LENGTH_SHORT).show();
- }
+ mDownloadManager.registerStateCallback(req, sInstance.getMainThreadHandler()::post,
+ new DownloadStateCallback() {
+ @Override
+ public void onProgressUpdated(DownloadRequest request, FileInfo fileInfo,
+ int currentDownloadSize, int fullDownloadSize,
+ int currentDecodedSize, int fullDecodedSize) {
+ Toast.makeText(EmbmsTestDownloadApp.this,
+ "Progress Updated (" + fileInfo + ") cd: " + currentDecodedSize
+ + " fd: " + fullDownloadSize, Toast.LENGTH_SHORT)
+ .show();
+ }
- @Override
- public void onStateUpdated(DownloadRequest request, FileInfo fileInfo,
- @MbmsDownloadSession.DownloadStatus int state) {
- Toast.makeText(EmbmsTestDownloadApp.this,
- "State Updated (" + fileInfo + ") state: " + state,
- Toast.LENGTH_SHORT).show();
- }
- }, sInstance.getMainThreadHandler());
+ @Override
+ public void onStateUpdated(DownloadRequest request, FileInfo fileInfo,
+ @MbmsDownloadSession.DownloadStatus int state) {
+ Toast.makeText(EmbmsTestDownloadApp.this,
+ "State Updated (" + fileInfo + ") state: " + state,
+ Toast.LENGTH_SHORT).show();
+ }
+ });
});
}
@@ -429,7 +435,8 @@
Intent completionIntent = new Intent(DOWNLOAD_DONE_ACTION);
completionIntent.setClass(this, DownloadCompletionReceiver.class);
- DownloadRequest request = new DownloadRequest.Builder(sourceUriBuilder.build())
+ DownloadRequest request = new DownloadRequest.Builder(sourceUriBuilder.build(),
+ getDestination(info.getServiceId()))
.setServiceInfo(info)
.setAppIntent(completionIntent)
.setSubscriptionId(SubscriptionManager.getDefaultSubscriptionId())
@@ -438,4 +445,24 @@
mDownloadManager.download(request);
mDownloadRequestAdapter.add(request);
}
+
+ private Uri getDestination(String serviceId) {
+ File dest;
+ try {
+ if (serviceId.contains("2")) {
+ dest = new File(getFilesDir().getCanonicalFile(), "images/animals/");
+ if (!dest.exists()) {
+ dest.mkdirs();
+ }
+ } else {
+ dest = new File(getFilesDir().getCanonicalFile(), "images/");
+ if (!dest.exists()) {
+ dest.mkdirs();
+ }
+ }
+ return Uri.fromFile(dest);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
}
diff --git a/testapps/EmbmsTestStreamingApp/Android.mk b/testapps/EmbmsTestStreamingApp/Android.mk
index 45826b9..f574990 100644
--- a/testapps/EmbmsTestStreamingApp/Android.mk
+++ b/testapps/EmbmsTestStreamingApp/Android.mk
@@ -10,6 +10,7 @@
LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, $(res_dirs))
LOCAL_PACKAGE_NAME := EmbmsTestStreamingApp
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_CERTIFICATE := platform
LOCAL_MODULE_TAGS := tests
diff --git a/testapps/EmbmsTestStreamingApp/src/com/android/phone/testapps/embmsfrontend/EmbmsTestStreamingApp.java b/testapps/EmbmsTestStreamingApp/src/com/android/phone/testapps/embmsfrontend/EmbmsTestStreamingApp.java
index 0546c9d..75febda 100644
--- a/testapps/EmbmsTestStreamingApp/src/com/android/phone/testapps/embmsfrontend/EmbmsTestStreamingApp.java
+++ b/testapps/EmbmsTestStreamingApp/src/com/android/phone/testapps/embmsfrontend/EmbmsTestStreamingApp.java
@@ -154,7 +154,7 @@
Button bindButton = (Button) findViewById(R.id.bind_button);
bindButton.setOnClickListener((view) -> {
mStreamingManager = MbmsStreamingSession.create(
- EmbmsTestStreamingApp.this, mStreamingListener, mHandler);
+ EmbmsTestStreamingApp.this, mHandler::post, mStreamingListener);
});
Button getStreamingServicesButton = (Button)
diff --git a/testapps/EmbmsTestStreamingApp/src/com/android/phone/testapps/embmsfrontend/StreamingServiceTracker.java b/testapps/EmbmsTestStreamingApp/src/com/android/phone/testapps/embmsfrontend/StreamingServiceTracker.java
index 5244d30..d45b5fd 100644
--- a/testapps/EmbmsTestStreamingApp/src/com/android/phone/testapps/embmsfrontend/StreamingServiceTracker.java
+++ b/testapps/EmbmsTestStreamingApp/src/com/android/phone/testapps/embmsfrontend/StreamingServiceTracker.java
@@ -60,13 +60,13 @@
* Start streaming using the provided streaming session
*/
public boolean startStreaming(MbmsStreamingSession streamingManager) {
- mStreamingService =
- streamingManager.startStreaming(mStreamingServiceInfo, new Callback(), null);
+ mStreamingService = streamingManager.startStreaming(mStreamingServiceInfo,
+ mActivity.getMainThreadHandler()::post, new Callback());
return true;
}
public void stopStreaming() {
- mStreamingService.stopStreaming();
+ mStreamingService.close();
}
public String getServiceId() {
diff --git a/testapps/ImsTestService/Android.mk b/testapps/ImsTestService/Android.mk
index 6afb3d5..29df224 100644
--- a/testapps/ImsTestService/Android.mk
+++ b/testapps/ImsTestService/Android.mk
@@ -19,6 +19,7 @@
LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, $(res_dirs))
LOCAL_PACKAGE_NAME := ImsTestApp
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_CERTIFICATE := platform
LOCAL_PRIVILEGED_MODULE := true
diff --git a/testapps/TelephonyRegistryTestApp/Android.mk b/testapps/TelephonyRegistryTestApp/Android.mk
index ed1f2a3..8c0d286 100644
--- a/testapps/TelephonyRegistryTestApp/Android.mk
+++ b/testapps/TelephonyRegistryTestApp/Android.mk
@@ -9,6 +9,7 @@
LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, $(res_dirs))
LOCAL_PACKAGE_NAME := TelephonyRegistryTestApp
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_CERTIFICATE := platform
LOCAL_MODULE_TAGS := tests
diff --git a/tests/Android.mk b/tests/Android.mk
index b43533f..a6a79fe 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -21,6 +21,7 @@
LOCAL_JAVA_LIBRARIES := android.test.runner
LOCAL_PACKAGE_NAME := TeleServiceTests
+LOCAL_PRIVATE_PLATFORM_APIS := true
LOCAL_CERTIFICATE := platform
LOCAL_MODULE_TAGS := tests
diff --git a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
index eb8c48a..3bd2716 100644
--- a/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
+++ b/tests/src/com/android/services/telephony/TelephonyConnectionServiceTest.java
@@ -16,13 +16,29 @@
package com.android.services.telephony;
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.fail;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
import android.net.Uri;
+import android.os.AsyncResult;
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.test.filters.FlakyTest;
+import android.support.test.runner.AndroidJUnit4;
import android.telecom.DisconnectCause;
import android.telecom.TelecomManager;
import android.telephony.RadioAccessFamily;
import android.telephony.ServiceState;
-import android.support.test.filters.FlakyTest;
-import android.support.test.runner.AndroidJUnit4;
import android.telephony.TelephonyManager;
import android.test.suitebuilder.annotation.SmallTest;
@@ -30,29 +46,18 @@
import com.android.internal.telephony.CallStateException;
import com.android.internal.telephony.Connection;
import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.gsm.SuppServiceNotification;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import java.util.ArrayList;
import java.util.List;
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.fail;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.nullable;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
/**
* Unit tests for TelephonyConnectionService.
*/
@@ -60,6 +65,7 @@
@RunWith(AndroidJUnit4.class)
public class TelephonyConnectionServiceTest extends TelephonyTestBase {
+ private static final long TIMEOUT_MS = 100;
private static final int SLOT_0_PHONE_ID = 0;
private static final int SLOT_1_PHONE_ID = 1;
@@ -533,7 +539,7 @@
// was redialed on the same slot
assertEquals(0, c.getNotifyPhoneAccountChangedCount());
try {
- verify(slot0Phone).dial(anyString(), any(), anyInt(), any());
+ verify(slot0Phone).dial(anyString(), any());
} catch (CallStateException e) {
// This shouldn't happen
fail();
@@ -565,7 +571,7 @@
// was never redialed
assertEquals(0, c.getNotifyPhoneAccountChangedCount());
try {
- verify(slot0Phone, never()).dial(anyString(), any(), anyInt(), any());
+ verify(slot0Phone, never()).dial(anyString(), any());
} catch (CallStateException e) {
// This shouldn't happen
fail();
@@ -605,7 +611,7 @@
// redialed on another slot
assertEquals(1, c.getNotifyPhoneAccountChangedCount());
try {
- verify(slot1Phone).dial(anyString(), any(), anyInt(), any());
+ verify(slot1Phone).dial(anyString(), any());
} catch (CallStateException e) {
// This shouldn't happen
fail();
@@ -643,7 +649,7 @@
// redialed on another slot
assertEquals(1, c.getNotifyPhoneAccountChangedCount());
try {
- verify(slot1Phone).dial(anyString(), any(), anyInt(), any());
+ verify(slot1Phone).dial(anyString(), any());
} catch (CallStateException e) {
// This shouldn't happen
fail();
@@ -694,8 +700,8 @@
// redialed on another slot
assertEquals(2, c.getNotifyPhoneAccountChangedCount());
try {
- verify(slot0Phone).dial(anyString(), any(), anyInt(), any());
- verify(slot1Phone).dial(anyString(), any(), anyInt(), any());
+ verify(slot0Phone).dial(anyString(), any());
+ verify(slot1Phone).dial(anyString(), any());
} catch (CallStateException e) {
// This shouldn't happen
fail();
@@ -744,14 +750,66 @@
// redialed on another slot
assertEquals(1, c.getNotifyPhoneAccountChangedCount());
try {
- verify(slot1Phone).dial(anyString(), any(), anyInt(), any());
- verify(slot0Phone, never()).dial(anyString(), any(), anyInt(), any());
+ verify(slot1Phone).dial(anyString(), any());
+ verify(slot0Phone, never()).dial(anyString(), any());
} catch (CallStateException e) {
// This shouldn't happen
fail();
}
}
+ @Test
+ @SmallTest
+ public void testSuppServiceNotification() {
+ TestTelephonyConnection c = new TestTelephonyConnection();
+
+ // We need to set the original connection to cause the supp service notification
+ // registration to occur.
+ Phone phone = c.getPhone();
+ c.setOriginalConnection(c.getOriginalConnection());
+
+ // When the registration occurs, we'll capture the handler and message so we can post our
+ // own messages to it.
+ ArgumentCaptor<Handler> handlerCaptor = ArgumentCaptor.forClass(Handler.class);
+ ArgumentCaptor<Integer> messageCaptor = ArgumentCaptor.forClass(Integer.class);
+ verify(phone).registerForSuppServiceNotification(handlerCaptor.capture(),
+ messageCaptor.capture(), any());
+ Handler handler = handlerCaptor.getValue();
+ int message = messageCaptor.getValue();
+
+ // With the handler and message now known, we'll post a supp service notification.
+ AsyncResult result = getSuppServiceNotification(
+ SuppServiceNotification.NOTIFICATION_TYPE_CODE_1,
+ SuppServiceNotification.CODE_1_CALL_FORWARDED);
+ handler.obtainMessage(message, result).sendToTarget();
+ waitForHandlerAction(handler, TIMEOUT_MS);
+
+ assertTrue(c.getLastConnectionEvents().contains(TelephonyManager.EVENT_CALL_FORWARDED));
+
+ // With the handler and message now known, we'll post a supp service notification.
+ result = getSuppServiceNotification(
+ SuppServiceNotification.NOTIFICATION_TYPE_CODE_1,
+ SuppServiceNotification.CODE_1_CALL_IS_WAITING);
+ handler.obtainMessage(message, result).sendToTarget();
+ waitForHandlerAction(handler, TIMEOUT_MS);
+
+ // We we want the 3rd event since the forwarding one above sends 2.
+ assertEquals(c.getLastConnectionEvents().get(2),
+ TelephonyManager.EVENT_SUPPLEMENTARY_SERVICE_NOTIFICATION);
+ Bundle extras = c.getLastConnectionEventExtras().get(2);
+ assertEquals(SuppServiceNotification.NOTIFICATION_TYPE_CODE_1,
+ extras.getInt(TelephonyManager.EXTRA_NOTIFICATION_TYPE));
+ assertEquals(SuppServiceNotification.CODE_1_CALL_IS_WAITING,
+ extras.getInt(TelephonyManager.EXTRA_NOTIFICATION_CODE));
+ }
+
+ private AsyncResult getSuppServiceNotification(int notificationType, int code) {
+ SuppServiceNotification notification = new SuppServiceNotification();
+ notification.notificationType = notificationType;
+ notification.code = code;
+ return new AsyncResult(null, notification, null);
+ }
+
private Phone makeTestPhone(int phoneId, int serviceState, boolean isEmergencyOnly) {
Phone phone = mock(Phone.class);
ServiceState testServiceState = new ServiceState();
@@ -792,7 +850,7 @@
private void setPhonesDialConnection(Phone phone, Connection c) {
try {
- when(phone.dial(anyString(), anyInt())).thenReturn(c);
+ when(phone.dial(anyString(), any())).thenReturn(c);
} catch (CallStateException e) {
// this shouldn't happen
fail();
diff --git a/tests/src/com/android/services/telephony/TestTelephonyConnection.java b/tests/src/com/android/services/telephony/TestTelephonyConnection.java
index ea0f965..9040257 100644
--- a/tests/src/com/android/services/telephony/TestTelephonyConnection.java
+++ b/tests/src/com/android/services/telephony/TestTelephonyConnection.java
@@ -16,17 +16,25 @@
package com.android.services.telephony;
+import android.content.Context;
+import android.os.Bundle;
import android.telecom.PhoneAccountHandle;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import com.android.internal.telephony.Call;
+import com.android.internal.telephony.Connection;
import com.android.internal.telephony.Phone;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.ArrayList;
+import java.util.List;
+
/**
* Mock Telephony Connection used in TelephonyConferenceController.java for testing purpose
*/
@@ -39,8 +47,13 @@
@Mock
Call mMockCall;
+ @Mock
+ Context mMockContext;
+
private Phone mMockPhone;
private int mNotifyPhoneAccountChangedCount = 0;
+ private List<String> mLastConnectionEvents = new ArrayList<>();
+ private List<Bundle> mLastConnectionEventExtras = new ArrayList<>();
@Override
public com.android.internal.telephony.Connection getOriginalConnection() {
@@ -52,11 +65,17 @@
MockitoAnnotations.initMocks(this);
mMockPhone = mock(Phone.class);
+ mMockContext = mock(Context.class);
// Set up mMockRadioConnection and mMockPhone to contain an active call
when(mMockRadioConnection.getState()).thenReturn(Call.State.ACTIVE);
when(mMockRadioConnection.getCall()).thenReturn(mMockCall);
+ doNothing().when(mMockRadioConnection).addListener(any(Connection.Listener.class));
+ doNothing().when(mMockRadioConnection).addPostDialListener(
+ any(Connection.PostDialListener.class));
when(mMockPhone.getRingingCall()).thenReturn(mMockCall);
+ when(mMockPhone.getContext()).thenReturn(null);
when(mMockCall.getState()).thenReturn(Call.State.ACTIVE);
+ when(mMockCall.getPhone()).thenReturn(mMockPhone);
}
@Override
@@ -82,7 +101,21 @@
mNotifyPhoneAccountChangedCount++;
}
+ @Override
+ public void sendConnectionEvent(String event, Bundle extras) {
+ mLastConnectionEvents.add(event);
+ mLastConnectionEventExtras.add(extras);
+ }
+
public int getNotifyPhoneAccountChangedCount() {
return mNotifyPhoneAccountChangedCount;
}
+
+ public List<String> getLastConnectionEvents() {
+ return mLastConnectionEvents;
+ }
+
+ public List<Bundle> getLastConnectionEventExtras() {
+ return mLastConnectionEventExtras;
+ }
}