Skip to content

Commit

Permalink
Merge pull request flutter#1109 from mpcomplete/version.check
Browse files Browse the repository at this point in the history
Compare versions before updating an app bundle.
  • Loading branch information
mpcomplete committed Sep 10, 2015
2 parents 5881fc1 + 2994380 commit 2c7f0bd
Show file tree
Hide file tree
Showing 9 changed files with 342 additions and 106 deletions.
3 changes: 3 additions & 0 deletions examples/fitness/sky.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
name: fitness
version: 0.0.1
update_url: http://localhost:9888/examples/fitness/
material-design-icons:
- name: action/assessment
- name: action/help
Expand Down
5 changes: 5 additions & 0 deletions sky/shell/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ common_deps = [
"//sky/shell/dart",
"//ui/gfx/geometry",
"//ui/gl",
"//url:url",
]

if (is_linux || is_mac) {
Expand Down Expand Up @@ -76,6 +77,10 @@ source_set("common") {
"ui/platform_impl.h",
"ui_delegate.cc",
"ui_delegate.h",
"updater/manifest.cc",
"updater/manifest.h",
"updater/update_task.cc",
"updater/update_task.h",
]

deps = common_deps
Expand Down
4 changes: 2 additions & 2 deletions sky/shell/android/org/domokit/sky/shell/UpdateService.java
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public void onCreate() {
@Override
public void onDestroy() {
if (mNativePtr != 0)
nativeDetach(mNativePtr);
nativeDestroy(mNativePtr);
mNativePtr = 0;
}

Expand All @@ -82,5 +82,5 @@ public void onUpdateFinished() {
}

private native long nativeCheckForUpdates(String dataDir);
private native void nativeDetach(long nativeUpdateTask);
private native void nativeDestroy(long nativeUpdateTaskAndroid);
}
101 changes: 14 additions & 87 deletions sky/shell/android/update_service_android.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,115 +4,42 @@

#include "sky/shell/android/update_service_android.h"

#include "base/android/jni_string.h"
#include "base/bind.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/threading/worker_pool.h"
#include "jni/UpdateService_jni.h"
#include "mojo/data_pipe_utils/data_pipe_utils.h"
#include "mojo/public/cpp/application/connect.h"
#include "mojo/public/interfaces/application/service_provider.mojom.h"
#include "mojo/services/network/public/interfaces/network_service.mojom.h"
#include "sky/shell/shell.h"

namespace sky {
namespace shell {

namespace {

// TODO(mpcomplete): make this a utility method?
static mojo::URLLoaderPtr FetchURL(
mojo::NetworkService* network_service,
const std::string& url,
base::Callback<void(mojo::URLResponsePtr)> callback) {
mojo::URLLoaderPtr loader;
network_service->CreateURLLoader(GetProxy(&loader));

mojo::URLRequestPtr request = mojo::URLRequest::New();
request->url = url;
request->auto_follow_redirects = true;
loader->Start(request.Pass(), callback);

return loader.Pass();
}

} // namespace

static jlong CheckForUpdates(JNIEnv* env, jobject jcaller, jstring j_data_dir) {
std::string data_dir = base::android::ConvertJavaStringToUTF8(env, j_data_dir);
scoped_ptr<UpdateTask> task(new UpdateTask(env, jcaller, data_dir));

// TODO(mpcomplete): download a manifest and check the version.
task->DownloadAppBundle("file://" + data_dir + "/app.skyx");

std::string data_dir =
base::android::ConvertJavaStringToUTF8(env, j_data_dir);
scoped_ptr<UpdateTask> task(new UpdateTaskAndroid(env, jcaller, data_dir));
task->Start();
return reinterpret_cast<jlong>(task.release());
}

bool RegisterUpdateService(JNIEnv* env) {
return RegisterNativesImpl(env);
}

UpdateTask::UpdateTask(JNIEnv* env, jobject update_service, std::string data_dir)
: final_path_(base::FilePath(data_dir).AppendASCII("app.skyx")) {
UpdateTaskAndroid::UpdateTaskAndroid(JNIEnv* env,
jobject update_service,
const std::string& data_dir)
: UpdateTask(data_dir) {
update_service_.Reset(env, update_service);

mojo::ServiceProviderPtr service_provider =
CreateServiceProvider(Shell::Shared().service_provider_context());
mojo::ConnectToService(service_provider.get(), &network_service_);
}

void UpdateTask::DownloadAppBundle(const std::string& url) {
LOG(INFO) << "Update downloading " << url << " to: " << final_path_.value();
if (!base::CreateTemporaryFile(&temp_path_)) {
CallOnFinished();
return;
}

url_loader_ =
FetchURL(network_service_.get(), url,
base::Bind(&UpdateTask::OnResponse, base::Unretained(this)));
UpdateTaskAndroid::~UpdateTaskAndroid() {
}

void UpdateTask::Detach(JNIEnv* env, jobject jcaller) {
delete this;
}

void UpdateTask::OnResponse(mojo::URLResponsePtr response) {
mojo::ScopedDataPipeConsumerHandle data;
if (response->status_code == 200)
data = response->body.Pass();
if (!data.is_valid()) {
LOG(ERROR) << "Update failed: Server responded " << response->status_code;
CallOnFinished();
return;
}

scoped_refptr<base::TaskRunner> worker_runner =
base::WorkerPool::GetTaskRunner(true);
mojo::common::CopyToFile(
data.Pass(), temp_path_, worker_runner.get(),
base::Bind(&UpdateTask::OnCopied, base::Unretained(this)));
}

void UpdateTask::OnCopied(bool success) {
int64 size = 0;
GetFileSize(temp_path_, &size);

// This assumes temp files are created on the same volume as the data_dir.
// TODO(mpcomplete): only replace on next startup. Otherwise, a currently
// running version of the app will get confused.
bool rv = base::ReplaceFile(temp_path_, final_path_, nullptr);
LOG(INFO) << "Update finished: " << rv << " filesize(" << final_path_.value() << ")=" << size;

CallOnFinished();
}

void UpdateTask::CallOnFinished() {
void UpdateTaskAndroid::Finish() {
// The Java side is responsible for deleting the UpdateTask when finished.
Java_UpdateService_onUpdateFinished(base::android::AttachCurrentThread(),
update_service_.obj());
}

void UpdateTaskAndroid::Destroy(JNIEnv* env, jobject jcaller) {
delete this;
}

} // namespace shell
} // namespace sky
31 changes: 14 additions & 17 deletions sky/shell/android/update_service_android.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,37 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef SKY_SHELL_UPDATE_SERVICE_H_
#define SKY_SHELL_UPDATE_SERVICE_H_
#ifndef SKY_SHELL_ANDROID_UPDATE_SERVICE_ANDROID_H_
#define SKY_SHELL_ANDROID_UPDATE_SERVICE_ANDROID_H_

#include <jni.h>

#include "base/android/jni_string.h"
#include "base/files/file_path.h"
#include "mojo/services/network/public/interfaces/network_service.mojom.h"
#include "sky/shell/updater/update_task.h"

namespace sky {
namespace shell {

class UpdateTask {
class UpdateTaskAndroid : public UpdateTask {
public:
UpdateTask(JNIEnv* env, jobject update_service, std::string data_dir);
UpdateTaskAndroid(JNIEnv* env,
jobject update_service,
const std::string& data_dir);
~UpdateTaskAndroid();

void DownloadAppBundle(const std::string& url);
void Detach(JNIEnv* env, jobject jcaller);
void Finish() override;

private:
void OnResponse(mojo::URLResponsePtr response);
void OnCopied(bool success);
void CallOnFinished();
// This C++ object is owned by the Java UpdateService. This is called by
// UpdateService when it is destroyed.
void Destroy(JNIEnv* env, jobject jcaller);

private:
base::android::ScopedJavaGlobalRef<jobject> update_service_;
mojo::NetworkServicePtr network_service_;
base::FilePath temp_path_;
base::FilePath final_path_;
mojo::URLLoaderPtr url_loader_;
};

bool RegisterUpdateService(JNIEnv* env);

} // namespace shell
} // namespace sky

#endif // SKY_SHELL_UPDATE_SERVICE_H_
#endif // SKY_SHELL_ANDROID_UPDATE_SERVICE_ANDROID_H_
33 changes: 33 additions & 0 deletions sky/shell/updater/manifest.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "sky/shell/updater/manifest.h"

#include "base/strings/string_split.h"
#include "base/strings/string_util.h"

namespace sky {
namespace shell {

// static
scoped_ptr<Manifest> Manifest::Parse(const std::string& manifest_data) {
scoped_ptr<Manifest> manifest(new Manifest);

// Poor man's YAML parsing.
// TODO(mpcomplete): use an actual YAML parser.
base::StringPairs key_values;
base::SplitStringIntoKeyValuePairs(manifest_data, ':', '\n', &key_values);
for (const auto& key_value : key_values) {
auto value = base::TrimWhitespaceASCII(key_value.second, base::TRIM_ALL);
if (key_value.first == "update_url") {
manifest->update_url_ = GURL(value.as_string());
} else if (key_value.first == "version") {
manifest->version_ = base::Version(value.as_string());
}
}
return manifest;
}

} // namespace shell
} // namespace sky
34 changes: 34 additions & 0 deletions sky/shell/updater/manifest.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef SKY_SHELL_MANIFEST_H_
#define SKY_SHELL_MANIFEST_H_

#include <string>

#include "base/memory/scoped_ptr.h"
#include "base/version.h"
#include "url/gurl.h"

namespace sky {
namespace shell {

struct Manifest {
public:
static scoped_ptr<Manifest> Parse(const std::string& manifest_data);

bool IsValid() const { return version_.IsValid(); }

const GURL& update_url() const { return update_url_; }
const base::Version& version() const { return version_; }

private:
GURL update_url_;
base::Version version_;
};

} // namespace shell
} // namespace sky

#endif // SKY_SHELL_MANIFEST_H_
Loading

0 comments on commit 2c7f0bd

Please sign in to comment.