session-android/src/org/thoughtcrime/securesms/mms/IncomingLegacyMmsConnection.java
Jake McGinty ab82ff0b69 use IPv6 routing API then fallback to Lollipop
1) Utilize the hidden API requestRouteToHostAddress that takes an
   InetAddress (IPv6-capable) instead of a forced IPv4 integer-
   encoded address. Will fallback to the IPv4 one if reflection
   fails for whatever reason.

2) If on Lollipop and our manual MMS code doesn't work, will try
   to use the Lollipop API and give it 60 seconds instead of 30,
   since I did run into the timeout not being long enough in
   certain conditions and I'm thinking maybe it just wasn't long
   enough for some carriers.

Closes #3105
// FREEBIE
2015-05-04 18:29:30 -07:00

145 lines
5.2 KiB
Java

/**
* Copyright (C) 2015 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms.mms;
import android.content.Context;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
import org.apache.http.Header;
import org.apache.http.HttpHost;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGetHC4;
import org.apache.http.client.methods.HttpUriRequest;
import java.io.IOException;
import java.util.Arrays;
import ws.com.google.android.mms.InvalidHeaderValueException;
import ws.com.google.android.mms.pdu.NotifyRespInd;
import ws.com.google.android.mms.pdu.PduComposer;
import ws.com.google.android.mms.pdu.PduHeaders;
import ws.com.google.android.mms.pdu.PduParser;
import ws.com.google.android.mms.pdu.RetrieveConf;
@SuppressWarnings("deprecation")
public class IncomingLegacyMmsConnection extends LegacyMmsConnection implements IncomingMmsConnection {
private static final String TAG = IncomingLegacyMmsConnection.class.getSimpleName();
public IncomingLegacyMmsConnection(Context context) throws ApnUnavailableException {
super(context);
}
private HttpUriRequest constructRequest(Apn contentApn, boolean useProxy) throws IOException {
HttpGetHC4 request = new HttpGetHC4(contentApn.getMmsc());
for (Header header : getBaseHeaders()) {
request.addHeader(header);
}
if (useProxy) {
HttpHost proxy = new HttpHost(contentApn.getProxy(), contentApn.getPort());
request.setConfig(RequestConfig.custom().setProxy(proxy).build());
}
return request;
}
@Override
public @Nullable RetrieveConf retrieve(@NonNull String contentLocation,
byte[] transactionId)
throws MmsRadioException, ApnUnavailableException, IOException
{
MmsRadio radio = MmsRadio.getInstance(context);
Apn contentApn = new Apn(contentLocation, apn.getProxy(), Integer.toString(apn.getPort()), apn.getUsername(), apn.getPassword());
if (isCdmaDevice()) {
Log.w(TAG, "Connecting directly...");
try {
return retrieve(contentApn, transactionId, false, false);
} catch (IOException | ApnUnavailableException e) {
Log.w(TAG, e);
}
}
Log.w(TAG, "Changing radio to MMS mode..");
radio.connect();
try {
Log.w(TAG, "Downloading in MMS mode with proxy...");
try {
return retrieve(contentApn, transactionId, true, true);
} catch (IOException | ApnUnavailableException e) {
Log.w(TAG, e);
}
Log.w(TAG, "Downloading in MMS mode without proxy...");
return retrieve(contentApn, transactionId, true, false);
} finally {
radio.disconnect();
}
}
public RetrieveConf retrieve(Apn contentApn, byte[] transactionId, boolean usingMmsRadio, boolean useProxyIfAvailable)
throws IOException, ApnUnavailableException
{
byte[] pdu = null;
final boolean useProxy = useProxyIfAvailable && contentApn.hasProxy();
final String targetHost = useProxy
? contentApn.getProxy()
: Uri.parse(contentApn.getMmsc()).getHost();
if (checkRouteToHost(context, targetHost, usingMmsRadio)) {
Log.w(TAG, "got successful route to host " + targetHost);
pdu = execute(constructRequest(contentApn, useProxy));
}
if (pdu == null) {
throw new IOException("Connection manager could not obtain route to host.");
}
RetrieveConf retrieved = (RetrieveConf)new PduParser(pdu).parse();
if (retrieved == null) {
Log.w(TAG, "Couldn't parse PDU, byte response: " + Arrays.toString(pdu));
Log.w(TAG, "Couldn't parse PDU, ASCII: " + new String(pdu));
throw new IOException("Bad retrieved PDU");
}
sendRetrievedAcknowledgement(transactionId, usingMmsRadio, useProxy);
return retrieved;
}
private void sendRetrievedAcknowledgement(byte[] transactionId,
boolean usingRadio,
boolean useProxy)
throws ApnUnavailableException
{
try {
NotifyRespInd notifyResponse = new NotifyRespInd(PduHeaders.CURRENT_MMS_VERSION,
transactionId,
PduHeaders.STATUS_RETRIEVED);
OutgoingLegacyMmsConnection connection = new OutgoingLegacyMmsConnection(context);
connection.sendNotificationReceived(new PduComposer(context, notifyResponse).make(), usingRadio, useProxy);
} catch (InvalidHeaderValueException | IOException e) {
Log.w(TAG, e);
}
}
}