Commit 1a33370f authored by John Abd-El-Malek's avatar John Abd-El-Malek Committed by Commit Bot

Resume interrupted downloads on restart in WebLayer.

Bug: 1025603
Change-Id: Ie2ef10c03b9ffea77436c0932727d1545fcf895f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2132540Reviewed-by: default avatarScott Violet <sky@chromium.org>
Reviewed-by: default avatarMin Qin <qinmin@chromium.org>
Commit-Queue: John Abd-El-Malek <jam@chromium.org>
Cr-Commit-Position: refs/heads/master@{#755922}
parent 4964f415
......@@ -23,6 +23,7 @@
#include "content/public/browser/device_service.h"
#include "content/public/browser/download_request_utils.h"
#include "content/public/browser/resource_context.h"
#include "content/public/browser/storage_partition.h"
#include "weblayer/browser/permissions/permission_manager_factory.h"
#include "weblayer/browser/stateful_ssl_host_state_delegate_factory.h"
#include "weblayer/public/common/switches.h"
......@@ -181,7 +182,10 @@ download::InProgressDownloadManager*
BrowserContextImpl::RetriveInProgressDownloadManager() {
// Override this to provide a connection to the wake lock service.
auto* download_manager = new download::InProgressDownloadManager(
nullptr, base::FilePath(), nullptr,
nullptr, path_,
path_.empty()
? nullptr
: GetDefaultStoragePartition(this)->GetProtoDatabaseProvider(),
base::BindRepeating(&IgnoreOriginSecurityCheck),
base::BindRepeating(&content::DownloadRequestUtils::IsURLSafe),
base::BindRepeating(&BindWakeLockProvider));
......
......@@ -73,8 +73,10 @@ DownloadState DownloadImpl::GetState() {
if (pause_pending_ || (item_->IsPaused() && !resume_pending_))
return DownloadState::kPaused;
if (item_->GetState() == download::DownloadItem::IN_PROGRESS)
if (resume_pending_ ||
item_->GetState() == download::DownloadItem::IN_PROGRESS) {
return DownloadState::kInProgress;
}
return DownloadState::kFailed;
}
......
......@@ -54,6 +54,12 @@ DownloadManagerDelegateImpl::DownloadManagerDelegateImpl(
content::DownloadManager* download_manager)
: download_manager_(download_manager) {
download_manager_->AddObserver(this);
// WebLayer doesn't use a history DB as the in-progress database maintained by
// the download component is enough. However the download code still depends
// this notification. TODO(jam): update download code to handle this.
download_manager_->PostInitialization(
content::DownloadManager::DOWNLOAD_INITIALIZATION_DEPENDENCY_HISTORY_DB);
}
DownloadManagerDelegateImpl::~DownloadManagerDelegateImpl() {
......@@ -172,6 +178,13 @@ void DownloadManagerDelegateImpl::OnDownloadCreated(
local_state->SetInteger(kDownloadNextIDPref, next_id);
}
if (item->GetLastReason() == download::DOWNLOAD_INTERRUPT_REASON_CRASH &&
item->CanResume() &&
// Don't automatically resume downloads which were previously paused.
!item->IsPaused()) {
DownloadImpl::Get(item)->Resume();
}
auto* delegate = GetDelegate(item);
if (delegate)
delegate->DownloadStarted(DownloadImpl::Get(item));
......@@ -183,6 +196,13 @@ void DownloadManagerDelegateImpl::OnDownloadDropped(
download_dropped_callback_.Run();
}
void DownloadManagerDelegateImpl::OnManagerInitialized() {
auto* browser_context_impl =
static_cast<BrowserContextImpl*>(download_manager_->GetBrowserContext());
auto* profile = browser_context_impl->profile_impl();
profile->DownloadsInitialized();
}
void DownloadManagerDelegateImpl::OnDownloadUpdated(
download::DownloadItem* item) {
auto* delegate = GetDelegate(item);
......
......@@ -60,6 +60,7 @@ class DownloadManagerDelegateImpl : public content::DownloadManagerDelegate,
void OnDownloadCreated(content::DownloadManager* manager,
download::DownloadItem* item) override;
void OnDownloadDropped(content::DownloadManager* manager) override;
void OnManagerInitialized() override;
// download::DownloadItem::Observer implementation:
void OnDownloadUpdated(download::DownloadItem* item) override;
......
......@@ -21,17 +21,16 @@ import org.chromium.weblayer_private.interfaces.ObjectWrapper;
@JNINamespace("weblayer")
public final class DownloadCallbackProxy {
private long mNativeDownloadCallbackProxy;
private String mProfileName;
private IDownloadCallbackClient mClient;
DownloadCallbackProxy(long profile, IDownloadCallbackClient client) {
assert client != null;
mClient = client;
DownloadCallbackProxy(String profileName, long profile) {
mProfileName = profileName;
mNativeDownloadCallbackProxy =
DownloadCallbackProxyJni.get().createDownloadCallbackProxy(this, profile);
}
public void setClient(IDownloadCallbackClient client) {
assert client != null;
mClient = client;
}
......@@ -43,6 +42,10 @@ public final class DownloadCallbackProxy {
@CalledByNative
private boolean interceptDownload(String url, String userAgent, String contentDisposition,
String mimetype, long contentLength) throws RemoteException {
if (mClient == null) {
return true;
}
return mClient.interceptDownload(
url, userAgent, contentDisposition, mimetype, contentLength);
}
......@@ -55,6 +58,11 @@ public final class DownloadCallbackProxy {
return;
}
if (mClient == null) {
DownloadCallbackProxyJni.get().allowDownload(callbackId, false);
return;
}
ValueCallback<Boolean> callback = new ValueCallback<Boolean>() {
@Override
public void onReceiveValue(Boolean result) {
......@@ -70,30 +78,38 @@ public final class DownloadCallbackProxy {
@CalledByNative
private DownloadImpl createDownload(long nativeDownloadImpl, int id) {
return new DownloadImpl(mClient, nativeDownloadImpl, id);
return new DownloadImpl(mProfileName, mClient, nativeDownloadImpl, id);
}
@CalledByNative
private void downloadStarted(DownloadImpl download) throws RemoteException {
mClient.downloadStarted(download.getClientDownload());
if (mClient != null) {
mClient.downloadStarted(download.getClientDownload());
}
download.downloadStarted();
}
@CalledByNative
private void downloadProgressChanged(DownloadImpl download) throws RemoteException {
mClient.downloadProgressChanged(download.getClientDownload());
if (mClient != null) {
mClient.downloadProgressChanged(download.getClientDownload());
}
download.downloadProgressChanged();
}
@CalledByNative
private void downloadCompleted(DownloadImpl download) throws RemoteException {
mClient.downloadCompleted(download.getClientDownload());
if (mClient != null) {
mClient.downloadCompleted(download.getClientDownload());
}
download.downloadCompleted();
}
@CalledByNative
private void downloadFailed(DownloadImpl download) throws RemoteException {
mClient.downloadFailed(download.getClientDownload());
if (mClient != null) {
mClient.downloadFailed(download.getClientDownload());
}
download.downloadFailed();
}
......
......@@ -51,11 +51,14 @@ public final class DownloadImpl extends IDownload.Stub {
"org.chromium.weblayer.downloads.NOTIFICATION_LOCATION";
static final String EXTRA_NOTIFICATION_MIME_TYPE =
"org.chromium.weblayer.downloads.NOTIFICATION_MIME_TYPE";
static final String EXTRA_NOTIFICATION_PROFILE =
"org.chromium.weblayer.downloads.NOTIFICATION_PROFILE";
static final String PREF_NEXT_NOTIFICATION_ID =
"org.chromium.weblayer.downloads.notification_next_id";
private static final String CHANNEL_ID = "org.chromium.weblayer.downloads.channel";
private static final String TAG = "DownloadImpl";
private final String mProfileName;
private final IDownloadCallbackClient mClient;
private final IClientDownload mClientDownload;
// WARNING: DownloadImpl may outlive the native side, in which case this member is set to 0.
......@@ -67,7 +70,8 @@ public final class DownloadImpl extends IDownload.Stub {
private static boolean sCreatedChannel = false;
private static final HashMap<Integer, DownloadImpl> sMap = new HashMap<Integer, DownloadImpl>();
public static void forwardIntent(Context context, Intent intent) {
public static void forwardIntent(
Context context, Intent intent, ProfileManager profileManager) {
if (intent.getAction().equals(OPEN_INTENT)) {
String location = intent.getStringExtra(EXTRA_NOTIFICATION_LOCATION);
if (TextUtils.isEmpty(location)) {
......@@ -96,6 +100,17 @@ public final class DownloadImpl extends IDownload.Stub {
return;
}
String profileName = intent.getStringExtra(EXTRA_NOTIFICATION_PROFILE);
ProfileImpl profile = profileManager.getProfile(profileName);
if (!profile.areDownloadsInitialized()) {
profile.addDownloadNotificationIntent(intent);
} else {
handleIntent(intent);
}
}
public static void handleIntent(Intent intent) {
int id = intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1);
DownloadImpl download = sMap.get(id);
if (download == null) {
......@@ -131,14 +146,20 @@ public final class DownloadImpl extends IDownload.Stub {
return nextId;
}
public DownloadImpl(IDownloadCallbackClient client, long nativeDownloadImpl, int id) {
public DownloadImpl(
String profileName, IDownloadCallbackClient client, long nativeDownloadImpl, int id) {
mProfileName = profileName;
mClient = client;
mNativeDownloadImpl = nativeDownloadImpl;
mNotificationId = id;
try {
mClientDownload = client.createClientDownload(this);
} catch (RemoteException e) {
throw new APICallException(e);
if (mClient == null) {
mClientDownload = null;
} else {
try {
mClientDownload = mClient.createClientDownload(this);
} catch (RemoteException e) {
throw new APICallException(e);
}
}
DownloadImplJni.get().setJavaDownload(mNativeDownloadImpl, DownloadImpl.this);
}
......@@ -305,6 +326,7 @@ public final class DownloadImpl extends IDownload.Stub {
Intent deleteIntent = createIntent();
deleteIntent.setAction(DELETE_INTENT);
deleteIntent.putExtra(EXTRA_NOTIFICATION_ID, mNotificationId);
deleteIntent.putExtra(EXTRA_NOTIFICATION_PROFILE, mProfileName);
PendingIntent deletePendingIntent = PendingIntent.getBroadcast(
ContextUtils.getApplicationContext(), mNotificationId, deleteIntent, 0);
......@@ -383,6 +405,7 @@ public final class DownloadImpl extends IDownload.Stub {
Intent openIntent = createIntent();
openIntent.setAction(OPEN_INTENT);
openIntent.putExtra(EXTRA_NOTIFICATION_ID, mNotificationId);
openIntent.putExtra(EXTRA_NOTIFICATION_PROFILE, mProfileName);
openIntent.putExtra(EXTRA_NOTIFICATION_LOCATION, getLocation());
openIntent.putExtra(EXTRA_NOTIFICATION_MIME_TYPE, getMimeType());
PendingIntent openPendingIntent = PendingIntent.getBroadcast(
......@@ -402,6 +425,7 @@ public final class DownloadImpl extends IDownload.Stub {
Intent pauseIntent = createIntent();
pauseIntent.setAction(PAUSE_INTENT);
pauseIntent.putExtra(EXTRA_NOTIFICATION_ID, mNotificationId);
pauseIntent.putExtra(EXTRA_NOTIFICATION_PROFILE, mProfileName);
PendingIntent pausePendingIntent = PendingIntent.getBroadcast(
ContextUtils.getApplicationContext(), mNotificationId, pauseIntent, 0);
mBuilder.addAction(0 /* no icon */, "Pause", pausePendingIntent)
......@@ -410,6 +434,7 @@ public final class DownloadImpl extends IDownload.Stub {
Intent resumeIntent = createIntent();
resumeIntent.setAction(RESUME_INTENT);
resumeIntent.putExtra(EXTRA_NOTIFICATION_ID, mNotificationId);
resumeIntent.putExtra(EXTRA_NOTIFICATION_PROFILE, mProfileName);
PendingIntent resumePendingIntent = PendingIntent.getBroadcast(
ContextUtils.getApplicationContext(), mNotificationId, resumeIntent, 0);
mBuilder.addAction(0 /* no icon */, "Resume", resumePendingIntent)
......@@ -420,6 +445,7 @@ public final class DownloadImpl extends IDownload.Stub {
Intent cancelIntent = createIntent();
cancelIntent.setAction(CANCEL_INTENT);
cancelIntent.putExtra(EXTRA_NOTIFICATION_ID, mNotificationId);
cancelIntent.putExtra(EXTRA_NOTIFICATION_PROFILE, mProfileName);
PendingIntent cancelPendingIntent = PendingIntent.getBroadcast(
ContextUtils.getApplicationContext(), mNotificationId, cancelIntent, 0);
mBuilder.addAction(0 /* no icon */, "Cancel", cancelPendingIntent);
......
......@@ -4,12 +4,14 @@
package org.chromium.weblayer_private;
import android.content.Intent;
import android.webkit.ValueCallback;
import androidx.annotation.NonNull;
import org.chromium.base.Callback;
import org.chromium.base.CollectionUtil;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.JNINamespace;
import org.chromium.base.annotations.NativeMethods;
import org.chromium.weblayer_private.interfaces.BrowsingDataType;
......@@ -33,7 +35,9 @@ public final class ProfileImpl extends IProfile.Stub {
private CookieManagerImpl mCookieManager;
private Runnable mOnDestroyCallback;
private boolean mBeingDeleted;
private boolean mDownloadsInitialized;
private DownloadCallbackProxy mDownloadCallbackProxy;
private List<Intent> mDownloadNotificationIntents = new ArrayList<>();
public static void enumerateAllProfileNames(ValueCallback<String[]> callback) {
final Callback<String[]> baseCallback = (String[] names) -> callback.onReceiveValue(names);
......@@ -45,10 +49,11 @@ public final class ProfileImpl extends IProfile.Stub {
throw new IllegalArgumentException("Name can only contain words: " + name);
}
mName = name;
mNativeProfile = ProfileImplJni.get().createProfile(name);
mNativeProfile = ProfileImplJni.get().createProfile(name, ProfileImpl.this);
mCookieManager =
new CookieManagerImpl(ProfileImplJni.get().getCookieManager(mNativeProfile));
mOnDestroyCallback = onDestroyCallback;
mDownloadCallbackProxy = new DownloadCallbackProxy(mName, mNativeProfile);
}
@Override
......@@ -108,6 +113,15 @@ public final class ProfileImpl extends IProfile.Stub {
return mName.isEmpty();
}
public boolean areDownloadsInitialized() {
return mDownloadsInitialized;
}
public void addDownloadNotificationIntent(Intent intent) {
mDownloadNotificationIntents.add(intent);
ProfileImplJni.get().ensureBrowserContextInitialized(mNativeProfile);
}
@Override
public void clearBrowsingData(@NonNull @BrowsingDataType int[] dataTypes, long fromMillis,
long toMillis, @NonNull IObjectWrapper completionCallback) {
......@@ -127,17 +141,7 @@ public final class ProfileImpl extends IProfile.Stub {
@Override
public void setDownloadCallbackClient(IDownloadCallbackClient client) {
StrictModeWorkaround.apply();
if (client != null) {
if (mDownloadCallbackProxy == null) {
mDownloadCallbackProxy = new DownloadCallbackProxy(mNativeProfile, client);
} else {
mDownloadCallbackProxy.setClient(client);
}
} else if (mDownloadCallbackProxy != null) {
mDownloadCallbackProxy.destroy();
mDownloadCallbackProxy = null;
}
mDownloadCallbackProxy.setClient(client);
}
@Override
......@@ -176,15 +180,26 @@ public final class ProfileImpl extends IProfile.Stub {
return mNativeProfile;
}
@CalledByNative
public void downloadsInitialized() {
mDownloadsInitialized = true;
for (Intent intent : mDownloadNotificationIntents) {
DownloadImpl.handleIntent(intent);
}
mDownloadNotificationIntents.clear();
}
@NativeMethods
interface Natives {
void enumerateAllProfileNames(Callback<String[]> callback);
long createProfile(String name);
long createProfile(String name, ProfileImpl caller);
void deleteProfile(long profile);
boolean deleteDataFromDisk(long nativeProfileImpl, Runnable completionCallback);
void clearBrowsingData(long nativeProfileImpl, @ImplBrowsingDataType int[] dataTypes,
long fromMillis, long toMillis, Runnable callback);
void setDownloadDirectory(long nativeProfileImpl, String directory);
long getCookieManager(long nativeProfileImpl);
void ensureBrowserContextInitialized(long nativeProfileImpl);
}
}
......@@ -308,7 +308,7 @@ public final class WebLayerImpl extends IWebLayer.Stub {
public void onReceivedDownloadNotification(IObjectWrapper appContextWrapper, Intent intent) {
StrictModeWorkaround.apply();
Context context = ObjectWrapper.unwrap(appContextWrapper, Context.class);
DownloadImpl.forwardIntent(context, intent);
DownloadImpl.forwardIntent(context, intent, mProfileManager);
}
@Override
......
......@@ -222,6 +222,13 @@ content::BrowserContext* ProfileImpl::GetBrowserContext() {
return browser_context_.get();
}
void ProfileImpl::DownloadsInitialized() {
#if defined(OS_ANDROID)
return Java_ProfileImpl_downloadsInitialized(
base::android::AttachCurrentThread(), java_profile_);
#endif
}
bool ProfileImpl::DeleteDataFromDisk(base::OnceClosure done_callback) {
if (num_browser_impl_ > 0)
return false;
......@@ -309,14 +316,19 @@ std::unique_ptr<Profile> Profile::Create(const std::string& name) {
}
#if defined(OS_ANDROID)
ProfileImpl::ProfileImpl(JNIEnv* env,
const base::android::JavaParamRef<jstring>& name)
: ProfileImpl(ConvertJavaStringToUTF8(env, name)) {}
ProfileImpl::ProfileImpl(
JNIEnv* env,
const base::android::JavaParamRef<jstring>& name,
const base::android::JavaParamRef<jobject>& java_profile)
: ProfileImpl(ConvertJavaStringToUTF8(env, name)) {
java_profile_ = java_profile;
}
static jlong JNI_ProfileImpl_CreateProfile(
JNIEnv* env,
const base::android::JavaParamRef<jstring>& name) {
return reinterpret_cast<jlong>(new ProfileImpl(env, name));
const base::android::JavaParamRef<jstring>& name,
const base::android::JavaParamRef<jobject>& java_profile) {
return reinterpret_cast<jlong>(new ProfileImpl(env, name, java_profile));
}
static void JNI_ProfileImpl_DeleteProfile(JNIEnv* env, jlong profile) {
......@@ -377,6 +389,9 @@ jlong ProfileImpl::GetCookieManager(JNIEnv* env) {
return reinterpret_cast<jlong>(GetCookieManager());
}
void ProfileImpl::EnsureBrowserContextInitialized(JNIEnv* env) {
content::BrowserContext::GetDownloadManager(GetBrowserContext());
}
#endif // OS_ANDROID
void ProfileImpl::IncrementBrowserImplCount() {
......
......@@ -43,6 +43,11 @@ class ProfileImpl : public Profile {
content::BrowserContext* GetBrowserContext();
// Called when the download subsystem has finished initializing. By this point
// information about downloads that were interrupted by a previous crash would
// be available.
void DownloadsInitialized();
// Path data is stored at, empty if off-the-record.
const base::FilePath& data_path() const { return data_path_; }
DownloadDelegate* download_delegate() { return download_delegate_; }
......@@ -58,7 +63,9 @@ class ProfileImpl : public Profile {
CookieManager* GetCookieManager() override;
#if defined(OS_ANDROID)
ProfileImpl(JNIEnv* env, const base::android::JavaParamRef<jstring>& path);
ProfileImpl(JNIEnv* env,
const base::android::JavaParamRef<jstring>& path,
const base::android::JavaParamRef<jobject>& java_profile);
jboolean DeleteDataFromDisk(
JNIEnv* env,
......@@ -73,6 +80,7 @@ class ProfileImpl : public Profile {
JNIEnv* env,
const base::android::JavaParamRef<jstring>& directory);
jlong GetCookieManager(JNIEnv* env);
void EnsureBrowserContextInitialized(JNIEnv* env);
#endif
void IncrementBrowserImplCount();
......@@ -107,6 +115,10 @@ class ProfileImpl : public Profile {
size_t num_browser_impl_ = 0u;
#if defined(OS_ANDROID)
base::android::ScopedJavaGlobalRef<jobject> java_profile_;
#endif
DISALLOW_COPY_AND_ASSIGN(ProfileImpl);
};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment