添加https支持
This commit is contained in:
@ -30,6 +30,7 @@ import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.widget.PopupWindow;
|
||||
import com.arialyy.aria.core.command.CmdFactory;
|
||||
import com.arialyy.aria.util.CAConfiguration;
|
||||
import com.arialyy.aria.util.CheckUtil;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
import com.arialyy.aria.core.command.IDownloadCmd;
|
||||
@ -48,12 +49,12 @@ import java.util.Set;
|
||||
* Aria管理器,任务操作在这里执行
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) public class AriaManager {
|
||||
private static final String TAG = "AriaManager";
|
||||
private static final Object LOCK = new Object();
|
||||
private static volatile AriaManager INSTANCE = null;
|
||||
private Map<String, AMReceiver> mTargets = new HashMap<>();
|
||||
private static final String TAG = "AriaManager";
|
||||
private static final Object LOCK = new Object();
|
||||
private static volatile AriaManager INSTANCE = null;
|
||||
private Map<String, AMReceiver> mTargets = new HashMap<>();
|
||||
private DownloadManager mManager;
|
||||
private LifeCallback mLifeCallback;
|
||||
private LifeCallback mLifeCallback;
|
||||
|
||||
private AriaManager(Context context) {
|
||||
regAppLifeCallback(context);
|
||||
@ -73,6 +74,24 @@ import java.util.Set;
|
||||
return getTarget(obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置CA证书信息
|
||||
*
|
||||
* @param caAlias ca证书别名
|
||||
* @param caPath assets 文件夹下的ca证书完整路径
|
||||
*/
|
||||
public void setCAInfo(String caAlias, String caPath) {
|
||||
if (TextUtils.isEmpty(caAlias)) {
|
||||
Log.e(TAG, "ca证书别名不能为null");
|
||||
return;
|
||||
} else if (TextUtils.isEmpty(caPath)) {
|
||||
Log.e(TAG, "ca证书路径不能为null");
|
||||
return;
|
||||
}
|
||||
CAConfiguration.CA_ALIAS = caAlias;
|
||||
CAConfiguration.CA_PATH = caPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取下载列表
|
||||
*/
|
||||
@ -100,7 +119,7 @@ import java.util.Set;
|
||||
*/
|
||||
public void stopAllTask() {
|
||||
List<DownloadEntity> allEntity = mManager.getAllDownloadEntity();
|
||||
List<IDownloadCmd> stopCmds = new ArrayList<>();
|
||||
List<IDownloadCmd> stopCmds = new ArrayList<>();
|
||||
for (DownloadEntity entity : allEntity) {
|
||||
if (entity.getState() == DownloadEntity.STATE_DOWNLOAD_ING) {
|
||||
stopCmds.add(CommonUtil.createCmd(entity, CmdFactory.TASK_STOP));
|
||||
@ -159,8 +178,8 @@ import java.util.Set;
|
||||
* 删除所有任务
|
||||
*/
|
||||
public void cancelAllTask() {
|
||||
List<DownloadEntity> allEntity = mManager.getAllDownloadEntity();
|
||||
List<IDownloadCmd> cancelCmds = new ArrayList<>();
|
||||
List<DownloadEntity> allEntity = mManager.getAllDownloadEntity();
|
||||
List<IDownloadCmd> cancelCmds = new ArrayList<>();
|
||||
for (DownloadEntity entity : allEntity) {
|
||||
cancelCmds.add(CommonUtil.createCmd(entity, CmdFactory.TASK_CANCEL));
|
||||
}
|
||||
@ -174,9 +193,9 @@ import java.util.Set;
|
||||
}
|
||||
|
||||
private AMReceiver putTarget(Object obj) {
|
||||
String clsName = obj.getClass().getName();
|
||||
AMReceiver target = null;
|
||||
String key = "";
|
||||
String clsName = obj.getClass().getName();
|
||||
AMReceiver target = null;
|
||||
String key = "";
|
||||
if (!(obj instanceof Activity)) {
|
||||
if (obj instanceof android.support.v4.app.Fragment) {
|
||||
key = clsName + "_" + ((Fragment) obj).getActivity().getClass().getName();
|
||||
@ -249,12 +268,12 @@ import java.util.Set;
|
||||
*/
|
||||
private void handleDialogLift(Dialog dialog) {
|
||||
try {
|
||||
Field dismissField = CommonUtil.getField(dialog.getClass(), "mDismissMessage");
|
||||
Message dismissMsg = (Message) dismissField.get(dialog);
|
||||
Field dismissField = CommonUtil.getField(dialog.getClass(), "mDismissMessage");
|
||||
Message dismissMsg = (Message) dismissField.get(dialog);
|
||||
//如果Dialog已经设置Dismiss事件,则查找cancel事件
|
||||
if (dismissMsg != null) {
|
||||
Field cancelField = CommonUtil.getField(dialog.getClass(), "mCancelMessage");
|
||||
Message cancelMsg = (Message) cancelField.get(dialog);
|
||||
Field cancelField = CommonUtil.getField(dialog.getClass(), "mCancelMessage");
|
||||
Message cancelMsg = (Message) cancelField.get(dialog);
|
||||
if (cancelMsg != null) {
|
||||
Log.e(TAG, "你已经对Dialog设置了Dismiss和cancel事件。为了防止内存泄露,"
|
||||
+ "请在dismiss方法中调用Aria.whit(this).removeSchedulerListener();来注销事件");
|
||||
@ -316,12 +335,12 @@ import java.util.Set;
|
||||
* onDestroy
|
||||
*/
|
||||
private void destroySchedulerListener(Object obj) {
|
||||
Set<String> keys = mTargets.keySet();
|
||||
String clsName = obj.getClass().getName();
|
||||
Set<String> keys = mTargets.keySet();
|
||||
String clsName = obj.getClass().getName();
|
||||
for (Iterator<Map.Entry<String, AMReceiver>> iter = mTargets.entrySet().iterator();
|
||||
iter.hasNext(); ) {
|
||||
Map.Entry<String, AMReceiver> entry = iter.next();
|
||||
String key = entry.getKey();
|
||||
String key = entry.getKey();
|
||||
if (key.equals(clsName) || key.contains(clsName)) {
|
||||
AMReceiver receiver = mTargets.get(key);
|
||||
if (receiver.obj != null) {
|
||||
|
@ -20,7 +20,9 @@ import android.content.Context;
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
import com.arialyy.aria.core.DownloadEntity;
|
||||
import com.arialyy.aria.util.CAConfiguration;
|
||||
import com.arialyy.aria.util.CommonUtil;
|
||||
import com.arialyy.aria.util.SSLContextUtil;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@ -29,43 +31,47 @@ import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.ProtocolException;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.util.Properties;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
|
||||
/**
|
||||
* Created by lyy on 2015/8/25.
|
||||
* 下载工具类
|
||||
*/
|
||||
final class DownloadUtil implements IDownloadUtil, Runnable {
|
||||
private static final String TAG = "DownloadUtil";
|
||||
private static final String TAG = "DownloadUtil";
|
||||
private static final Object LOCK = new Object();
|
||||
/**
|
||||
* 线程数
|
||||
*/
|
||||
private final int THREAD_NUM;
|
||||
private final int THREAD_NUM;
|
||||
//下载监听
|
||||
private IDownloadListener mListener;
|
||||
private int mConnectTimeOut = 5000 * 4; //连接超时时间
|
||||
private int mReadTimeOut = 5000 * 20; //流读取的超时时间
|
||||
private IDownloadListener mListener;
|
||||
private int mConnectTimeOut = 5000 * 4; //连接超时时间
|
||||
private int mReadTimeOut = 5000 * 20; //流读取的超时时间
|
||||
/**
|
||||
* 已经完成下载任务的线程数量
|
||||
*/
|
||||
private boolean isDownloading = false;
|
||||
private boolean isStop = false;
|
||||
private boolean isCancel = false;
|
||||
private boolean isNewTask = true;
|
||||
private boolean isDownloading = false;
|
||||
private boolean isStop = false;
|
||||
private boolean isCancel = false;
|
||||
private boolean isNewTask = true;
|
||||
private boolean isSupportBreakpoint = true;
|
||||
private int mCompleteThreadNum = 0;
|
||||
private int mCancelNum = 0;
|
||||
private long mCurrentLocation = 0;
|
||||
private int mStopNum = 0;
|
||||
private int mFailNum = 0;
|
||||
private Context mContext;
|
||||
private DownloadEntity mDownloadEntity;
|
||||
private int mCompleteThreadNum = 0;
|
||||
private int mCancelNum = 0;
|
||||
private long mCurrentLocation = 0;
|
||||
private int mStopNum = 0;
|
||||
private int mFailNum = 0;
|
||||
private Context mContext;
|
||||
private DownloadEntity mDownloadEntity;
|
||||
private ExecutorService mFixedThreadPool;
|
||||
private File mDownloadFile; //下载的文件
|
||||
private File mConfigFile;//下载信息配置文件
|
||||
private File mDownloadFile; //下载的文件
|
||||
private File mConfigFile;//下载信息配置文件
|
||||
private SparseArray<Runnable> mTask = new SparseArray<>();
|
||||
|
||||
DownloadUtil(Context context, DownloadEntity entity, IDownloadListener downloadListener) {
|
||||
@ -222,8 +228,8 @@ final class DownloadUtil implements IDownloadUtil, Runnable {
|
||||
|
||||
@Override public void run() {
|
||||
try {
|
||||
URL url = new URL(mDownloadEntity.getDownloadUrl());
|
||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||
URL url = new URL(mDownloadEntity.getDownloadUrl());
|
||||
HttpURLConnection conn = handleConnection(url);
|
||||
setConnectParam(conn);
|
||||
conn.setRequestProperty("Range", "bytes=" + 0 + "-");
|
||||
conn.setConnectTimeout(mConnectTimeOut * 4);
|
||||
@ -242,7 +248,7 @@ final class DownloadUtil implements IDownloadUtil, Runnable {
|
||||
handleBreakpoint(conn);
|
||||
} else if (code == HttpURLConnection.HTTP_OK || len < 0) {
|
||||
//在conn.setRequestProperty("Range", "bytes=" + 0 + "-");下,200为不支持断点状态
|
||||
if (len < 0){
|
||||
if (len < 0) {
|
||||
failDownload("任务【" + mDownloadEntity.getDownloadUrl() + "】下载失败,文件长度小于0");
|
||||
return;
|
||||
}
|
||||
@ -263,6 +269,30 @@ final class DownloadUtil implements IDownloadUtil, Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理链接
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
private HttpURLConnection handleConnection(URL url) throws IOException {
|
||||
HttpURLConnection conn;
|
||||
URLConnection urlConn = url.openConnection();
|
||||
if (urlConn instanceof HttpsURLConnection) {
|
||||
conn = (HttpsURLConnection) urlConn;
|
||||
SSLContext sslContext =
|
||||
SSLContextUtil.getSSLContext(CAConfiguration.CA_ALIAS, CAConfiguration.CA_ALIAS);
|
||||
if (sslContext == null) {
|
||||
sslContext = SSLContextUtil.getDefaultSLLContext();
|
||||
}
|
||||
SSLSocketFactory ssf = sslContext.getSocketFactory();
|
||||
((HttpsURLConnection) conn).setSSLSocketFactory(ssf);
|
||||
((HttpsURLConnection) conn).setHostnameVerifier(SSLContextUtil.HOSTNAME_VERIFIER);
|
||||
} else {
|
||||
conn = (HttpURLConnection) urlConn;
|
||||
}
|
||||
return conn;
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理断点
|
||||
*/
|
||||
@ -305,15 +335,15 @@ final class DownloadUtil implements IDownloadUtil, Runnable {
|
||||
}
|
||||
}
|
||||
}
|
||||
int blockSize = fileLength / THREAD_NUM;
|
||||
int[] recordL = new int[THREAD_NUM];
|
||||
int rl = 0;
|
||||
int blockSize = fileLength / THREAD_NUM;
|
||||
int[] recordL = new int[THREAD_NUM];
|
||||
int rl = 0;
|
||||
for (int i = 0; i < THREAD_NUM; i++) {
|
||||
recordL[i] = -1;
|
||||
}
|
||||
for (int i = 0; i < THREAD_NUM; i++) {
|
||||
long startL = i * blockSize, endL = (i + 1) * blockSize;
|
||||
Object state = pro.getProperty(mDownloadFile.getName() + "_state_" + i);
|
||||
long startL = i * blockSize, endL = (i + 1) * blockSize;
|
||||
Object state = pro.getProperty(mDownloadFile.getName() + "_state_" + i);
|
||||
if (state != null && Integer.parseInt(state + "") == 1) { //该线程已经完成
|
||||
mCurrentLocation += endL - startL;
|
||||
Log.d(TAG, "++++++++++ 线程_" + i + "_已经下载完成 ++++++++++");
|
||||
@ -381,12 +411,12 @@ final class DownloadUtil implements IDownloadUtil, Runnable {
|
||||
*/
|
||||
private static class ConfigEntity {
|
||||
//文件大小
|
||||
long fileSize;
|
||||
String downloadUrl;
|
||||
int threadId;
|
||||
long startLocation;
|
||||
long endLocation;
|
||||
File tempFile;
|
||||
long fileSize;
|
||||
String downloadUrl;
|
||||
int threadId;
|
||||
long startLocation;
|
||||
long endLocation;
|
||||
File tempFile;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -395,7 +425,7 @@ final class DownloadUtil implements IDownloadUtil, Runnable {
|
||||
private class SingleThreadTask implements Runnable {
|
||||
private static final String TAG = "SingleThreadTask";
|
||||
private ConfigEntity configEntity;
|
||||
private String configFPath;
|
||||
private String configFPath;
|
||||
private long currentLocation = 0;
|
||||
|
||||
private SingleThreadTask(ConfigEntity downloadInfo) {
|
||||
@ -410,10 +440,11 @@ final class DownloadUtil implements IDownloadUtil, Runnable {
|
||||
|
||||
@Override public void run() {
|
||||
HttpURLConnection conn = null;
|
||||
InputStream is = null;
|
||||
InputStream is = null;
|
||||
try {
|
||||
URL url = new URL(configEntity.downloadUrl);
|
||||
conn = (HttpURLConnection) url.openConnection();
|
||||
//conn = (HttpURLConnection) url.openConnection();
|
||||
conn = handleConnection(url);
|
||||
if (isSupportBreakpoint) {
|
||||
Log.d(TAG, "线程_"
|
||||
+ configEntity.threadId
|
||||
@ -437,7 +468,7 @@ final class DownloadUtil implements IDownloadUtil, Runnable {
|
||||
//设置每条线程写入文件的位置
|
||||
file.seek(configEntity.startLocation);
|
||||
byte[] buffer = new byte[1024];
|
||||
int len;
|
||||
int len;
|
||||
//当前子线程的下载位置
|
||||
currentLocation = configEntity.startLocation;
|
||||
while ((len = is.read(buffer)) != -1) {
|
||||
@ -597,8 +628,8 @@ final class DownloadUtil implements IDownloadUtil, Runnable {
|
||||
* 将记录写入到配置文件
|
||||
*/
|
||||
private void writeConfig(String key, String record) throws IOException {
|
||||
File configFile = new File(configFPath);
|
||||
Properties pro = CommonUtil.loadConfig(configFile);
|
||||
File configFile = new File(configFPath);
|
||||
Properties pro = CommonUtil.loadConfig(configFile);
|
||||
pro.setProperty(key, record);
|
||||
CommonUtil.saveConfig(configFile, pro);
|
||||
}
|
||||
|
@ -0,0 +1,9 @@
|
||||
package com.arialyy.aria.util;
|
||||
|
||||
/**
|
||||
* Created by Aria.Lao on 2017/1/11.
|
||||
*/
|
||||
|
||||
public class CAConfiguration {
|
||||
public static String CA_PATH, CA_ALIAS;
|
||||
}
|
112
Aria/src/main/java/com/arialyy/aria/util/SSLContextUtil.java
Normal file
112
Aria/src/main/java/com/arialyy/aria/util/SSLContextUtil.java
Normal file
@ -0,0 +1,112 @@
|
||||
package com.arialyy.aria.util;
|
||||
|
||||
import com.arialyy.aria.core.DownloadManager;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.UnrecoverableKeyException;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.KeyManagerFactory;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSession;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
|
||||
/**
|
||||
* Created by Aria.Lao on 2017/1/11.
|
||||
*/
|
||||
|
||||
public class SSLContextUtil {
|
||||
|
||||
/**
|
||||
* 颁发服务器证书的 CA 未知
|
||||
*
|
||||
* @param caAlias CA证书别名
|
||||
* @param caPath 保存在assets目录下的CA证书完整路径
|
||||
*/
|
||||
public static SSLContext getSSLContext(String caAlias, String caPath) {
|
||||
// Load CAs from an InputStream
|
||||
// (could be from a resource or ByteArrayInputStream or ...)
|
||||
CertificateFactory cf = null;
|
||||
try {
|
||||
cf = CertificateFactory.getInstance("X.509");
|
||||
InputStream caInput = DownloadManager.APP.getAssets().open(caPath);
|
||||
Certificate ca;
|
||||
ca = cf.generateCertificate(caInput);
|
||||
System.out.println("ca=" + ((X509Certificate) ca).getSubjectDN());
|
||||
|
||||
// Create a KeyStore containing our trusted CAs
|
||||
String keyStoreType = KeyStore.getDefaultType();
|
||||
KeyStore keyStore = KeyStore.getInstance(keyStoreType);
|
||||
keyStore.load(null, null);
|
||||
keyStore.setCertificateEntry(caAlias, ca);
|
||||
|
||||
// Create a TrustManager that trusts the CAs in our KeyStore
|
||||
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
|
||||
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
|
||||
tmf.init(keyStore);
|
||||
KeyManagerFactory kmf =
|
||||
KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
|
||||
kmf.init(keyStore, null);
|
||||
|
||||
// Create an SSLContext that uses our TrustManager
|
||||
SSLContext context = SSLContext.getInstance("TLS");
|
||||
context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), new SecureRandom());
|
||||
return context;
|
||||
} catch (CertificateException | NoSuchAlgorithmException | IOException | KeyStoreException | KeyManagementException | UnrecoverableKeyException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 服务器证书不是由 CA 签署的,而是自签署时,获取默认的SSL
|
||||
*/
|
||||
public static SSLContext getDefaultSLLContext() {
|
||||
SSLContext sslContext = null;
|
||||
try {
|
||||
sslContext = SSLContext.getInstance("TLS");
|
||||
sslContext.init(null, new TrustManager[] { trustManagers }, new SecureRandom());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return sslContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建自己的 TrustManager,这次直接信任服务器证书。这种方法具有前面所述的将应用与证书直接关联的所有弊端,但可以安全地操作。
|
||||
*/
|
||||
private static TrustManager trustManagers = new X509TrustManager() {
|
||||
|
||||
@Override public void checkClientTrusted(X509Certificate[] chain, String authType)
|
||||
throws CertificateException {
|
||||
}
|
||||
|
||||
@Override public void checkServerTrusted(X509Certificate[] chain, String authType)
|
||||
throws CertificateException {
|
||||
|
||||
}
|
||||
|
||||
@Override public X509Certificate[] getAcceptedIssuers() {
|
||||
return new X509Certificate[0];
|
||||
}
|
||||
};
|
||||
|
||||
public static final HostnameVerifier HOSTNAME_VERIFIER = new HostnameVerifier() {
|
||||
public boolean verify(String hostname, SSLSession session) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
Reference in New Issue
Block a user