ResearchLog uploading

- uploads files in the background to server

multi-project commit with Ie0d937773e04b2fbefc8d76c231aaa52ebc392c9

Bug: 6188932

Change-Id: I90bb0e237eeb567e4cbb51085f2229f17f1fe71c
main
Kurt Partridge 2012-07-18 17:05:48 -07:00
parent 6b966160ac
commit 9c539d5a5c
3 changed files with 251 additions and 1 deletions

22
java/res/values/urls.xml Normal file
View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
**
** Copyright 2012, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
** you may not use this file except in compliance with the License.
** You may obtain a copy of the License at
**
** http://www.apache.org/licenses/LICENSE-2.0
**
** Unless required by applicable law or agreed to in writing, software
** distributed under the License is distributed on an "AS IS" BASIS,
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<resources>
<string name="research_logger_upload_url" translatable="false"></string>
</resources>

View File

@ -0,0 +1,223 @@
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.android.inputmethod.research;
import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.BatteryManager;
import android.util.Log;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.R.string;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public final class ResearchLogUploader {
private static final String TAG = ResearchLogUploader.class.getSimpleName();
private static final int UPLOAD_INTERVAL_IN_MS = 1000 * 60 * 15; // every 15 min
private static final int BUF_SIZE = 1024 * 8;
private final boolean mCanUpload;
private final Context mContext;
private final File mFilesDir;
private final URL mUrl;
private final ScheduledExecutorService mExecutor;
private Runnable doUploadRunnable = new UploadRunnable(null, false);
public ResearchLogUploader(final Context context, final File filesDir) {
mContext = context;
mFilesDir = filesDir;
final PackageManager packageManager = context.getPackageManager();
final boolean hasPermission = packageManager.checkPermission(Manifest.permission.INTERNET,
context.getPackageName()) == PackageManager.PERMISSION_GRANTED;
if (!hasPermission) {
mCanUpload = false;
mUrl = null;
mExecutor = null;
return;
}
URL tempUrl = null;
boolean canUpload = false;
ScheduledExecutorService executor = null;
try {
final String urlString = context.getString(R.string.research_logger_upload_url);
if (urlString == null || urlString.equals("")) {
return;
}
tempUrl = new URL(urlString);
canUpload = true;
executor = Executors.newSingleThreadScheduledExecutor();
} catch (MalformedURLException e) {
tempUrl = null;
e.printStackTrace();
return;
} finally {
mCanUpload = canUpload;
mUrl = tempUrl;
mExecutor = executor;
}
}
public void start() {
if (mCanUpload) {
Log.d(TAG, "scheduling regular uploading");
mExecutor.scheduleWithFixedDelay(doUploadRunnable, UPLOAD_INTERVAL_IN_MS,
UPLOAD_INTERVAL_IN_MS, TimeUnit.MILLISECONDS);
} else {
Log.d(TAG, "no permission to upload");
}
}
public void uploadNow(final Callback callback) {
// Perform an immediate upload. Note that this should happen even if there is
// another upload happening right now, as it may have missed the latest changes.
// TODO: Reschedule regular upload tests starting from now.
if (mCanUpload) {
mExecutor.submit(new UploadRunnable(callback, true));
}
}
public interface Callback {
public void onUploadCompleted(final boolean success);
}
private boolean isExternallyPowered() {
final Intent intent = mContext.registerReceiver(null, new IntentFilter(
Intent.ACTION_BATTERY_CHANGED));
final int pluggedState = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
return pluggedState == BatteryManager.BATTERY_PLUGGED_AC
|| pluggedState == BatteryManager.BATTERY_PLUGGED_USB;
}
private boolean hasWifiConnection() {
final ConnectivityManager manager =
(ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
final NetworkInfo wifiInfo = manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
return wifiInfo.isConnected();
}
class UploadRunnable implements Runnable {
private final Callback mCallback;
private final boolean mForceUpload;
public UploadRunnable(final Callback callback, final boolean forceUpload) {
mCallback = callback;
mForceUpload = forceUpload;
}
@Override
public void run() {
doUpload();
}
private void doUpload() {
if (!mForceUpload && (!isExternallyPowered() || !hasWifiConnection())) {
return;
}
if (mFilesDir == null) {
return;
}
final File[] files = mFilesDir.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
return pathname.getName().startsWith(ResearchLogger.FILENAME_PREFIX)
&& !pathname.canWrite();
}
});
boolean success = true;
if (files.length == 0) {
success = false;
}
for (final File file : files) {
if (!uploadFile(file)) {
success = false;
}
}
if (mCallback != null) {
mCallback.onUploadCompleted(success);
}
}
private boolean uploadFile(File file) {
Log.d(TAG, "attempting upload of " + file.getAbsolutePath());
boolean success = false;
final int contentLength = (int) file.length();
HttpURLConnection connection = null;
InputStream fileIs = null;
try {
fileIs = new FileInputStream(file);
connection = (HttpURLConnection) mUrl.openConnection();
connection.setRequestMethod("PUT");
connection.setDoOutput(true);
connection.setFixedLengthStreamingMode(contentLength);
final OutputStream os = connection.getOutputStream();
final byte[] buf = new byte[BUF_SIZE];
int numBytesRead;
while ((numBytesRead = fileIs.read(buf)) != -1) {
os.write(buf, 0, numBytesRead);
}
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
Log.d(TAG, "upload failed: " + connection.getResponseCode());
InputStream netIs = connection.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(netIs));
String line;
while ((line = reader.readLine()) != null) {
Log.d(TAG, "| " + reader.readLine());
}
reader.close();
return success;
}
file.delete();
success = true;
Log.d(TAG, "upload successful");
} catch (Exception e) {
e.printStackTrace();
} finally {
if (fileIs != null) {
try {
fileIs.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (connection != null) {
connection.disconnect();
}
}
return success;
}
}
}

View File

@ -120,6 +120,8 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
private KeyboardSwitcher mKeyboardSwitcher; private KeyboardSwitcher mKeyboardSwitcher;
private Context mContext; private Context mContext;
private ResearchLogUploader mResearchLogUploader;
private ResearchLogger() { private ResearchLogger() {
} }
@ -158,6 +160,8 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
e.apply(); e.apply();
} }
} }
mResearchLogUploader = new ResearchLogUploader(ims, mFilesDir);
mResearchLogUploader.start();
mKeyboardSwitcher = keyboardSwitcher; mKeyboardSwitcher = keyboardSwitcher;
mContext = ims; mContext = ims;
mPrefs = prefs; mPrefs = prefs;
@ -327,7 +331,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
latinIME.getString(R.string.note_timestamp_for_researchlog), latinIME.getString(R.string.note_timestamp_for_researchlog),
showEnable ? latinIME.getString(R.string.enable_session_logging) : showEnable ? latinIME.getString(R.string.enable_session_logging) :
latinIME.getString(R.string.do_not_log_this_session), latinIME.getString(R.string.do_not_log_this_session),
latinIME.getString(R.string.log_whole_session_history), latinIME.getString(R.string.log_whole_session_history)
}; };
final DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() { final DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
@Override @Override
@ -536,6 +540,7 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang
return; return;
} }
if (mMainResearchLog == null) { if (mMainResearchLog == null) {
Log.w(TAG, "ResearchLog was not properly set up");
return; return;
} }
if (isPrivacySensitive) { if (isPrivacySensitive) {