Merge "Update touch to tap in display strings."
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index a75e46d..fa0a3d7 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -503,7 +503,12 @@
</service>
<!-- service to dump telephony information -->
- <service android:name="TelephonyDebugService" />
+ <service android:name="com.android.phone.TelephonyDebugService"
+ android:permission="android.permission.DUMP">
+ <intent-filter>
+ <action android:name="com.android.phone.TelephonyDebugService" />
+ </intent-filter>
+ </service>
<activity android:name="EmergencyCallbackModeExitDialog"
android:excludeFromRecents="true"
@@ -663,6 +668,13 @@
android:mimeType="vnd.android.cursor.item/voicemail" />
</intent-filter>
</receiver>
+ <receiver
+ android:name="com.android.phone.vvm.omtp.sync.OmtpVvmSyncReceiver"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.SYNC_VOICEMAIL"/>
+ </intent-filter>
+ </receiver>
<receiver
android:name="com.android.phone.vvm.omtp.sync.VoicemailProviderChangeReceiver"
android:exported="true">
diff --git a/res/values/strings.xml b/res/values/strings.xml
index a9e58ea..1f65c3d 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -983,6 +983,8 @@
<string name="incall_error_supp_service_reject">Can\'t reject call.</string>
<!-- In-call screen: message displayed in an error dialog -->
<string name="incall_error_supp_service_hangup">Can\'t release call(s).</string>
+ <!-- In-call screen: message displayed in an error dialog -->
+ <string name="incall_error_supp_service_hold">Can\'t hold calls.</string>
<!-- In-call screen: call failure message displayed in an error dialog when WFC is enabled, is wifi-only, and not connected to a wireless network. [CHAR_LIMIT=NONE] -->
<string name="incall_error_wfc_only_no_wireless_network">Connect to a wireless network to make a call.</string>
diff --git a/src/com/android/phone/CallNotifier.java b/src/com/android/phone/CallNotifier.java
index cfffb94..7836248 100644
--- a/src/com/android/phone/CallNotifier.java
+++ b/src/com/android/phone/CallNotifier.java
@@ -570,6 +570,9 @@
if (DBG) log("onSuppServiceFailed: displaying merge failure message");
mergeFailedString = mApplication.getResources().getString(
R.string.incall_error_supp_service_switch);
+ } else if (r.result == Phone.SuppService.HOLD) {
+ mergeFailedString = mApplication.getResources().getString(
+ R.string.incall_error_supp_service_hold);
}
PhoneDisplayMessage.displayErrorMessage(mApplication, mergeFailedString);
diff --git a/src/com/android/phone/EmergencyDialer.java b/src/com/android/phone/EmergencyDialer.java
index e8a6e8b..59d8de7 100644
--- a/src/com/android/phone/EmergencyDialer.java
+++ b/src/com/android/phone/EmergencyDialer.java
@@ -202,7 +202,7 @@
CarrierConfigManager configMgr =
(CarrierConfigManager) getSystemService(Context.CARRIER_CONFIG_SERVICE);
PersistableBundle carrierConfig =
- configMgr.getConfigForSubId(SubscriptionManager.getDefaultVoiceSubId());
+ configMgr.getConfigForSubId(SubscriptionManager.getDefaultVoiceSubscriptionId());
if (carrierConfig.getBoolean(CarrierConfigManager.KEY_SHOW_ONSCREEN_DIAL_BUTTON_BOOL)) {
mDialButton.setOnClickListener(this);
} else {
diff --git a/src/com/android/phone/PhoneGlobals.java b/src/com/android/phone/PhoneGlobals.java
index fd42f6f..026e798 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -438,7 +438,7 @@
}
public PersistableBundle getCarrierConfig() {
- return getCarrierConfigForSubId(SubscriptionManager.getDefaultSubId());
+ return getCarrierConfigForSubId(SubscriptionManager.getDefaultSubscriptionId());
}
public PersistableBundle getCarrierConfigForSubId(int subId) {
diff --git a/src/com/android/phone/PhoneInterfaceManager.java b/src/com/android/phone/PhoneInterfaceManager.java
index 4b22e57..63256b6 100644
--- a/src/com/android/phone/PhoneInterfaceManager.java
+++ b/src/com/android/phone/PhoneInterfaceManager.java
@@ -740,6 +740,10 @@
loge("queryModemActivityInfo: Unknown exception");
}
}
+ // Result cannot be null. Return ModemActivityInfo with all fields set to 0.
+ if (request.result == null) {
+ request.result = new ModemActivityInfo(0, 0, 0, null, 0, 0);
+ }
synchronized (request) {
request.notifyAll();
}
diff --git a/src/com/android/phone/TelephonyDebugService.java b/src/com/android/phone/TelephonyDebugService.java
index 04ebec2..8ec76a2 100644
--- a/src/com/android/phone/TelephonyDebugService.java
+++ b/src/com/android/phone/TelephonyDebugService.java
@@ -18,22 +18,20 @@
import com.android.internal.telephony.DebugService;
import com.android.internal.telephony.ITelephonyDebug;
-import com.android.internal.telephony.TelephonyEventLog;
+import com.android.internal.telephony.ITelephonyDebugSubscriber;
+import com.android.internal.telephony.TelephonyEvent;
import android.app.Service;
import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
+import android.os.RemoteException;
import android.util.Log;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Formatter;
import java.util.List;
-import java.util.Locale;
-
-import static com.android.internal.telephony.RILConstants.*;
/**
* A debug service for telephony.
@@ -44,52 +42,6 @@
private static final boolean VDBG = true;
private DebugService mDebugService = new DebugService();
- public static final String JSON_KEY_TAG = "tag";
- public static final String JSON_KEY_REG_STATE = "reg-state";
- public static final String JSON_KEY_DATA_REG_STATE = "data-reg-state";
- public static final String JSON_KEY_ROAMING_TYPE = "roaming-type";
- public static final String JSON_KEY_DATA_ROAMING_TYPE = "data-roaming-type";
- public static final String JSON_KEY_OPERATOR_ALPHA_LONG = "operator-alpha-long";
- public static final String JSON_KEY_OPERATOR_ALPHA_SHORT = "operator-alpha-short";
- public static final String JSON_KEY_OPERATOR_NUMERIC = "operator-numeric";
- public static final String JSON_KEY_DATA_OPERATOR_ALPHA_LONG = "data-operator-alpha-long";
- public static final String JSON_KEY_DATA_OPERATOR_ALPHA_SHORT = "data-operator-alpha-short";
- public static final String JSON_KEY_DATA_OPERATOR_NUMERIC = "data-operator-numeric";
- public static final String JSON_KEY_RAT = "rat";
- public static final String JSON_KEY_DATA_RAT = "data-rat";
- public static final String JSON_KEY_STATE = "state";
- public static final String JSON_KEY_REASON_INFO = "reason_info";
- public static final String JSON_KEY_REASON_INFO_CODE = "code";
- public static final String JSON_KEY_REASON_INFO_EXTRA_CODE = "extra_code";
- public static final String JSON_KEY_REASON_INFO_EXTRA_MESSAGE = "extra_message";
- public static final String JSON_KEY_VOLTE = "VoLTE";
- public static final String JSON_KEY_VILTE = "ViLTE";
- public static final String JSON_KEY_VOWIFI = "VoWiFi";
- public static final String JSON_KEY_VIWIFI = "ViWiFi";
- public static final String JSON_KEY_UTLTE = "UTLTE";
- public static final String JSON_KEY_UTWIFI = "UTWiFi";
- public static final String JSON_KEY_DATA_CALLS = "data-calls";
- public static final String JSON_KEY_STATUS = "status";
- public static final String JSON_KEY_CID = "cid";
- public static final String JSON_KEY_ACTIVE = "active";
- public static final String JSON_KEY_TYPE = "type";
- public static final String JSON_KEY_IFNAME = "ifname";
- public static final String JSON_KEY_SERIAL = "serial";
- public static final String JSON_KEY_PROFILE = "profile";
- public static final String JSON_KEY_APN = "apn";
- public static final String JSON_KEY_PROTOCOL = "protocol";
- public static final String JSON_KEY_REASON = "reason";
- public static final String JSON_KEY_CLIR_MODE = "clirMode";
- public static final String JSON_KEY_EVT = "evt";
- public static final String JSON_KEY_GSM_INDEX = "gsmIndex";
- public static final String JSON_KEY_RETRY = "retry";
- public static final String JSON_KEY_SMS_MESSAGE_REF = "messageRef";
- public static final String JSON_KEY_SMS_ERROR_CODE = "errorCode";
- public static final String JSON_KEY_RIL_ERROR = "error";
- public static final String JSON_KEY_CALL_ID = "call-id";
- public static final String JSON_KEY_SRC_TECH = "src-tech";
- public static final String JSON_KEY_TARGET_TECH = "target-tech";
-
/** Constructor */
public TelephonyDebugService() {
if (DBG) Log.d(TAG, "TelephonyDebugService()");
@@ -106,489 +58,82 @@
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- boolean dumpEvents = false;
- if (args != null) {
- for (String arg : args) {
- if ("--events".equals(arg)) {
- dumpEvents = true;
- } else if ("--reset-events".equals(arg)) {
- synchronized (mEvents) {
- mEvents.clear();
- }
- pw.println("TelephonyDebugService reset.");
- return;
- } else if ("-h".equals(arg)) {
- dumpHelp(pw);
- return;
- } else {
- pw.println("Unknown option: " + arg);
- dumpHelp(pw);
- return;
- }
- }
- }
-
- if (dumpEvents) {
- synchronized (mEvents) {
- pw.println("{\"version\": \"1.0\"," +
- "\"events\": [");
- for (Event e : mEvents) {
- pw.println(e.toJson());
- }
- pw.println("]}");
- }
- } else {
- mDebugService.dump(fd, pw, args);
- }
+ mDebugService.dump(fd, pw, args);
}
- private static void dumpHelp(PrintWriter pw) {
- pw.println("TelephonyDebugService dump options:");
- pw.println(" [--events] [--reset-events] [-h]");
- pw.println(" --events: dump events in JSON format.");
- pw.println(" --reset-events: reset the stats, clearing all current data.");
- pw.println(" -h: print this help text.");
- }
-
- class Event {
-
- public static final String JSON_TAG_SETTINGS = "SETTINGS";
- public static final String JSON_TAG_SERVICE_STATE = "SERVICE_STATE";
- public static final String JSON_TAG_IMS_CONNECTION_STATE = "IMS_CONNECTION_STATE";
- public static final String JSON_TAG_IMS_CAPABILITIES = "IMS_CAPABILITIES";
- public static final String JSON_TAG_DATA_CALL_LIST = "DATA_CALL_LIST";
- public static final String JSON_TAG_RIL_REQUEST_SETUP_DATA_CALL
- = "RIL_REQUEST_SETUP_DATA_CALL";
- public static final String JSON_TAG_RIL_REQUEST_DEACTIVATE_DATA_CALL
- = "RIL_REQUEST_DEACTIVATE_DATA_CALL";
- public static final String JSON_TAG_RIL_REQUEST_DIAL = "RIL_REQUEST_DIAL";
- public static final String JSON_TAG_RIL_REQUEST_HANGUP = "RIL_REQUEST_HANGUP";
- public static final String JSON_TAG_RIL_REQUEST_ANSWER = "RIL_REQUEST_ANSWER";
- public static final String JSON_TAG_RIL_REQUEST_SEND_SMS = "RIL_REQUEST_SEND_SMS";
- public static final String JSON_TAG_RIL_RESPONSE_SETUP_DATA_CALL
- = "RIL_RESPONSE_SETUP_DATA_CALL";
- public static final String JSON_TAG_RIL_UNSOL_CALL_RING = "RIL_UNSOL_CALL_RING";
- public static final String JSON_TAG_RIL_UNSOL_SRVCC_STATE_NOTIFY
- = "RIL_UNSOL_SRVCC_STATE_NOTIFY";
- public static final String JSON_TAG_RIL_UNSOL_RESPONSE_NEW_SMS
- = "RIL_UNSOL_RESPONSE_NEW_SMS";
- public static final String JSON_TAG_RIL_UNSOL_RESPONSE_CDMA_NEW_SMS
- = "RIL_UNSOL_RESPONSE_CDMA_NEW_SMS";
- public static final String JSON_TAG_IMS_CALL = "IMS_CALL";
- public static final String JSON_TAG_IMS_CALL_HANDOVER = "IMS_CALL_HANDOVER";
- public static final String JSON_TAG_IMS_CALL_STATE = "IMS_CALL_STATE";
- public static final String JSON_TAG_PHONE_STATE = "PHONE_STATE";
- public static final String JSON_TAG_SMS = "SMS";
-
- public long timestamp;
- public int phoneId;
- public int tag;
- public int param1;
- public int param2;
- public Bundle data;
-
- public Event(long timestamp, int phoneId, int tag, int param1, int param2, Bundle data) {
- this.timestamp = timestamp;
- this.phoneId = phoneId;
- this.tag = tag;
- this.param1 = param1;
- this.param2 = param2;
- this.data = data;
- }
-
- public String imsCallEventToString(int evt) {
- switch (evt) {
- case TelephonyEventLog.TAG_IMS_CALL_START: return "START";
- case TelephonyEventLog.TAG_IMS_CALL_START_CONFERENCE: return "START_CONFERENCE";
- case TelephonyEventLog.TAG_IMS_CALL_RECEIVE: return "RECEIVE";
- case TelephonyEventLog.TAG_IMS_CALL_ACCEPT: return "ACCEPT";
- case TelephonyEventLog.TAG_IMS_CALL_REJECT: return "REJECT";
- case TelephonyEventLog.TAG_IMS_CALL_TERMINATE: return "TERMINATE";
- case TelephonyEventLog.TAG_IMS_CALL_HOLD: return "HOLD";
- case TelephonyEventLog.TAG_IMS_CALL_RESUME: return "RESUME";
- case TelephonyEventLog.TAG_IMS_CALL_MERGE: return "MERGE";
- case TelephonyEventLog.TAG_IMS_CALL_UPDATE: return "UPDATE";
- case TelephonyEventLog.TAG_IMS_CALL_PROGRESSING: return "PROGRESSING";
- case TelephonyEventLog.TAG_IMS_CALL_STARTED: return "STARTED";
- case TelephonyEventLog.TAG_IMS_CALL_START_FAILED: return "START_FAILED";
- case TelephonyEventLog.TAG_IMS_CALL_TERMINATED: return "TERMINATED";
- case TelephonyEventLog.TAG_IMS_CALL_HELD: return "HELD";
- case TelephonyEventLog.TAG_IMS_CALL_HOLD_FAILED: return "HOLD_FAILED";
- case TelephonyEventLog.TAG_IMS_CALL_HOLD_RECEIVED: return "HOLD_RECEIVED";
- case TelephonyEventLog.TAG_IMS_CALL_RESUMED: return "RESUMED";
- case TelephonyEventLog.TAG_IMS_CALL_RESUME_FAILED: return "RESUME_FAILED";
- case TelephonyEventLog.TAG_IMS_CALL_RESUME_RECEIVED: return "RESUME_RECEIVED";
- case TelephonyEventLog.TAG_IMS_CALL_UPDATED: return "UPDATED";
- case TelephonyEventLog.TAG_IMS_CALL_UPDATE_FAILED: return "UPDATE_FAILED";
- case TelephonyEventLog.TAG_IMS_CALL_MERGED: return "MERGED";
- case TelephonyEventLog.TAG_IMS_CALL_MERGE_FAILED: return "MERGE_FAILED";
- case TelephonyEventLog.TAG_IMS_CALL_HANDOVER: return "HANDOVER";
- case TelephonyEventLog.TAG_IMS_CALL_HANDOVER_FAILED: return "HANDOVER_FAILED";
- case TelephonyEventLog.TAG_IMS_CALL_TTY_MODE_RECEIVED: return "TTY_MODE_RECEIVED";
- case TelephonyEventLog.TAG_IMS_CONFERENCE_PARTICIPANTS_STATE_CHANGED:
- return "CONFERENCE_PARTICIPANTS_STATE_CHANGED";
- case TelephonyEventLog.TAG_IMS_MULTIPARTY_STATE_CHANGED:
- return "MULTIPARTY_STATE_CHANGED";
- case TelephonyEventLog.TAG_IMS_CALL_STATE: return "STATE";
- }
- return "UNKNOWN("+evt+")";
- }
-
- public String rilResponseToString(int evt) {
- switch (evt) {
- case RIL_REQUEST_DEACTIVATE_DATA_CALL: return "RIL_RESPONSE_DEACTIVATE_DATA_CALL";
- case RIL_REQUEST_HANGUP: return "RIL_RESPONSE_HANGUP";
- case RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND: return "RIL_RESPONSE_HANGUP_WAITING_OR_BACKGROUND";
- case RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND: return "RIL_RESPONSE_HANGUP_FOREGROUND_RESUME_BACKGROUND";
- case RIL_REQUEST_DIAL: return "RIL_RESPONSE_DIAL";
- case RIL_REQUEST_ANSWER: return "RIL_RESPONSE_ANSWER";
- case RIL_REQUEST_SEND_SMS: return "RIL_RESPONSE_SEND_SMS";
- case RIL_REQUEST_SEND_SMS_EXPECT_MORE: return "RIL_RESPONSE_SEND_SMS_EXPECT_MORE";
- case RIL_REQUEST_CDMA_SEND_SMS: return "RIL_RESPONSE_CDMA_SEND_SMS";
- case RIL_REQUEST_IMS_SEND_SMS: return "RIL_RESPONSE_IMS_SEND_SMS";
- }
- return "UNKNOWN("+evt+")";
- }
-
- public String toString() {
- return String.format("%d,%d,%d,%d,%d,%s",
- timestamp, phoneId, tag, param1, param2, data);
- }
-
- public String toJson() {
- StringBuilder sb = new StringBuilder();
- Formatter formatter = new Formatter(sb, Locale.US);
- formatter.format("{\"ts\":%d, \"phone\":%d", timestamp, phoneId);
- switch (tag) {
- case TelephonyEventLog.TAG_SETTINGS:
- formatter.format(", \"%s\":\"%s\"", JSON_KEY_TAG, JSON_TAG_SETTINGS);
- break;
-
- case TelephonyEventLog.TAG_SERVICE_STATE:
- serviceStateToJson(formatter);
- break;
-
- case TelephonyEventLog.TAG_IMS_CONNECTION_STATE:
- imsConnectionStateToJson(formatter);
- break;
-
- case TelephonyEventLog.TAG_IMS_CAPABILITIES:
- imsCapabilitiesToJson(formatter);
- break;
-
- case TelephonyEventLog.TAG_DATA_CALL_LIST:
- dataCallListToJson(sb, formatter);
- break;
-
- case TelephonyEventLog.TAG_RIL_REQUEST:
- rilRequestToJson(formatter);
- break;
-
- case TelephonyEventLog.TAG_RIL_RESPONSE:
- rilResponseToJson(formatter);
- break;
-
- case TelephonyEventLog.TAG_RIL_UNSOL_RESPONSE:
- unsolRilResponseToJson(formatter);
- break;
-
- case TelephonyEventLog.TAG_IMS_CALL_START:
- case TelephonyEventLog.TAG_IMS_CALL_START_CONFERENCE:
- case TelephonyEventLog.TAG_IMS_CALL_RECEIVE:
- case TelephonyEventLog.TAG_IMS_CALL_ACCEPT:
- case TelephonyEventLog.TAG_IMS_CALL_REJECT:
- case TelephonyEventLog.TAG_IMS_CALL_TERMINATE:
- case TelephonyEventLog.TAG_IMS_CALL_HOLD:
- case TelephonyEventLog.TAG_IMS_CALL_RESUME:
- case TelephonyEventLog.TAG_IMS_CALL_PROGRESSING:
- case TelephonyEventLog.TAG_IMS_CALL_STARTED:
- case TelephonyEventLog.TAG_IMS_CALL_START_FAILED:
- case TelephonyEventLog.TAG_IMS_CALL_TERMINATED:
- case TelephonyEventLog.TAG_IMS_CALL_HELD:
- case TelephonyEventLog.TAG_IMS_CALL_HOLD_RECEIVED:
- case TelephonyEventLog.TAG_IMS_CALL_HOLD_FAILED:
- case TelephonyEventLog.TAG_IMS_CALL_RESUMED:
- case TelephonyEventLog.TAG_IMS_CALL_RESUME_RECEIVED:
- case TelephonyEventLog.TAG_IMS_CALL_RESUME_FAILED:
- imsCallEventToJson(formatter);
- break;
-
- case TelephonyEventLog.TAG_IMS_CALL_HANDOVER:
- case TelephonyEventLog.TAG_IMS_CALL_HANDOVER_FAILED:
- imsHandoverToJson(formatter);
- break;
-
- case TelephonyEventLog.TAG_IMS_CALL_STATE:
- imsCallStateToJson(formatter);
- break;
-
- case TelephonyEventLog.TAG_PHONE_STATE:
- phoneStateToJson(formatter);
- break;
-
- case TelephonyEventLog.TAG_SMS:
- formatter.format(", \"%s\":\"%s\"", JSON_KEY_TAG, JSON_TAG_SMS);
- break;
-
- default:
- formatter.format(", \"%s\":\"UNKNOWN(%d)\"", JSON_KEY_TAG, tag);
- break;
- }
- sb.append("},");
- return sb.toString();
- }
-
- private void serviceStateToJson(Formatter formatter) {
- formatter.format(", \"%s\":\"%s\""
- + ",\"%s\":%d,\"%s\":%d,\"%s\":%d,\"%s\":%d"
- + ",\"%s\":\"%s\",\"%s\":\"%s\",\"%s\":\"%s\""
- + ",\"%s\":\"%s\",\"%s\":\"%s\",\"%s\":\"%s\""
- + ",\"%s\":%d,\"%s\":%d",
- JSON_KEY_TAG, JSON_TAG_SERVICE_STATE,
- JSON_KEY_REG_STATE, data.getInt("voiceRegState"),
- JSON_KEY_DATA_REG_STATE, data.getInt("dataRegState"),
- JSON_KEY_ROAMING_TYPE, data.getInt("voiceRoamingType"),
- JSON_KEY_DATA_ROAMING_TYPE, data.getInt("dataRoamingType"),
- JSON_KEY_OPERATOR_ALPHA_LONG, data.getString("operator-alpha-long"),
- JSON_KEY_OPERATOR_ALPHA_SHORT, data.getString("operator-alpha-short"),
- JSON_KEY_OPERATOR_NUMERIC, data.getString("operator-numeric"),
- JSON_KEY_DATA_OPERATOR_ALPHA_LONG, data.getString("data-operator-alpha-long"),
- JSON_KEY_DATA_OPERATOR_ALPHA_SHORT, data.getString("data-operator-alpha-short"),
- JSON_KEY_DATA_OPERATOR_NUMERIC, data.getString("data-operator-numeric"),
- JSON_KEY_RAT, data.getInt("radioTechnology"),
- JSON_KEY_DATA_RAT, data.getInt("dataRadioTechnology"));
- }
-
- private void imsConnectionStateToJson(Formatter formatter) {
- if (data == null) {
- formatter.format(", \"%s\":\"%s\", \"%s\":%d",
- JSON_KEY_TAG, JSON_TAG_IMS_CONNECTION_STATE, JSON_KEY_STATE, param1);
- } else {
- formatter.format(", \"%s\":\"%s\""
- + ", \"%s\":%d"
- + ", \"%s\":{\"%s\":%d,\"%s\":%d,\"%s\":%s}",
- JSON_KEY_TAG, JSON_TAG_IMS_CONNECTION_STATE,
- JSON_KEY_STATE, param1,
- JSON_KEY_REASON_INFO,
- JSON_KEY_REASON_INFO_CODE, data.getInt(
- TelephonyEventLog.DATA_KEY_REASONINFO_CODE),
- JSON_KEY_REASON_INFO_EXTRA_CODE, data.getInt(
- TelephonyEventLog.DATA_KEY_REASONINFO_EXTRA_CODE),
- JSON_KEY_REASON_INFO_EXTRA_MESSAGE, data.getString(
- TelephonyEventLog.DATA_KEY_REASONINFO_EXTRA_MESSAGE));
- }
- }
-
- private void imsCapabilitiesToJson(Formatter formatter) {
- formatter.format(", \"%s\":\"%s\""
- + ",\"%s\":%b,\"%s\":%b,\"%s\":%b"
- + ",\"%s\":%b,\"%s\":%b,\"%s\":%b",
- JSON_KEY_TAG, JSON_TAG_IMS_CAPABILITIES,
- JSON_KEY_VOLTE, data.getBoolean(TelephonyEventLog.DATA_KEY_VOLTE),
- JSON_KEY_VILTE, data.getBoolean(TelephonyEventLog.DATA_KEY_VILTE),
- JSON_KEY_VOWIFI, data.getBoolean(TelephonyEventLog.DATA_KEY_VOWIFI),
- JSON_KEY_VIWIFI, data.getBoolean(TelephonyEventLog.DATA_KEY_VIWIFI),
- JSON_KEY_UTLTE, data.getBoolean(TelephonyEventLog.DATA_KEY_UTLTE),
- JSON_KEY_UTWIFI, data.getBoolean(TelephonyEventLog.DATA_KEY_UTWIFI));
- }
-
- private void dataCallListToJson(StringBuilder sb, Formatter formatter) {
- formatter.format(", \"%s\":\"%s\",\"%s\":[",
- JSON_KEY_TAG, JSON_TAG_DATA_CALL_LIST, JSON_KEY_DATA_CALLS);
- int[] statuses = data.getIntArray(TelephonyEventLog.DATA_KEY_DATA_CALL_STATUSES);
- int[] cids = data.getIntArray(TelephonyEventLog.DATA_KEY_DATA_CALL_CIDS);
- int[] actives = data.getIntArray(TelephonyEventLog.DATA_KEY_DATA_CALL_ACTIVES);
- String[] types = data.getStringArray(TelephonyEventLog.DATA_KEY_DATA_CALL_TYPES);
- String[] ifnames = data.getStringArray(TelephonyEventLog.DATA_KEY_DATA_CALL_IFNAMES);
- for (int i = 0; i < cids.length; i++) {
- formatter.format("{\"%s\":%d,\"%s\":%d,\"%s\":%d"
- + ",\"%s\":\"%s\",\"%s\":\"%s\"},",
- JSON_KEY_STATUS, statuses[i], JSON_KEY_CID, cids[i],
- JSON_KEY_ACTIVE, actives[i],
- JSON_KEY_TYPE, types[i], JSON_KEY_IFNAME, ifnames[i]);
- }
- sb.append("]");
- }
-
- private void rilRequestToJson(Formatter formatter) {
- switch (param1) {
- case RIL_REQUEST_SETUP_DATA_CALL:
- formatter.format(", \"%s\":\"%s\""
- + ",\"%s\":%d,\"%s\":\"%s\",\"%s\":\"%s\""
- + ",\"%s\":\"%s\",\"%s\":\"%s\"",
- JSON_KEY_TAG, JSON_TAG_RIL_REQUEST_SETUP_DATA_CALL,
- JSON_KEY_SERIAL, param2,
- JSON_KEY_RAT, data.getString(
- TelephonyEventLog.DATA_KEY_RAT),
- JSON_KEY_PROFILE, data.getString(
- TelephonyEventLog.DATA_KEY_DATA_PROFILE),
- JSON_KEY_APN, data.getString(
- TelephonyEventLog.DATA_KEY_APN),
- JSON_KEY_PROTOCOL, data.getString(
- TelephonyEventLog.DATA_KEY_PROTOCOL));
- break;
- case RIL_REQUEST_DEACTIVATE_DATA_CALL:
- formatter.format(", \"%s\":\"%s\""
- + ",\"%s\":%d,\"%s\":%d,\"%s\":%d",
- JSON_KEY_TAG, JSON_TAG_RIL_REQUEST_DEACTIVATE_DATA_CALL,
- JSON_KEY_SERIAL, param2,
- JSON_KEY_CID, data.getInt(
- TelephonyEventLog.DATA_KEY_DATA_CALL_CID),
- JSON_KEY_REASON, data.getInt(
- TelephonyEventLog.DATA_KEY_DATA_DEACTIVATE_REASON));
- break;
- case RIL_REQUEST_DIAL:
- formatter.format(", \"%s\":\"%s\""
- + ",\"%s\":%d,\"%s\":%d",
- JSON_KEY_TAG, JSON_TAG_RIL_REQUEST_DIAL,
- JSON_KEY_SERIAL, param2,
- JSON_KEY_CLIR_MODE, data.getInt(
- TelephonyEventLog.DATA_KEY_CLIR_MODE));
- break;
- case RIL_REQUEST_HANGUP:
- case RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND:
- case RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND:
- formatter.format(", \"%s\":\"%s\""
- + ",\"%s\":%d, \"%s\":%d"
- + ",\"%s\":\"%d\"",
- JSON_KEY_TAG, JSON_TAG_RIL_REQUEST_HANGUP,
- JSON_KEY_SERIAL, param2, JSON_KEY_EVT, param1,
- JSON_KEY_GSM_INDEX, data.getInt(
- TelephonyEventLog.DATA_KEY_RIL_HANGUP_GSM_INDEX));
- break;
- case RIL_REQUEST_ANSWER:
- formatter.format(", \"%s\":\"%s\",\"%s\":%d",
- JSON_KEY_TAG, JSON_TAG_RIL_REQUEST_ANSWER, JSON_KEY_SERIAL, param2);
- break;
-
- case RIL_REQUEST_SEND_SMS:
- case RIL_REQUEST_SEND_SMS_EXPECT_MORE:
- case RIL_REQUEST_CDMA_SEND_SMS:
- case RIL_REQUEST_IMS_SEND_SMS:
- formatter.format(", \"%s\":\"%s\",\"%s\":%d",
- JSON_KEY_TAG, JSON_TAG_RIL_REQUEST_SEND_SMS, JSON_KEY_SERIAL, param2);
- break;
- }
- }
-
- private void rilResponseToJson(Formatter formatter) {
- switch (param1) {
- case RIL_REQUEST_SETUP_DATA_CALL:
- formatter.format(", \"%s\":\"%s\""
- + ",\"%s\":%d,\"%s\":%d,\"%s\":%d"
- + ",\"%s\":%d,\"%s\":%d"
- + ",\"%s\":\"%s\",\"%s\":\"%s\"",
- JSON_KEY_TAG, JSON_TAG_RIL_RESPONSE_SETUP_DATA_CALL,
- JSON_KEY_SERIAL, param2,
- JSON_KEY_STATUS, data.getInt(
- TelephonyEventLog.DATA_KEY_DATA_CALL_STATUS),
- JSON_KEY_RETRY, data.getInt(
- TelephonyEventLog.DATA_KEY_DATA_CALL_RETRY),
- JSON_KEY_CID, data.getInt(
- TelephonyEventLog.DATA_KEY_DATA_CALL_CID),
- JSON_KEY_ACTIVE, data.getInt(
- TelephonyEventLog.DATA_KEY_DATA_CALL_ACTIVE),
- JSON_KEY_TYPE, data.getString(
- TelephonyEventLog.DATA_KEY_DATA_CALL_TYPE),
- JSON_KEY_IFNAME, data.getString(
- TelephonyEventLog.DATA_KEY_DATA_CALL_IFNAME));
- break;
-
- case RIL_REQUEST_DEACTIVATE_DATA_CALL:
- case RIL_REQUEST_HANGUP:
- case RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND:
- case RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND:
- case RIL_REQUEST_DIAL:
- case RIL_REQUEST_ANSWER:
- formatter.format(", \"%s\":\"%s\",\"%s\":%d",
- JSON_KEY_TAG, rilResponseToString(param1), JSON_KEY_SERIAL, param2);
- break;
-
- case RIL_REQUEST_SEND_SMS:
- case RIL_REQUEST_SEND_SMS_EXPECT_MORE:
- case RIL_REQUEST_CDMA_SEND_SMS:
- case RIL_REQUEST_IMS_SEND_SMS:
- formatter.format(", \"%s\":\"%s\",\"%s\":%d"
- + ",\"%s\":%d,\"%s\":%d",
- JSON_KEY_TAG, rilResponseToString(param1), JSON_KEY_SERIAL, param2,
- JSON_KEY_SMS_MESSAGE_REF, data.getInt(
- TelephonyEventLog.DATA_KEY_SMS_MESSAGE_REF),
- JSON_KEY_SMS_ERROR_CODE, data.getInt(
- TelephonyEventLog.DATA_KEY_SMS_ERROR_CODE));
- break;
- }
- formatter.format(", \"%s\":%d",
- JSON_KEY_RIL_ERROR, data.getInt(TelephonyEventLog.DATA_KEY_RIL_ERROR));
- }
-
- private void unsolRilResponseToJson(Formatter formatter) {
- switch (param1) {
- case RIL_UNSOL_CALL_RING:
- formatter.format(", \"%s\":\"%s\"", JSON_KEY_TAG, JSON_TAG_RIL_UNSOL_CALL_RING);
- break;
- case RIL_UNSOL_SRVCC_STATE_NOTIFY:
- formatter.format(", \"%s\":\"%s\",\"%s\":%d",
- JSON_KEY_TAG, JSON_TAG_RIL_UNSOL_SRVCC_STATE_NOTIFY,
- JSON_KEY_STATE, param2);
- break;
- case RIL_UNSOL_RESPONSE_NEW_SMS:
- formatter.format(", \"%s\":\"%s\"",
- JSON_KEY_TAG, JSON_TAG_RIL_UNSOL_RESPONSE_NEW_SMS);
- break;
- case RIL_UNSOL_RESPONSE_CDMA_NEW_SMS:
- formatter.format(", \"%s\":\"%s\"",
- JSON_KEY_TAG, JSON_TAG_RIL_UNSOL_RESPONSE_CDMA_NEW_SMS);
- break;
- }
- }
-
- private void imsCallEventToJson(Formatter formatter) {
- formatter.format(", \"%s\":\"%s\", \"%s\":\"%s\",\"%s\":%d",
- JSON_KEY_TAG, JSON_TAG_IMS_CALL, JSON_KEY_EVT, imsCallEventToString(tag),
- JSON_KEY_CALL_ID, param1);
- }
-
- private void imsHandoverToJson(Formatter formatter) {
- formatter.format(", \"%s\":\"%s\", \"%s\":\"%s\",\"%s\":%d"
- + ",\"%s\":%d,\"%s\":%d"
- + ",\"%s\":%d,\"%s\":%d,\"%s\":\"%s\"",
- JSON_KEY_TAG, JSON_TAG_IMS_CALL_HANDOVER,
- JSON_KEY_EVT, imsCallEventToString(tag), JSON_KEY_CALL_ID, param1,
- JSON_KEY_SRC_TECH, data.getInt(TelephonyEventLog.DATA_KEY_SRC_TECH),
- JSON_KEY_TARGET_TECH, data.getInt(TelephonyEventLog.DATA_KEY_TARGET_TECH),
- JSON_KEY_REASON_INFO_CODE, data.getInt(
- TelephonyEventLog.DATA_KEY_REASONINFO_CODE),
- JSON_KEY_REASON_INFO_EXTRA_CODE, data.getInt(
- TelephonyEventLog.DATA_KEY_REASONINFO_EXTRA_CODE),
- JSON_KEY_REASON_INFO_EXTRA_MESSAGE, data.getString(
- TelephonyEventLog.DATA_KEY_REASONINFO_EXTRA_MESSAGE));
- }
-
- private void imsCallStateToJson(Formatter formatter) {
- formatter.format(", \"%s\":\"%s\", \"%s\":%d, \"%s\":%d",
- JSON_KEY_TAG, JSON_TAG_IMS_CALL_STATE, JSON_KEY_CALL_ID, param1,
- JSON_KEY_STATE, param2);
- }
-
- private void phoneStateToJson(Formatter formatter) {
- formatter.format(", \"%s\":\"%s\", \"%s\":%d",
- JSON_KEY_TAG, JSON_TAG_PHONE_STATE, JSON_KEY_STATE, param1);
- }
- }
- private final List<Event> mEvents = new ArrayList<Event>();
+ private final int MAX_NUMBER_OF_EVENTS = 100;
+ private final int MIN_TIME_OFFSET = 900000; // 15 minutes
+ private final List<TelephonyEvent> mEvents = new ArrayList<TelephonyEvent>();
+ private long mLastSentEventTimeMillis = System.currentTimeMillis();
/**
* Implementation of the ITelephonyDebug interface.
*/
private final ITelephonyDebug.Stub mBinder = new ITelephonyDebug.Stub() {
+
+ private final List<ITelephonyDebugSubscriber> mSubscribers = new ArrayList<>();
+
public void writeEvent(long timestamp, int phoneId, int tag,
int param1, int param2, Bundle data) {
+ final TelephonyEvent ev = new TelephonyEvent(timestamp, phoneId, tag,
+ param1, param2, data);
+ TelephonyEvent[] events = null;
+
if (VDBG) {
- Log.v(TAG, String.format("writeEvent(%d, %d, %d, %d, %d)",
- timestamp, phoneId, tag, param1, param2));
+ Log.v(TAG, "writeEvent(" + ev.toString() + ")");
}
+
synchronized (mEvents) {
- mEvents.add(new Event(timestamp, phoneId, tag, param1, param2, data));
+ mEvents.add(ev);
+
+ final long currentTimeMillis = System.currentTimeMillis();
+ final long timeOffset = currentTimeMillis - mLastSentEventTimeMillis;
+ if (timeOffset > MIN_TIME_OFFSET
+ || timeOffset < 0 // system time has changed
+ || mEvents.size() >= MAX_NUMBER_OF_EVENTS) {
+ // batch events
+ mLastSentEventTimeMillis = currentTimeMillis;
+ events = new TelephonyEvent[mEvents.size()];
+ mEvents.toArray(events);
+ mEvents.clear();
+ }
+ }
+
+ if (events != null) {
+ synchronized (mSubscribers) {
+ for (ITelephonyDebugSubscriber s : mSubscribers) {
+ try {
+ s.onEvents(events);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "RemoteException " + ex);
+ }
+ }
+ }
+ }
+ }
+
+ public void subscribe(ITelephonyDebugSubscriber subscriber) {
+ if (VDBG) Log.v(TAG, "subscribe");
+ synchronized (mSubscribers) {
+ mSubscribers.add(subscriber);
+ }
+
+ synchronized (mEvents) {
+ try {
+ // send cached events
+ TelephonyEvent[] events = new TelephonyEvent[mEvents.size()];
+ mEvents.toArray(events);
+ subscriber.onEvents(events);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "RemoteException " + ex);
+ }
+ }
+ }
+
+ public void unsubscribe(ITelephonyDebugSubscriber subscriber) {
+ if (VDBG) Log.v(TAG, "unsubscribe");
+ synchronized (mSubscribers) {
+ mSubscribers.remove(subscriber);
}
}
};
diff --git a/src/com/android/phone/VoicemailUtils.java b/src/com/android/phone/VoicemailUtils.java
new file mode 100644
index 0000000..f67c64b
--- /dev/null
+++ b/src/com/android/phone/VoicemailUtils.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2016 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.content.Context;
+import android.provider.VoicemailContract;
+import android.provider.VoicemailContract.Status;
+import android.telecom.PhoneAccountHandle;
+
+public class VoicemailUtils {
+
+ public static void setConfigurationState(Context context, PhoneAccountHandle accountHandle,
+ int configurationState) {
+ VoicemailContract.Status.setStatus(context, accountHandle,
+ configurationState,
+ Status.DATA_CHANNEL_STATE_IGNORE,
+ Status.NOTIFICATION_CHANNEL_STATE_IGNORE);
+ }
+
+ public static void setDataChannelState(Context context, PhoneAccountHandle accountHandle,
+ int dataChannelState) {
+ VoicemailContract.Status.setStatus(context, accountHandle,
+ Status.CONFIGURATION_STATE_IGNORE,
+ dataChannelState,
+ Status.NOTIFICATION_CHANNEL_STATE_IGNORE);
+ }
+
+ public static void setNotificationChannelState(Context context,
+ PhoneAccountHandle accountHandle, int notificationChannelState) {
+ VoicemailContract.Status.setStatus(context, accountHandle,
+ Status.CONFIGURATION_STATE_IGNORE,
+ Status.DATA_CHANNEL_STATE_IGNORE,
+ notificationChannelState);
+ }
+}
diff --git a/src/com/android/phone/common/mail/MailTransport.java b/src/com/android/phone/common/mail/MailTransport.java
index 172d1a9..f452bab 100644
--- a/src/com/android/phone/common/mail/MailTransport.java
+++ b/src/com/android/phone/common/mail/MailTransport.java
@@ -17,10 +17,21 @@
import android.content.Context;
import android.net.Network;
+import android.provider.VoicemailContract.Status;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.phone.common.mail.store.ImapStore;
import com.android.phone.common.mail.utils.LogUtils;
+import com.android.phone.vvm.omtp.imap.ImapHelper;
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
import java.net.SocketAddress;
import java.util.ArrayList;
import java.util.List;
@@ -32,15 +43,6 @@
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.Socket;
-
/**
* Make connection and perform operations on mail server by reading and writing lines.
*/
@@ -54,17 +56,21 @@
private static final HostnameVerifier HOSTNAME_VERIFIER =
HttpsURLConnection.getDefaultHostnameVerifier();
- private Context mContext;
- private Network mNetwork;
- private String mHost;
- private int mPort;
+ private final Context mContext;
+ private final ImapHelper mImapHelper;
+ private final Network mNetwork;
+ private final String mHost;
+ private final int mPort;
private Socket mSocket;
private BufferedInputStream mIn;
private BufferedOutputStream mOut;
- private int mFlags;
+ private final int mFlags;
+ private SocketCreator mSocketCreator;
- public MailTransport(Context context, Network network, String address, int port, int flags) {
+ public MailTransport(Context context, ImapHelper imapHelper, Network network, String address,
+ int port, int flags) {
mContext = context;
+ mImapHelper = imapHelper;
mNetwork = network;
mHost = address;
mPort = port;
@@ -77,7 +83,7 @@
*/
@Override
public MailTransport clone() {
- return new MailTransport(mContext, mNetwork, mHost, mPort, mFlags);
+ return new MailTransport(mContext, mImapHelper, mNetwork, mHost, mPort, mFlags);
}
public boolean canTrySslSecurity() {
@@ -92,55 +98,112 @@
* Attempts to open a connection using the Uri supplied for connection parameters. Will attempt
* an SSL connection if indicated.
*/
- public void open() throws MessagingException, CertificateValidationException {
+ public void open() throws MessagingException {
LogUtils.d(TAG, "*** IMAP open " + mHost + ":" + String.valueOf(mPort));
- List<SocketAddress> socketAddresses = new ArrayList<SocketAddress>();
- try {
- if (canTrySslSecurity()) {
- mSocket = HttpsURLConnection.getDefaultSSLSocketFactory().createSocket();
- socketAddresses.add(new InetSocketAddress(mHost, mPort));
- } else {
- if (mNetwork == null) {
- mSocket = new Socket();
- socketAddresses.add(new InetSocketAddress(mHost, mPort));
- } else {
- InetAddress[] inetAddresses = mNetwork.getAllByName(mHost);
- for (int i = 0; i < inetAddresses.length; i++) {
- socketAddresses.add(new InetSocketAddress(inetAddresses[i], mPort));
- }
- mSocket = mNetwork.getSocketFactory().createSocket();
+ List<InetSocketAddress> socketAddresses = new ArrayList<InetSocketAddress>();
+
+ if (mNetwork == null) {
+ socketAddresses.add(new InetSocketAddress(mHost, mPort));
+ } else {
+ try {
+ InetAddress[] inetAddresses = mNetwork.getAllByName(mHost);
+ if (inetAddresses.length == 0) {
+ throw new MessagingException(MessagingException.IOERROR,
+ "Host name " + mHost + "cannot be resolved on designated network");
}
+ for (int i = 0; i < inetAddresses.length; i++) {
+ socketAddresses.add(new InetSocketAddress(inetAddresses[i], mPort));
+ }
+ } catch (IOException ioe) {
+ LogUtils.d(TAG, ioe.toString());
+ mImapHelper.setDataChannelState(Status.DATA_CHANNEL_STATE_SERVER_CONNECTION_ERROR);
+ throw new MessagingException(MessagingException.IOERROR, ioe.toString());
}
- } catch (IOException ioe) {
- LogUtils.d(TAG, ioe.toString());
- throw new MessagingException(MessagingException.IOERROR, ioe.toString());
}
+ boolean success = false;
while (socketAddresses.size() > 0) {
+ mSocket = createSocket();
try {
- mSocket.connect(socketAddresses.remove(0), SOCKET_CONNECT_TIMEOUT);
+ InetSocketAddress address = socketAddresses.remove(0);
+ mSocket.connect(address, SOCKET_CONNECT_TIMEOUT);
- // After the socket connects to an SSL server, confirm that the hostname is as
- // expected
- if (canTrySslSecurity() && !canTrustAllCertificates()) {
- verifyHostname(mSocket, mHost);
+ if (canTrySslSecurity()) {
+ /**
+ * {@link SSLSocket} must connect in its constructor, or create through a
+ * already connected socket. Since we need to use
+ * {@link Socket#connect(SocketAddress, int) } to set timeout, we can only
+ * create it here.
+ */
+ LogUtils.d(TAG, "open: converting to SSL socket");
+ mSocket = HttpsURLConnection.getDefaultSSLSocketFactory()
+ .createSocket(mSocket, address.getHostName(), address.getPort(), true);
+ // After the socket connects to an SSL server, confirm that the hostname is as
+ // expected
+ if (!canTrustAllCertificates()) {
+ verifyHostname(mSocket, mHost);
+ }
}
mIn = new BufferedInputStream(mSocket.getInputStream(), 1024);
mOut = new BufferedOutputStream(mSocket.getOutputStream(), 512);
mSocket.setSoTimeout(SOCKET_READ_TIMEOUT);
+ success = true;
return;
} catch (IOException ioe) {
LogUtils.d(TAG, ioe.toString());
if (socketAddresses.size() == 0) {
// Only throw an error when there are no more sockets to try.
+ mImapHelper
+ .setDataChannelState(Status.DATA_CHANNEL_STATE_SERVER_CONNECTION_ERROR);
throw new MessagingException(MessagingException.IOERROR, ioe.toString());
}
+ } finally {
+ if (!success) {
+ try {
+ mSocket.close();
+ mSocket = null;
+ } catch (IOException ioe) {
+ throw new MessagingException(MessagingException.IOERROR, ioe.toString());
+ }
+
+ }
}
}
}
+ // For testing. We need something that can replace the behavior of "new Socket()"
+ @VisibleForTesting
+ interface SocketCreator {
+
+ Socket createSocket() throws MessagingException;
+ }
+
+ @VisibleForTesting
+ void setSocketCreator(SocketCreator creator) {
+ mSocketCreator = creator;
+ }
+
+ protected Socket createSocket() throws MessagingException {
+ if (mSocketCreator != null) {
+ return mSocketCreator.createSocket();
+ }
+
+ if (mNetwork == null) {
+ LogUtils.v(TAG, "createSocket: network not specified");
+ return new Socket();
+ }
+
+ try {
+ LogUtils.v(TAG, "createSocket: network specified");
+ return mNetwork.getSocketFactory().createSocket();
+ } catch (IOException ioe) {
+ LogUtils.d(TAG, ioe.toString());
+ throw new MessagingException(MessagingException.IOERROR, ioe.toString());
+ }
+ }
+
/**
* Lightweight version of SSLCertificateSocketFactory.verifyHostname, which provides this
* service but is not in the public API.
@@ -158,7 +221,7 @@
* @throws IOException if something goes wrong handshaking with the server
* @throws SSLPeerUnverifiedException if the server cannot prove its identity
*/
- private static void verifyHostname(Socket socket, String hostname) throws IOException {
+ private void verifyHostname(Socket socket, String hostname) throws IOException {
// The code at the start of OpenSSLSocketImpl.startHandshake()
// ensures that the call is idempotent, so we can safely call it.
SSLSocket ssl = (SSLSocket) socket;
@@ -166,6 +229,7 @@
SSLSession session = ssl.getSession();
if (session == null) {
+ mImapHelper.setDataChannelState(Status.DATA_CHANNEL_STATE_COMMUNICATION_ERROR);
throw new SSLException("Cannot verify SSL socket without session");
}
// TODO: Instead of reporting the name of the server we think we're connecting to,
@@ -173,8 +237,9 @@
// in the verifier code and is not available in the verifier API, and extracting the
// CN & alts is beyond the scope of this patch.
if (!HOSTNAME_VERIFIER.verify(hostname, session)) {
- throw new SSLPeerUnverifiedException(
- "Certificate hostname not useable for server: " + hostname);
+ mImapHelper.setDataChannelState(Status.DATA_CHANNEL_STATE_COMMUNICATION_ERROR);
+ throw new SSLPeerUnverifiedException("Certificate hostname not useable for server: "
+ + session.getPeerPrincipal());
}
}
diff --git a/src/com/android/phone/common/mail/store/ImapConnection.java b/src/com/android/phone/common/mail/store/ImapConnection.java
index ace7029..9207aa9 100644
--- a/src/com/android/phone/common/mail/store/ImapConnection.java
+++ b/src/com/android/phone/common/mail/store/ImapConnection.java
@@ -15,6 +15,7 @@
*/
package com.android.phone.common.mail.store;
+import android.provider.VoicemailContract.Status;
import android.text.TextUtils;
import com.android.phone.common.mail.AuthenticationFailedException;
@@ -105,9 +106,12 @@
doLogin();
} catch (SSLException e) {
LogUtils.d(TAG, "SSLException ", e);
+ mImapStore.getImapHelper().setDataChannelState(Status.DATA_CHANNEL_STATE_SERVER_ERROR);
throw new CertificateValidationException(e.getMessage(), e);
} catch (IOException ioe) {
LogUtils.d(TAG, "IOException", ioe);
+ mImapStore.getImapHelper()
+ .setDataChannelState(Status.DATA_CHANNEL_STATE_COMMUNICATION_ERROR);
throw ioe;
} finally {
destroyResponses();
@@ -144,6 +148,8 @@
if (ImapConstants.AUTHENTICATIONFAILED.equals(code) ||
ImapConstants.EXPIRED.equals(code) ||
(ImapConstants.NO.equals(status) && TextUtils.isEmpty(code))) {
+ mImapStore.getImapHelper()
+ .setDataChannelState(Status.DATA_CHANNEL_STATE_BAD_CONFIGURATION);
throw new AuthenticationFailedException(alertText, ie);
}
@@ -233,12 +239,12 @@
final String alert = response.getAlertTextOrEmpty().getString();
final String responseCode = response.getResponseCodeOrEmpty().getString();
destroyResponses();
-
+ mImapStore.getImapHelper().setDataChannelState(Status.DATA_CHANNEL_STATE_SERVER_ERROR);
// if the response code indicates an error occurred within the server, indicate that
if (ImapConstants.UNAVAILABLE.equals(responseCode)) {
+
throw new MessagingException(MessagingException.SERVER_ERROR, alert);
}
-
throw new ImapException(toString, status, alert, responseCode);
}
return responses;
diff --git a/src/com/android/phone/common/mail/store/ImapFolder.java b/src/com/android/phone/common/mail/store/ImapFolder.java
index 1d17ea9..90c552d 100644
--- a/src/com/android/phone/common/mail/store/ImapFolder.java
+++ b/src/com/android/phone/common/mail/store/ImapFolder.java
@@ -19,6 +19,8 @@
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.provider.VoicemailContract;
+import android.provider.VoicemailContract.Status;
+import android.telecom.Voicemail;
import android.text.TextUtils;
import android.util.Base64DataException;
import android.util.Log;
@@ -727,6 +729,7 @@
mMode = MODE_READ_WRITE;
}
} else if (response.isTagged()) { // Not OK
+ mStore.getImapHelper().setDataChannelState(Status.DATA_CHANNEL_STATE_SERVER_ERROR);
throw new MessagingException("Can't open mailbox: "
+ response.getStatusResponseTextOrEmpty());
}
@@ -789,6 +792,7 @@
mConnection = null; // To prevent close() from returning the connection to the pool.
close(false);
}
+ mStore.getImapHelper().setDataChannelState(Status.DATA_CHANNEL_STATE_COMMUNICATION_ERROR);
return new MessagingException(MessagingException.IOERROR, "IO Error", ioe);
}
diff --git a/src/com/android/phone/common/mail/store/ImapStore.java b/src/com/android/phone/common/mail/store/ImapStore.java
index 63a5c36..c8095e5 100644
--- a/src/com/android/phone/common/mail/store/ImapStore.java
+++ b/src/com/android/phone/common/mail/store/ImapStore.java
@@ -23,6 +23,7 @@
import com.android.phone.common.mail.Message;
import com.android.phone.common.mail.MessagingException;
import com.android.phone.common.mail.internet.MimeMessage;
+import com.android.phone.vvm.omtp.imap.ImapHelper;
import java.io.IOException;
import java.io.InputStream;
@@ -33,10 +34,11 @@
* should be returned on FetchProfile.Item.BODY_SANE requests. We'll use 125k now.
*/
public static final int FETCH_BODY_SANE_SUGGESTED_SIZE = (125 * 1024);
- private Context mContext;
- private String mUsername;
- private String mPassword;
- private MailTransport mTransport;
+ private final Context mContext;
+ private final ImapHelper mHelper;
+ private final String mUsername;
+ private final String mPassword;
+ private final MailTransport mTransport;
private ImapConnection mConnection;
public static final int FLAG_NONE = 0x00; // No flags
@@ -49,18 +51,24 @@
/**
* Contains all the information necessary to log into an imap server
*/
- public ImapStore(Context context, String username, String password, int port,
+ public ImapStore(Context context, ImapHelper helper, String username, String password, int port,
String serverName, int flags, Network network) {
mContext = context;
+ mHelper = helper;
mUsername = username;
mPassword = password;
- mTransport = new MailTransport(context, network, serverName, port, flags);
+ mTransport = new MailTransport(context, this.getImapHelper(),
+ network, serverName, port, flags);
}
public Context getContext() {
return mContext;
}
+ public ImapHelper getImapHelper() {
+ return mHelper;
+ }
+
public String getUsername() {
return mUsername;
}
diff --git a/src/com/android/phone/vvm/omtp/OmtpVvmCarrierConfigHelper.java b/src/com/android/phone/vvm/omtp/OmtpVvmCarrierConfigHelper.java
index 9393f81..57e23a9 100644
--- a/src/com/android/phone/vvm/omtp/OmtpVvmCarrierConfigHelper.java
+++ b/src/com/android/phone/vvm/omtp/OmtpVvmCarrierConfigHelper.java
@@ -96,6 +96,14 @@
.getBoolean(CarrierConfigManager.KEY_VVM_CELLULAR_DATA_REQUIRED_BOOLEAN);
}
+ public boolean isPrefetchEnabled() {
+ if (mCarrierConfig == null) {
+ return false;
+ }
+ return mCarrierConfig
+ .getBoolean(CarrierConfigManager.KEY_VVM_PREFETCH_BOOLEAN);
+ }
+
public void startActivation() {
OmtpMessageSender messageSender = getMessageSender();
if (messageSender != null) {
diff --git a/src/com/android/phone/vvm/omtp/fetch/VoicemailFetchedCallback.java b/src/com/android/phone/vvm/omtp/fetch/VoicemailFetchedCallback.java
index eb6a175..3862d54 100644
--- a/src/com/android/phone/vvm/omtp/fetch/VoicemailFetchedCallback.java
+++ b/src/com/android/phone/vvm/omtp/fetch/VoicemailFetchedCallback.java
@@ -39,7 +39,7 @@
private ContentResolver mContentResolver;
private Uri mUri;
- VoicemailFetchedCallback(Context context, Uri uri) {
+ public VoicemailFetchedCallback(Context context, Uri uri) {
mContentResolver = context.getContentResolver();
mUri = uri;
}
diff --git a/src/com/android/phone/vvm/omtp/imap/ImapHelper.java b/src/com/android/phone/vvm/omtp/imap/ImapHelper.java
index fd56c77..da2d34b 100644
--- a/src/com/android/phone/vvm/omtp/imap/ImapHelper.java
+++ b/src/com/android/phone/vvm/omtp/imap/ImapHelper.java
@@ -20,6 +20,7 @@
import android.net.Network;
import android.preference.PreferenceManager;
import android.provider.VoicemailContract;
+import android.provider.VoicemailContract.Status;
import android.telecom.PhoneAccountHandle;
import android.telecom.Voicemail;
import android.telephony.TelephonyManager;
@@ -27,6 +28,7 @@
import android.util.Log;
import com.android.phone.PhoneUtils;
+import com.android.phone.VoicemailUtils;
import com.android.phone.common.mail.Address;
import com.android.phone.common.mail.Body;
import com.android.phone.common.mail.BodyPart;
@@ -100,8 +102,10 @@
}
mImapStore = new ImapStore(
- context, username, password, port, serverName, auth, network);
+ context, this, username, password, port, serverName, auth, network);
} catch (NumberFormatException e) {
+ VoicemailUtils.setDataChannelState(
+ mContext, mPhoneAccount, Status.DATA_CHANNEL_STATE_BAD_CONFIGURATION);
LogUtils.w(TAG, "Could not parse port number");
}
@@ -133,6 +137,10 @@
return setFlags(voicemails, Flag.DELETED);
}
+ public void setDataChannelState(int dataChannelState) {
+ VoicemailUtils.setDataChannelState(mContext, mPhoneAccount, dataChannelState);
+ }
+
/**
* Set flags on the server for a given set of voicemails.
*
diff --git a/src/com/android/phone/vvm/omtp/sync/OmtpVvmSyncReceiver.java b/src/com/android/phone/vvm/omtp/sync/OmtpVvmSyncReceiver.java
new file mode 100644
index 0000000..f0d21d1
--- /dev/null
+++ b/src/com/android/phone/vvm/omtp/sync/OmtpVvmSyncReceiver.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2016 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.vvm.omtp.sync;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.provider.VoicemailContract;
+import android.util.Log;
+
+public class OmtpVvmSyncReceiver extends BroadcastReceiver {
+
+ private static final String TAG = "OmtpVvmSyncReceiver";
+
+ @Override
+ public void onReceive(final Context context, Intent intent) {
+ if (VoicemailContract.ACTION_SYNC_VOICEMAIL.equals(intent.getAction())) {
+ Log.v(TAG, "Sync intent received");
+ Intent syncIntent = OmtpVvmSyncService
+ .getSyncIntent(context, OmtpVvmSyncService.SYNC_FULL_SYNC, null, true);
+ context.startService(syncIntent);
+ }
+ }
+}
diff --git a/src/com/android/phone/vvm/omtp/sync/OmtpVvmSyncService.java b/src/com/android/phone/vvm/omtp/sync/OmtpVvmSyncService.java
index b3659ae..36deb08 100644
--- a/src/com/android/phone/vvm/omtp/sync/OmtpVvmSyncService.java
+++ b/src/com/android/phone/vvm/omtp/sync/OmtpVvmSyncService.java
@@ -22,14 +22,20 @@
import android.content.Intent;
import android.net.Network;
import android.net.NetworkInfo;
+import android.net.Uri;
import android.provider.VoicemailContract;
+import android.provider.VoicemailContract.Status;
import android.telecom.PhoneAccountHandle;
import android.telecom.Voicemail;
import android.text.TextUtils;
import android.util.Log;
+import com.android.phone.PhoneUtils;
+import com.android.phone.VoicemailUtils;
import com.android.phone.settings.VisualVoicemailSettingsUtil;
import com.android.phone.vvm.omtp.LocalLogHelper;
+import com.android.phone.vvm.omtp.OmtpVvmCarrierConfigHelper;
+import com.android.phone.vvm.omtp.fetch.VoicemailFetchedCallback;
import com.android.phone.vvm.omtp.imap.ImapHelper;
import java.util.HashMap;
@@ -207,9 +213,9 @@
boolean success = true;
if (voicemail == null) {
- success = syncAll(action, imapHelper);
+ success = syncAll(action, imapHelper, phoneAccount);
} else {
- success = syncOne(imapHelper, voicemail);
+ success = syncOne(imapHelper, voicemail, phoneAccount);
}
imapHelper.updateQuota();
@@ -223,6 +229,8 @@
// Nothing more to do here, just exit.
VisualVoicemailSettingsUtil.resetVisualVoicemailRetryInterval(this,
phoneAccount);
+ VoicemailUtils.setDataChannelState(
+ this, phoneAccount, Status.DATA_CHANNEL_STATE_OK);
return;
}
}
@@ -233,7 +241,7 @@
}
}
- private boolean syncAll(String action, ImapHelper imapHelper) {
+ private boolean syncAll(String action, ImapHelper imapHelper, PhoneAccountHandle account) {
boolean uploadSuccess = true;
boolean downloadSuccess = true;
@@ -241,7 +249,7 @@
uploadSuccess = upload(imapHelper);
}
if (SYNC_FULL_SYNC.equals(action) || SYNC_DOWNLOAD_ONLY.equals(action)) {
- downloadSuccess = download(imapHelper);
+ downloadSuccess = download(imapHelper, account);
}
Log.v(TAG, "upload succeeded: [" + String.valueOf(uploadSuccess)
@@ -259,7 +267,17 @@
return success;
}
- private boolean syncOne(ImapHelper imapHelper, Voicemail voicemail) {
+ private boolean syncOne(ImapHelper imapHelper, Voicemail voicemail,
+ PhoneAccountHandle account) {
+ OmtpVvmCarrierConfigHelper carrierConfigHelper =
+ new OmtpVvmCarrierConfigHelper(
+ this, PhoneUtils.getSubIdForPhoneAccountHandle(account));
+ if (carrierConfigHelper.isPrefetchEnabled()) {
+ VoicemailFetchedCallback callback = new VoicemailFetchedCallback(this,
+ voicemail.getUri());
+ imapHelper.fetchVoicemailPayload(callback, voicemail.getSourceData());
+ }
+
return imapHelper.fetchTranscription(
new TranscriptionFetchedCallback(this, voicemail),
voicemail.getSourceData());
@@ -314,7 +332,7 @@
return success;
}
- private boolean download(ImapHelper imapHelper) {
+ private boolean download(ImapHelper imapHelper, PhoneAccountHandle account) {
List<Voicemail> serverVoicemails = imapHelper.fetchAllVoicemails();
List<Voicemail> localVoicemails = mQueryHelper.getAllVoicemails();
@@ -349,8 +367,16 @@
}
// The leftover messages are messages that exist on the server but not locally.
+ OmtpVvmCarrierConfigHelper carrierConfigHelper =
+ new OmtpVvmCarrierConfigHelper(
+ this, PhoneUtils.getSubIdForPhoneAccountHandle(account));
+ boolean prefetchEnabled = carrierConfigHelper.isPrefetchEnabled();
for (Voicemail remoteVoicemail : remoteMap.values()) {
- VoicemailContract.Voicemails.insert(this, remoteVoicemail);
+ Uri uri = VoicemailContract.Voicemails.insert(this, remoteVoicemail);
+ if (prefetchEnabled) {
+ VoicemailFetchedCallback fetchedCallback = new VoicemailFetchedCallback(this, uri);
+ imapHelper.fetchVoicemailPayload(fetchedCallback, remoteVoicemail.getSourceData());
+ }
}
return true;
diff --git a/src/com/android/phone/vvm/omtp/sync/VvmNetworkRequestCallback.java b/src/com/android/phone/vvm/omtp/sync/VvmNetworkRequestCallback.java
index 8bef9dc..884bec9 100644
--- a/src/com/android/phone/vvm/omtp/sync/VvmNetworkRequestCallback.java
+++ b/src/com/android/phone/vvm/omtp/sync/VvmNetworkRequestCallback.java
@@ -23,10 +23,13 @@
import android.net.NetworkRequest;
import android.os.Handler;
import android.os.Looper;
+import android.provider.VoicemailContract;
+import android.provider.VoicemailContract.Status;
import android.telecom.PhoneAccountHandle;
import android.util.Log;
import com.android.phone.PhoneUtils;
+import com.android.phone.VoicemailUtils;
import com.android.phone.vvm.omtp.OmtpVvmCarrierConfigHelper;
/**
@@ -47,32 +50,32 @@
protected PhoneAccountHandle mPhoneAccount;
protected NetworkRequest mNetworkRequest;
private ConnectivityManager mConnectivityManager;
-
+ private final OmtpVvmCarrierConfigHelper mCarrierConfigHelper;
+ private final int mSubId;
private boolean mRequestSent = false;
private boolean mResultReceived = false;
public VvmNetworkRequestCallback(Context context, PhoneAccountHandle phoneAccount) {
mContext = context;
mPhoneAccount = phoneAccount;
- mNetworkRequest = getNetworkRequest(context, phoneAccount);
+ mSubId = PhoneUtils.getSubIdForPhoneAccountHandle(phoneAccount);
+ mCarrierConfigHelper = new OmtpVvmCarrierConfigHelper(context, mSubId);
+ mNetworkRequest = createNetworkRequest();
}
/**
* @return NetworkRequest for a proper transport type. Use only cellular network if the carrier
* requires it. Otherwise use whatever available.
*/
- private NetworkRequest getNetworkRequest(Context context, PhoneAccountHandle phoneAccount) {
- int subId = PhoneUtils.getSubIdForPhoneAccountHandle(phoneAccount);
- OmtpVvmCarrierConfigHelper carrierConfigHelper =
- new OmtpVvmCarrierConfigHelper(context, subId);
+ private NetworkRequest createNetworkRequest() {
NetworkRequest.Builder builder = new NetworkRequest.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
- if (carrierConfigHelper.isCellularDataRequired()) {
+ if (mCarrierConfigHelper.isCellularDataRequired()) {
Log.d(TAG, "Transport type: CELLULAR");
builder.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
- .setNetworkSpecifier(Integer.toString(subId));
+ .setNetworkSpecifier(Integer.toString(mSubId));
} else {
Log.d(TAG, "Transport type: ANY");
}
@@ -143,7 +146,14 @@
@CallSuper
public void onFailed(String reason) {
Log.d(TAG, "onFailed: " + reason);
- // TODO: Notify the user sync has failed?
+ if (mCarrierConfigHelper.isCellularDataRequired()) {
+ VoicemailUtils.setDataChannelState(
+ mContext, mPhoneAccount,
+ Status.DATA_CHANNEL_STATE_NO_CONNECTION_CELLULAR_REQUIRED);
+ } else {
+ VoicemailUtils.setDataChannelState(
+ mContext, mPhoneAccount, Status.DATA_CHANNEL_STATE_NO_CONNECTION);
+ }
releaseNetwork();
}
}
diff --git a/src/com/android/services/telephony/ImsConference.java b/src/com/android/services/telephony/ImsConference.java
index d1f4cc6..c039106 100644
--- a/src/com/android/services/telephony/ImsConference.java
+++ b/src/com/android/services/telephony/ImsConference.java
@@ -171,8 +171,8 @@
@Override
public void onConnectionCapabilitiesChanged(Connection c, int connectionCapabilities) {
- Log.d(this, "onCallCapabilitiesChanged: Connection: %s, callCapabilities: %s", c,
- connectionCapabilities);
+ Log.d(this, "onConnectionCapabilitiesChanged: Connection: %s," +
+ " connectionCapabilities: %s", c, connectionCapabilities);
int capabilites = ImsConference.this.getConnectionCapabilities();
setConnectionCapabilities(applyHostCapabilities(capabilites, connectionCapabilities));
}
@@ -269,37 +269,26 @@
* @return The merged capabilities to be applied to the conference.
*/
private int applyHostCapabilities(int conferenceCapabilities, int capabilities) {
- if (can(capabilities, Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL)) {
- conferenceCapabilities = applyCapability(conferenceCapabilities,
- Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL);
- } else {
- conferenceCapabilities = removeCapability(conferenceCapabilities,
- Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL);
- }
+ conferenceCapabilities = changeCapability(conferenceCapabilities,
+ Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL,
+ can(capabilities, Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL));
- if (can(capabilities, Connection.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL)) {
- conferenceCapabilities = applyCapability(conferenceCapabilities,
- Connection.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL);
- } else {
- conferenceCapabilities = removeCapability(conferenceCapabilities,
- Connection.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL);
- }
+ conferenceCapabilities = changeCapability(conferenceCapabilities,
+ Connection.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL,
+ can(capabilities, Connection.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL));
- if (can(capabilities, Connection.CAPABILITY_CAN_UPGRADE_TO_VIDEO)) {
- conferenceCapabilities = applyCapability(conferenceCapabilities,
- Connection.CAPABILITY_CAN_UPGRADE_TO_VIDEO);
- } else {
- conferenceCapabilities = removeCapability(conferenceCapabilities,
- Connection.CAPABILITY_CAN_UPGRADE_TO_VIDEO);
- }
+ conferenceCapabilities = changeCapability(conferenceCapabilities,
+ Connection.CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO,
+ can(capabilities, Connection.CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO));
- if (can(capabilities, Connection.CAPABILITY_HIGH_DEF_AUDIO)) {
- conferenceCapabilities = applyCapability(conferenceCapabilities,
- Connection.CAPABILITY_HIGH_DEF_AUDIO);
- } else {
- conferenceCapabilities = removeCapability(conferenceCapabilities,
- Connection.CAPABILITY_HIGH_DEF_AUDIO);
- }
+ conferenceCapabilities = changeCapability(conferenceCapabilities,
+ Connection.CAPABILITY_CAN_UPGRADE_TO_VIDEO,
+ can(capabilities, Connection.CAPABILITY_CAN_UPGRADE_TO_VIDEO));
+
+ conferenceCapabilities = changeCapability(conferenceCapabilities,
+ Connection.CAPABILITY_HIGH_DEF_AUDIO,
+ can(capabilities, Connection.CAPABILITY_HIGH_DEF_AUDIO));
+
return conferenceCapabilities;
}
@@ -452,14 +441,20 @@
// No-op
}
- private int applyCapability(int capabilities, int capability) {
- int newCapabilities = capabilities | capability;
- return newCapabilities;
- }
-
- private int removeCapability(int capabilities, int capability) {
- int newCapabilities = capabilities & ~capability;
- return newCapabilities;
+ /**
+ * Changes a capabilities bit-mask to add or remove a capability.
+ *
+ * @param capabilities The capabilities bit-mask.
+ * @param capability The capability to change.
+ * @param enabled Whether the capability should be set or removed.
+ * @return The capabilities bit-mask with the capability changed.
+ */
+ private int changeCapability(int capabilities, int capability, boolean enabled) {
+ if (enabled) {
+ return capabilities | capability;
+ } else {
+ return capabilities & ~capability;
+ }
}
/**
diff --git a/src/com/android/services/telephony/TelecomAccountRegistry.java b/src/com/android/services/telephony/TelecomAccountRegistry.java
index e8a93c6..fc4bb4d 100644
--- a/src/com/android/services/telephony/TelecomAccountRegistry.java
+++ b/src/com/android/services/telephony/TelecomAccountRegistry.java
@@ -102,7 +102,7 @@
int subId = mPhone.getSubId();
int color = PhoneAccount.NO_HIGHLIGHT_COLOR;
int slotId = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
- String line1Number = mTelephonyManager.getLine1NumberForSubscriber(subId);
+ String line1Number = mTelephonyManager.getLine1Number(subId);
if (line1Number == null) {
line1Number = "";
}
diff --git a/src/com/android/services/telephony/TelephonyConnection.java b/src/com/android/services/telephony/TelephonyConnection.java
index 339db6d..0142d64 100644
--- a/src/com/android/services/telephony/TelephonyConnection.java
+++ b/src/com/android/services/telephony/TelephonyConnection.java
@@ -34,6 +34,7 @@
import com.android.ims.ImsCallProfile;
import com.android.internal.telephony.Call;
import com.android.internal.telephony.CallStateException;
+import com.android.internal.telephony.Connection.Capability;
import com.android.internal.telephony.Connection.PostDialListener;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.gsm.SuppServiceNotification;
@@ -71,12 +72,11 @@
private static final Map<String, String> sExtrasMap = createExtrasMap();
private static final int MSG_SET_VIDEO_STATE = 8;
- private static final int MSG_SET_LOCAL_VIDEO_CAPABILITY = 9;
- private static final int MSG_SET_REMOTE_VIDEO_CAPABILITY = 10;
- private static final int MSG_SET_VIDEO_PROVIDER = 11;
- private static final int MSG_SET_AUDIO_QUALITY = 12;
- private static final int MSG_SET_CONFERENCE_PARTICIPANTS = 13;
- private static final int MSG_CONNECTION_EXTRAS_CHANGED = 14;
+ private static final int MSG_SET_VIDEO_PROVIDER = 9;
+ private static final int MSG_SET_AUDIO_QUALITY = 10;
+ private static final int MSG_SET_CONFERENCE_PARTICIPANTS = 11;
+ private static final int MSG_CONNECTION_EXTRAS_CHANGED = 12;
+ private static final int MSG_SET_ORIGNAL_CONNECTION_CAPABILITIES = 13;
private final Handler mHandler = new Handler() {
@Override
@@ -157,18 +157,6 @@
setVideoState(videoState);
break;
- case MSG_SET_LOCAL_VIDEO_CAPABILITY:
- boolean localVideoCapable = false;
- localVideoCapable = (boolean) msg.obj;
- setLocalVideoCapable(localVideoCapable);
- break;
-
- case MSG_SET_REMOTE_VIDEO_CAPABILITY:
- boolean remoteVideoCapable = false;
- remoteVideoCapable = (boolean) msg.obj;
- setRemoteVideoCapable(remoteVideoCapable);
- break;
-
case MSG_SET_VIDEO_PROVIDER:
VideoProvider videoProvider = (VideoProvider) msg.obj;
setVideoProvider(videoProvider);
@@ -188,6 +176,10 @@
final Bundle extras = (Bundle) msg.obj;
updateExtras(extras);
break;
+
+ case MSG_SET_ORIGNAL_CONNECTION_CAPABILITIES:
+ setOriginalConnectionCapabilities(msg.arg1);
+ break;
}
}
};
@@ -229,26 +221,15 @@
mHandler.obtainMessage(MSG_SET_VIDEO_STATE, videoState).sendToTarget();
}
- /**
- * The {@link com.android.internal.telephony.Connection} has reported a change in local
- * video capability.
- *
- * @param capable True if capable.
+ /*
+ * The {@link com.android.internal.telephony.Connection} has reported a change in
+ * connection capability.
+ * @param capabilities bit mask containing voice or video or both capabilities.
*/
@Override
- public void onLocalVideoCapabilityChanged(boolean capable) {
- mHandler.obtainMessage(MSG_SET_LOCAL_VIDEO_CAPABILITY, capable).sendToTarget();
- }
-
- /**
- * The {@link com.android.internal.telephony.Connection} has reported a change in remote
- * video capability.
- *
- * @param capable True if capable.
- */
- @Override
- public void onRemoteVideoCapabilityChanged(boolean capable) {
- mHandler.obtainMessage(MSG_SET_REMOTE_VIDEO_CAPABILITY, capable).sendToTarget();
+ public void onConnectionCapabilitiesChanged(int capabilities) {
+ mHandler.obtainMessage(MSG_SET_ORIGNAL_CONNECTION_CAPABILITIES,
+ capabilities, 0).sendToTarget();
}
/**
@@ -343,23 +324,10 @@
private boolean mIsMultiParty = false;
/**
- * Determines if the {@link TelephonyConnection} has local video capabilities.
- * This is used when {@link TelephonyConnection#updateConnectionCapabilities()}} is called,
- * ensuring the appropriate capabilities are set. Since capabilities
- * can be rebuilt at any time it is necessary to track the video capabilities between rebuild.
- * The capabilities (including video capabilities) are communicated to the telecom
- * layer.
+ * The {@link com.android.internal.telephony.Connection} capabilities associated with the
+ * current {@link #mOriginalConnection}.
*/
- private boolean mLocalVideoCapable;
-
- /**
- * Determines if the {@link TelephonyConnection} has remote video capabilities.
- * This is used when {@link TelephonyConnection#updateConnectionCapabilities()}} is called,
- * ensuring the appropriate capabilities are set. Since capabilities can be rebuilt at any time
- * it is necessary to track the video capabilities between rebuild. The capabilities (including
- * video capabilities) are communicated to the telecom layer.
- */
- private boolean mRemoteVideoCapable;
+ private int mOriginalConnectionCapabilities;
/**
* Determines if the {@link TelephonyConnection} is using wifi.
@@ -594,7 +562,7 @@
}
/**
- * Builds call capabilities common to all TelephonyConnections. Namely, apply IMS-based
+ * Builds connection capabilities common to all TelephonyConnections. Namely, apply IMS-based
* capabilities.
*/
protected int buildConnectionCapabilities() {
@@ -623,16 +591,12 @@
protected final void updateConnectionCapabilities() {
int newCapabilities = buildConnectionCapabilities();
- newCapabilities = changeCapability(newCapabilities,
- CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL, mRemoteVideoCapable);
- newCapabilities = changeCapability(newCapabilities,
- CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL, mLocalVideoCapable);
+ newCapabilities = applyOriginalConnectionCapabilities(newCapabilities);
newCapabilities = changeCapability(newCapabilities,
CAPABILITY_HIGH_DEF_AUDIO, mHasHighDefAudio);
newCapabilities = changeCapability(newCapabilities, CAPABILITY_WIFI, mIsWifi);
newCapabilities = changeCapability(newCapabilities, CAPABILITY_CAN_PAUSE_VIDEO,
- mIsVideoPauseSupported && mRemoteVideoCapable && mLocalVideoCapable);
-
+ mIsVideoPauseSupported && isVideoCapable());
newCapabilities = applyConferenceTerminationCapabilities(newCapabilities);
if (getConnectionCapabilities() != newCapabilities) {
@@ -687,8 +651,7 @@
// Set video state and capabilities
setVideoState(mOriginalConnection.getVideoState());
- setLocalVideoCapable(mOriginalConnection.isLocalVideoCapable());
- setRemoteVideoCapable(mOriginalConnection.isRemoteVideoCapable());
+ setOriginalConnectionCapabilities(mOriginalConnection.getConnectionCapabilities());
setWifi(mOriginalConnection.isWifi());
setVideoProvider(mOriginalConnection.getVideoProvider());
setAudioQuality(mOriginalConnection.getAudioQuality());
@@ -1077,10 +1040,24 @@
}
/**
- * Applies capabilities specific to conferences termination to the
- * {@code CallCapabilities} bit-mask.
+ * Determines if the current connection is video capable.
*
- * @param capabilities The {@code CallCapabilities} bit-mask.
+ * A connection is deemed to be video capable if the original connection capabilities state that
+ * both local and remote video is supported.
+ *
+ * @return {@code true} if the connection is video capable, {@code false} otherwise.
+ */
+ private boolean isVideoCapable() {
+ return can(mOriginalConnectionCapabilities, Capability.SUPPORTS_VT_LOCAL_BIDIRECTIONAL)
+ && can(mOriginalConnectionCapabilities,
+ Capability.SUPPORTS_VT_REMOTE_BIDIRECTIONAL);
+ }
+
+ /**
+ * Applies capabilities specific to conferences termination to the
+ * {@code ConnectionCapabilities} bit-mask.
+ *
+ * @param capabilities The {@code ConnectionCapabilities} bit-mask.
* @return The capabilities with the IMS conference capabilities applied.
*/
private int applyConferenceTerminationCapabilities(int capabilities) {
@@ -1097,43 +1074,42 @@
}
/**
- * Returns the local video capability state for the connection.
+ * Stores the new original connection capabilities, and applies them to the current connection,
+ * notifying any listeners as necessary.
*
- * @return {@code True} if the connection has local video capabilities.
+ * @param connectionCapabilities The original connection capabilties.
*/
- public boolean isLocalVideoCapable() {
- return mLocalVideoCapable;
- }
-
- /**
- * Returns the remote video capability state for the connection.
- *
- * @return {@code True} if the connection has remote video capabilities.
- */
- public boolean isRemoteVideoCapable() {
- return mRemoteVideoCapable;
- }
-
- /**
- * Sets whether video capability is present locally. Used during rebuild of the
- * capabilities to set the video call capabilities.
- *
- * @param capable {@code True} if video capable.
- */
- public void setLocalVideoCapable(boolean capable) {
- mLocalVideoCapable = capable;
+ public void setOriginalConnectionCapabilities(int connectionCapabilities) {
+ mOriginalConnectionCapabilities = connectionCapabilities;
updateConnectionCapabilities();
}
/**
- * Sets whether video capability is present remotely. Used during rebuild of the
- * capabilities to set the video call capabilities.
+ * Called to apply the capabilities present in the {@link #mOriginalConnection} to this
+ * {@link Connection}. Provides a mapping between the capabilities present in the original
+ * connection (see {@link com.android.internal.telephony.Connection.Capability}) and those in
+ * this {@link Connection}.
*
- * @param capable {@code True} if video capable.
+ * @param capabilities The capabilities bitmask from the {@link Connection}.
+ * @return the capabilities bitmask with the original connection capabilities remapped and
+ * applied.
*/
- public void setRemoteVideoCapable(boolean capable) {
- mRemoteVideoCapable = capable;
- updateConnectionCapabilities();
+ public int applyOriginalConnectionCapabilities(int capabilities) {
+ // We only support downgrading to audio if both the remote and local side support
+ // downgrading to audio.
+ boolean supportsDowngradeToAudio = can(mOriginalConnectionCapabilities,
+ Capability.SUPPORTS_DOWNGRADE_TO_VOICE_LOCAL |
+ Capability.SUPPORTS_DOWNGRADE_TO_VOICE_REMOTE);
+ capabilities = changeCapability(capabilities,
+ CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO, !supportsDowngradeToAudio);
+
+ capabilities = changeCapability(capabilities, CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL,
+ can(mOriginalConnectionCapabilities, Capability.SUPPORTS_VT_REMOTE_BIDIRECTIONAL));
+
+ capabilities = changeCapability(capabilities, CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL,
+ can(mOriginalConnectionCapabilities, Capability.SUPPORTS_VT_LOCAL_BIDIRECTIONAL));
+
+ return capabilities;
}
/**
diff --git a/tests/Android.mk b/tests/Android.mk
index a3a657b..6cc0355 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -27,4 +27,8 @@
LOCAL_INSTRUMENTATION_FOR := TeleService
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ android-support-test \
+ mockito-target
+
include $(BUILD_PACKAGE)
diff --git a/tests/src/com/android/phone/MockitoHelper.java b/tests/src/com/android/phone/MockitoHelper.java
new file mode 100644
index 0000000..3da5d6e
--- /dev/null
+++ b/tests/src/com/android/phone/MockitoHelper.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2016 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.services.telephony.Log;
+
+/**
+ * Helper for Mockito-based test cases.
+ */
+public final class MockitoHelper {
+
+ private static final String TAG = "MockitoHelper";
+
+ private ClassLoader mOriginalClassLoader;
+ private Thread mContextThread;
+
+ /**
+ * Creates a new helper, which in turn will set the context classloader so it can load Mockito
+ * resources.
+ *
+ * @param packageClass test case class
+ */
+ public void setUp(Class<?> packageClass) throws Exception {
+ // makes a copy of the context classloader
+ mContextThread = Thread.currentThread();
+ mOriginalClassLoader = mContextThread.getContextClassLoader();
+ ClassLoader newClassLoader = packageClass.getClassLoader();
+ Log.v(TAG, "Changing context classloader from " + mOriginalClassLoader
+ + " to " + newClassLoader);
+ mContextThread.setContextClassLoader(newClassLoader);
+ }
+
+ /**
+ * Restores the context classloader to the previous value.
+ */
+ public void tearDown() throws Exception {
+ Log.v(TAG, "Restoring context classloader to " + mOriginalClassLoader);
+ mContextThread.setContextClassLoader(mOriginalClassLoader);
+ }
+}
\ No newline at end of file
diff --git a/tests/src/com/android/phone/common/mail/MailTransportTest.java b/tests/src/com/android/phone/common/mail/MailTransportTest.java
new file mode 100644
index 0000000..6acd517
--- /dev/null
+++ b/tests/src/com/android/phone/common/mail/MailTransportTest.java
@@ -0,0 +1,413 @@
+/*
+ * Copyright (C) 2016 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.common.mail;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.net.Network;
+import android.test.AndroidTestCase;
+
+import com.android.phone.MockitoHelper;
+import com.android.phone.common.mail.MailTransport.SocketCreator;
+import com.android.phone.common.mail.store.ImapStore;
+import com.android.phone.vvm.omtp.imap.ImapHelper;
+
+import junit.framework.AssertionFailedError;
+
+import org.mockito.MockitoAnnotations;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.net.SocketAddress;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+
+import javax.net.SocketFactory;
+
+public class MailTransportTest extends AndroidTestCase {
+
+ private static final String HOST_ADDRESS = "127.0.0.1";
+ private static final String INVALID_HOST_ADDRESS = "255.255.255.255";
+ private static final int HOST_PORT = 80;
+ private static final int HOST_FLAGS = 0;
+ // bypass verifyHostname() in open() by setting ImapStore.FLAG_TRUST_ALL
+ private static final int HOST_FLAGS_SSL = ImapStore.FLAG_SSL & ImapStore.FLAG_TRUST_ALL;
+ private static final InetAddress VALID_INET_ADDRESS = createInetAddress(HOST_ADDRESS);
+ private static final InetAddress INVALID_INET_ADDRESS = createInetAddress(INVALID_HOST_ADDRESS);
+
+ // ClassLoader need to be replaced for mockito to work.
+ private MockitoHelper mMokitoHelper = new MockitoHelper();
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ mMokitoHelper.setUp(getClass());
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ mMokitoHelper.tearDown();
+ super.tearDown();
+ }
+
+ public void testCreateSocket_anyNetwork() throws MessagingException {
+ // With no network, Socket#Socket() should be called.
+ MailTransport transport =
+ new MailTransport(getContext(), createMockImapHelper(), null, HOST_ADDRESS,
+ HOST_PORT, HOST_FLAGS);
+ Socket socket = transport.createSocket();
+ assertTrue(socket != null);
+ }
+
+ public void testCreateSocket_networkSpecified() throws MessagingException, IOException {
+ // Network#getSocketFactory should be used to create socket.
+ Network mockNetwork = createMockNetwork();
+ MailTransport transport =
+ new MailTransport(getContext(), createMockImapHelper(), mockNetwork, HOST_ADDRESS,
+ HOST_PORT, HOST_FLAGS);
+ Socket socket = transport.createSocket();
+ assertTrue(socket != null);
+ verify(mockNetwork).getSocketFactory();
+ }
+
+ public void testCreateSocket_socketCreator() throws MessagingException, IOException {
+ // For testing purposes, how sockets are created can be overridden.
+ SocketCreator socketCreator = new SocketCreator() {
+
+ private final Socket mSocket = new Socket();
+
+ @Override
+ public Socket createSocket() {
+ return mSocket;
+ }
+ };
+
+ MailTransport transport = new
+ MailTransport(getContext(), createMockImapHelper(), null, HOST_ADDRESS, HOST_PORT,
+ HOST_FLAGS);
+
+ transport.setSocketCreator(socketCreator);
+
+ Socket socket = transport.createSocket();
+ assertTrue(socket == socketCreator.createSocket());
+ }
+
+ public void testOpen() throws MessagingException {
+ MailTransport transport = new MailTransport(getContext(), createMockImapHelper(), null,
+ HOST_ADDRESS,
+ HOST_PORT, HOST_FLAGS);
+ transport.setSocketCreator(new TestSocketCreator());
+ transport.open();
+ assertTrue(transport.isOpen());
+
+ }
+
+ public void testOpen_Ssl() throws MessagingException {
+ //opening with ssl support.
+ MailTransport transport = new MailTransport(getContext(), createMockImapHelper(), null,
+ HOST_ADDRESS, HOST_PORT, HOST_FLAGS_SSL);
+ transport.setSocketCreator(new TestSocketCreator());
+ transport.open();
+ assertTrue(transport.isOpen());
+
+ }
+
+ public void testOpen_MultiIp() throws MessagingException {
+ //In case of round robin DNS, try all resolved address until one succeeded.
+ Network network = createMultiIpMockNetwork();
+ MailTransport transport = new MailTransport(getContext(), createMockImapHelper(), network,
+ HOST_ADDRESS,
+ HOST_PORT, HOST_FLAGS);
+ transport.setSocketCreator(new TestSocketCreator());
+ transport.open();
+ assertTrue(transport.isOpen());
+ }
+
+ public void testOpen_MultiIp_SSL() throws MessagingException {
+ Network network = createMultiIpMockNetwork();
+
+ MailTransport transport = new MailTransport(getContext(), createMockImapHelper(), network,
+ HOST_ADDRESS,
+ HOST_PORT, HOST_FLAGS_SSL);
+ transport.setSocketCreator(new TestSocketCreator());
+ transport.open();
+ assertTrue(transport.isOpen());
+ }
+
+ public void testOpen_network_hostResolutionFailed() {
+ // Couldn't resolve host on the network. Open() should fail.
+ Network network = createMockNetwork();
+ try {
+ when(network.getAllByName(HOST_ADDRESS))
+ .thenThrow(new UnknownHostException("host resolution failed"));
+ } catch (IOException e) {
+ //ignored
+ }
+
+ MailTransport transport = new MailTransport(getContext(), createMockImapHelper(), network,
+ HOST_ADDRESS,
+ HOST_PORT, HOST_FLAGS);
+ try {
+ transport.open();
+ throw new AssertionFailedError("Should throw MessagingException");
+ } catch (MessagingException e) {
+ //expected
+ }
+ assertFalse(transport.isOpen());
+ }
+
+ public void testOpen_createSocketFailed() {
+ // Unable to create socket. Open() should fail.
+ MailTransport transport = new MailTransport(getContext(), createMockImapHelper(), null,
+ HOST_ADDRESS,
+ HOST_PORT, HOST_FLAGS);
+ transport.setSocketCreator(new SocketCreator() {
+ @Override
+ public Socket createSocket() throws MessagingException {
+ throw new MessagingException("createSocket failed");
+ }
+ });
+ try {
+ transport.open();
+ throw new AssertionFailedError("Should throw MessagingException");
+ } catch (MessagingException e) {
+ //expected
+ }
+ assertFalse(transport.isOpen());
+ }
+
+ public void testOpen_network_createSocketFailed() {
+ // Unable to create socket. Open() should fail.
+
+ Network network = createOneIpMockNetwork();
+ SocketFactory mockSocketFactory = mock(SocketFactory.class);
+ try {
+ when(mockSocketFactory.createSocket())
+ .thenThrow(new IOException("unable to create socket"));
+ } catch (IOException e) {
+ //ignored
+ }
+ when(network.getSocketFactory()).thenReturn(mockSocketFactory);
+
+ MailTransport transport = new MailTransport(getContext(), createMockImapHelper(), network,
+ HOST_ADDRESS, HOST_PORT, HOST_FLAGS);
+
+ try {
+ transport.open();
+ throw new AssertionFailedError("Should throw MessagingException");
+ } catch (MessagingException e) {
+ //expected
+ }
+ assertFalse(transport.isOpen());
+ }
+
+ public void testOpen_connectFailed_one() {
+ // There is only one IP for this host, and we failed to connect to it. Open() should fail.
+
+ MailTransport transport = new MailTransport(getContext(), createMockImapHelper(), null,
+ HOST_ADDRESS, HOST_PORT, HOST_FLAGS);
+ transport.setSocketCreator(new SocketCreator() {
+ @Override
+ public Socket createSocket() throws MessagingException {
+ return new Socket() {
+ @Override
+ public void connect(SocketAddress address, int timeout) throws IOException {
+ throw new IOException("connect failed");
+ }
+ };
+ }
+ });
+ try {
+ transport.open();
+ throw new AssertionFailedError("Should throw MessagingException");
+ } catch (MessagingException e) {
+ //expected
+ }
+ assertFalse(transport.isOpen());
+ }
+
+ public void testOpen_connectFailed_multi() {
+ // There are multiple IP for this host, and we failed to connect to any of it.
+ // Open() should fail.
+ MailTransport transport = new MailTransport(getContext(), createMockImapHelper(),
+ createMultiIpMockNetwork(), HOST_ADDRESS, HOST_PORT, HOST_FLAGS);
+ transport.setSocketCreator(new SocketCreator() {
+ @Override
+ public Socket createSocket() throws MessagingException {
+ return new Socket() {
+ @Override
+ public void connect(SocketAddress address, int timeout) throws IOException {
+ throw new IOException("connect failed");
+ }
+ };
+ }
+ });
+ try {
+ transport.open();
+ throw new AssertionFailedError("Should throw MessagingException");
+ } catch (MessagingException e) {
+ //expected
+ }
+ assertFalse(transport.isOpen());
+ }
+
+ private class TestSocket extends Socket {
+
+ boolean mConnected = false;
+
+
+ /**
+ * A make a mock connection to the address.
+ *
+ * @param address Only address equivalent to VALID_INET_ADDRESS or INVALID_INET_ADDRESS is
+ * accepted
+ * @param timeout Ignored but should >= 0.
+ */
+ @Override
+ public void connect(SocketAddress address, int timeout) throws IOException {
+ // copied from Socket#connect
+ if (isClosed()) {
+ throw new SocketException("Socket is closed");
+ }
+ if (timeout < 0) {
+ throw new IllegalArgumentException("timeout < 0");
+ }
+ if (isConnected()) {
+ throw new SocketException("Already connected");
+ }
+ if (address == null) {
+ throw new IllegalArgumentException("remoteAddr == null");
+ }
+
+ if (!(address instanceof InetSocketAddress)) {
+ throw new AssertionError("address should be InetSocketAddress");
+ }
+
+ InetSocketAddress inetSocketAddress = (InetSocketAddress) address;
+ if (inetSocketAddress.getAddress().equals(INVALID_INET_ADDRESS)) {
+ throw new IOException("invalid address");
+ } else if (inetSocketAddress.getAddress().equals(VALID_INET_ADDRESS)) {
+ mConnected = true;
+ } else {
+ throw new AssertionError("Only INVALID_ADDRESS or VALID_ADDRESS are allowed");
+ }
+ }
+
+ @Override
+ public InputStream getInputStream() {
+ return null;
+ }
+
+ @Override
+ public OutputStream getOutputStream() {
+ return null;
+ }
+
+ @Override
+ public boolean isConnected() {
+ return mConnected;
+ }
+
+ }
+
+
+ private class TestSocketCreator implements MailTransport.SocketCreator {
+
+ @Override
+ public Socket createSocket() throws MessagingException {
+ Socket socket = new TestSocket();
+ return socket;
+ }
+
+ }
+
+ private ImapHelper createMockImapHelper() {
+ return mock(ImapHelper.class);
+ }
+
+ /**
+ * @return a mock Network that can create a TestSocket with {@code getSocketFactory()
+ * .createSocket()}
+ */
+ private Network createMockNetwork() {
+ Network network = mock(Network.class);
+ SocketFactory mockSocketFactory = mock(SocketFactory.class);
+ try {
+ when(mockSocketFactory.createSocket()).thenReturn(new TestSocket());
+ } catch (IOException e) {
+ //ignored
+ }
+ when(network.getSocketFactory()).thenReturn(mockSocketFactory);
+ return network;
+ }
+
+ /**
+ * @return a mock Network like {@link MailTransportTest#createMockNetwork()}, but also supports
+ * {@link Network#getAllByName(String)} with one valid result.
+ */
+ private Network createOneIpMockNetwork() {
+ Network network = createMockNetwork();
+ try {
+ when(network.getAllByName(HOST_ADDRESS))
+ .thenReturn(new InetAddress[] {VALID_INET_ADDRESS});
+ } catch (UnknownHostException e) {
+ //ignored
+ }
+
+ return network;
+ }
+
+ /**
+ * @return a mock Network like {@link MailTransportTest#createMockNetwork()}, but also supports
+ * {@link Network#getAllByName(String)}, which will return 2 address with the first one
+ * invalid.
+ */
+ private Network createMultiIpMockNetwork() {
+ Network network = createMockNetwork();
+ try {
+ when(network.getAllByName(HOST_ADDRESS))
+ .thenReturn(new InetAddress[] {INVALID_INET_ADDRESS, VALID_INET_ADDRESS});
+ } catch (UnknownHostException e) {
+ //ignored
+ }
+
+ return network;
+ }
+
+ /**
+ * helper method to translate{@code host} into a InetAddress.
+ *
+ * @param host IP address of the host. Domain name should not be used as this method should not
+ * access the internet.
+ */
+ private static InetAddress createInetAddress(String host) {
+ try {
+ return InetAddress.getByName(host);
+ } catch (UnknownHostException e) {
+ return null;
+ }
+ }
+
+
+}