Merge pull request #991 from mpcomplete/update.service

Introduce an UpdateService for android
This commit is contained in:
mpcomplete 2015-09-02 15:44:24 -04:00
commit c780e0cb13
9 changed files with 266 additions and 3 deletions

View File

@ -15,5 +15,9 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name="org.domokit.sky.shell.UpdateService"
android:exported="false"
android:process=":remote"/>
</application>
</manifest>
</manifest>

View File

@ -47,8 +47,11 @@ template("sky_app") {
sources = [
"$root_build_dir/icudtl.dat",
"$target_gen_dir/app.skyx",
"$target_gen_dir/app.skyx"
]
if (defined(invoker.manifest)) {
sources += [ invoker.manifest ]
}
deps = [
":$skyx_target_name",
":gen_${bundle_prefix}_bundle",

View File

@ -90,6 +90,7 @@ if (is_android) {
"android/org/domokit/sky/shell/PlatformViewAndroid.java",
"android/org/domokit/sky/shell/SkyMain.java",
"android/org/domokit/sky/shell/TracingController.java",
"android/org/domokit/sky/shell/UpdateService.java",
]
jni_package = "sky/shell"
}
@ -105,6 +106,7 @@ if (is_android) {
"android/sky_main.h",
"android/tracing_controller.cc",
"android/tracing_controller.h",
"android/update_service_android.cc",
]
deps = common_deps + [
@ -131,6 +133,7 @@ if (is_android) {
"android/org/domokit/sky/shell/SkyApplication.java",
"android/org/domokit/sky/shell/SkyMain.java",
"android/org/domokit/sky/shell/TracingController.java",
"android/org/domokit/sky/shell/UpdateService.java",
]
deps = [

View File

@ -21,5 +21,9 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name="UpdateService"
android:exported="false"
android:process=":remote"/>
</application>
</manifest>

View File

@ -15,6 +15,7 @@
#include "sky/shell/android/platform_view_android.h"
#include "sky/shell/android/sky_main.h"
#include "sky/shell/android/tracing_controller.h"
#include "sky/shell/android/update_service_android.h"
namespace {
@ -25,6 +26,7 @@ base::android::RegistrationMethod kSkyRegisteredMethods[] = {
{"PlatformViewAndroid", sky::shell::PlatformViewAndroid::Register},
{"SkyMain", sky::shell::RegisterSkyMain},
{"TracingController", sky::shell::RegisterTracingController},
{"UpdateService", sky::shell::RegisterUpdateService},
};
bool RegisterJNI(JNIEnv* env) {

View File

@ -34,10 +34,11 @@ import org.domokit.vsync.VSyncProviderImpl;
public class SkyApplication extends BaseChromiumApplication {
static final String SNAPSHOT = "snapshot_blob.bin";
static final String APP_BUNDLE = "app.skyx";
static final String MANIFEST = "sky.yaml";
private static final String TAG = "SkyApplication";
private static final String PRIVATE_DATA_DIRECTORY_SUFFIX = "sky_shell";
private static final String[] SKY_RESOURCES = {"icudtl.dat", SNAPSHOT, APP_BUNDLE};
private static final String[] SKY_RESOURCES = {"icudtl.dat", SNAPSHOT, APP_BUNDLE, MANIFEST};
private ResourceExtractor mResourceExtractor;
@ -51,6 +52,7 @@ public class SkyApplication extends BaseChromiumApplication {
initJavaUtils();
initResources();
initNative();
UpdateService.init(getApplicationContext());
onServiceRegistryAvailable(ServiceRegistry.SHARED);
}

View File

@ -0,0 +1,86 @@
// 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.
package org.domokit.sky.shell;
import android.app.AlarmManager;
import android.app.Service;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
import java.io.File;
import org.chromium.base.CalledByNative;
import org.chromium.base.JNINamespace;
import org.chromium.base.PathUtils;
/**
* A class that schedules and runs periodic autoupdate checks.
*/
@JNINamespace("sky::shell")
public class UpdateService extends Service {
private static final String TAG = "UpdateService";
private static final int REQUEST_CODE = 0; // Not sure why this is needed.
private static final boolean ENABLED = false;
private long mNativePtr = 0;
public static void init(Context context) {
if (ENABLED)
maybeScheduleUpdateCheck(context);
}
private static void maybeScheduleUpdateCheck(Context context) {
Intent alarm = new Intent(context, UpdateService.class);
PendingIntent existingIntent = PendingIntent.getService(
context, REQUEST_CODE, alarm, PendingIntent.FLAG_NO_CREATE);
if (existingIntent != null) {
Log.i(TAG, "Update alarm exists: " + PathUtils.getDataDirectory(context));
return;
}
PendingIntent pendingIntent = PendingIntent.getService(
context, REQUEST_CODE, alarm, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager manager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
manager.setInexactRepeating(AlarmManager.RTC, System.currentTimeMillis(),
AlarmManager.INTERVAL_DAY, pendingIntent);
Log.i(TAG, "Update scheduled: " + PathUtils.getDataDirectory(context));
}
@Override
public void onCreate() {
super.onCreate();
SkyMain.ensureInitialized(getApplicationContext(), null);
}
@Override
public void onDestroy() {
if (mNativePtr != 0)
nativeDetach(mNativePtr);
mNativePtr = 0;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
final File dataDir = new File(PathUtils.getDataDirectory(this));
mNativePtr = nativeCheckForUpdates(dataDir.getPath());
return START_NOT_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@SuppressWarnings("unused")
@CalledByNative
public void onUpdateFinished() {
stopSelf();
}
private native long nativeCheckForUpdates(String dataDir);
private native void nativeDetach(long nativeUpdateTask);
}

View File

@ -0,0 +1,118 @@
// 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/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");
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")) {
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)));
}
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() {
// The Java side is responsible for deleting the UpdateTask when finished.
Java_UpdateService_onUpdateFinished(base::android::AttachCurrentThread(),
update_service_.obj());
}
} // namespace shell
} // namespace sky

View File

@ -0,0 +1,41 @@
// 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_UPDATE_SERVICE_H_
#define SKY_SHELL_UPDATE_SERVICE_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"
namespace sky {
namespace shell {
class UpdateTask {
public:
UpdateTask(JNIEnv* env, jobject update_service, std::string data_dir);
void DownloadAppBundle(const std::string& url);
void Detach(JNIEnv* env, jobject jcaller);
private:
void OnResponse(mojo::URLResponsePtr response);
void OnCopied(bool success);
void CallOnFinished();
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_