1b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge/*
2b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge * Copyright (C) 2012 The Android Open Source Project
3b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge *
4b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge * use this file except in compliance with the License. You may obtain a copy of
6b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge * the License at
7b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge *
8b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge * http://www.apache.org/licenses/LICENSE-2.0
9b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge *
10b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge * Unless required by applicable law or agreed to in writing, software
11b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge * License for the specific language governing permissions and limitations under
14b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge * the License.
15b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge */
16b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge
17b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridgepackage com.android.inputmethod.research;
18b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge
19b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridgeimport android.Manifest;
20b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridgeimport android.app.AlarmManager;
21b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridgeimport android.app.IntentService;
22b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridgeimport android.content.Context;
23b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridgeimport android.content.Intent;
24b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridgeimport android.content.IntentFilter;
25b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridgeimport android.content.pm.PackageManager;
26b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridgeimport android.net.ConnectivityManager;
27b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridgeimport android.net.NetworkInfo;
28b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridgeimport android.os.BatteryManager;
29b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridgeimport android.os.Bundle;
30b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridgeimport android.util.Log;
31b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge
32b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridgeimport com.android.inputmethod.latin.R;
33b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge
34b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridgeimport java.io.BufferedReader;
35b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridgeimport java.io.File;
36b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridgeimport java.io.FileFilter;
37b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridgeimport java.io.FileInputStream;
38b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridgeimport java.io.IOException;
39b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridgeimport java.io.InputStream;
40b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridgeimport java.io.InputStreamReader;
41b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridgeimport java.io.OutputStream;
42b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridgeimport java.net.HttpURLConnection;
43b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridgeimport java.net.MalformedURLException;
44b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridgeimport java.net.URL;
45b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge
46b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridgepublic final class UploaderService extends IntentService {
47b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge    private static final String TAG = UploaderService.class.getSimpleName();
48b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge    public static final long RUN_INTERVAL = AlarmManager.INTERVAL_HOUR;
49b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge    private static final String EXTRA_UPLOAD_UNCONDITIONALLY = UploaderService.class.getName()
50b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge            + ".extra.UPLOAD_UNCONDITIONALLY";
51b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge    private static final int BUF_SIZE = 1024 * 8;
52b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge    protected static final int TIMEOUT_IN_MS = 1000 * 4;
53b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge
54b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge    private boolean mCanUpload;
55b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge    private File mFilesDir;
56b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge    private URL mUrl;
57b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge
58b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge    public UploaderService() {
59b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge        super("Research Uploader Service");
60b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge    }
61b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge
62b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge    @Override
63b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge    public void onCreate() {
64b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge        super.onCreate();
65b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge
66b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge        mCanUpload = false;
67b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge        mFilesDir = null;
68b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge        mUrl = null;
69b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge
70b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge        final PackageManager packageManager = getPackageManager();
71b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge        final boolean hasPermission = packageManager.checkPermission(Manifest.permission.INTERNET,
72b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge                getPackageName()) == PackageManager.PERMISSION_GRANTED;
73b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge        if (!hasPermission) {
74b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge            return;
75b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge        }
76b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge
77b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge        try {
78b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge            final String urlString = getString(R.string.research_logger_upload_url);
79b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge            if (urlString == null || urlString.equals("")) {
80b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge                return;
81b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge            }
82b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge            mFilesDir = getFilesDir();
83b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge            mUrl = new URL(urlString);
84b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge            mCanUpload = true;
85b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge        } catch (MalformedURLException e) {
86b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge            e.printStackTrace();
87b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge        }
88b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge    }
89b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge
90b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge    @Override
91b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge    protected void onHandleIntent(Intent intent) {
92b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge        if (!mCanUpload) {
93b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge            return;
94b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge        }
95b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge        boolean isUploadingUnconditionally = false;
96b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge        Bundle bundle = intent.getExtras();
97b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge        if (bundle != null && bundle.containsKey(EXTRA_UPLOAD_UNCONDITIONALLY)) {
98b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge            isUploadingUnconditionally = bundle.getBoolean(EXTRA_UPLOAD_UNCONDITIONALLY);
99b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge        }
100b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge        doUpload(isUploadingUnconditionally);
101b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge    }
102b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge
103b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge    private boolean isExternallyPowered() {
104b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge        final Intent intent = registerReceiver(null, new IntentFilter(
105b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge                Intent.ACTION_BATTERY_CHANGED));
106b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge        final int pluggedState = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
107b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge        return pluggedState == BatteryManager.BATTERY_PLUGGED_AC
108b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge                || pluggedState == BatteryManager.BATTERY_PLUGGED_USB;
109b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge    }
110b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge
111b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge    private boolean hasWifiConnection() {
112b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge        final ConnectivityManager manager =
113b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge                (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
114b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge        final NetworkInfo wifiInfo = manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
115b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge        return wifiInfo.isConnected();
116b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge    }
117b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge
118b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge    private void doUpload(final boolean isUploadingUnconditionally) {
119b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge        if (!isUploadingUnconditionally && (!isExternallyPowered() || !hasWifiConnection())) {
120b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge            return;
121b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge        }
122b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge        if (mFilesDir == null) {
123b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge            return;
124b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge        }
125b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge        final File[] files = mFilesDir.listFiles(new FileFilter() {
126b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge            @Override
127b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge            public boolean accept(File pathname) {
128b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge                return pathname.getName().startsWith(ResearchLogger.FILENAME_PREFIX)
129b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge                        && !pathname.canWrite();
130b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge            }
131b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge        });
132b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge        boolean success = true;
133b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge        if (files.length == 0) {
134b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge            success = false;
135b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge        }
136b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge        for (final File file : files) {
137b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge            if (!uploadFile(file)) {
138b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge                success = false;
139b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge            }
140b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge        }
141b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge    }
142b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge
143b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge    private boolean uploadFile(File file) {
144b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge        Log.d(TAG, "attempting upload of " + file.getAbsolutePath());
145b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge        boolean success = false;
146b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge        final int contentLength = (int) file.length();
147b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge        HttpURLConnection connection = null;
148b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge        InputStream fileInputStream = null;
149b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge        try {
150b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge            fileInputStream = new FileInputStream(file);
151b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge            connection = (HttpURLConnection) mUrl.openConnection();
152b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge            connection.setRequestMethod("PUT");
153b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge            connection.setDoOutput(true);
154b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge            connection.setFixedLengthStreamingMode(contentLength);
155b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge            final OutputStream os = connection.getOutputStream();
156b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge            final byte[] buf = new byte[BUF_SIZE];
157b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge            int numBytesRead;
158b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge            while ((numBytesRead = fileInputStream.read(buf)) != -1) {
159b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge                os.write(buf, 0, numBytesRead);
160b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge            }
161b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge            if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
162b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge                Log.d(TAG, "upload failed: " + connection.getResponseCode());
163b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge                InputStream netInputStream = connection.getInputStream();
164b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge                BufferedReader reader = new BufferedReader(new InputStreamReader(netInputStream));
165b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge                String line;
166b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge                while ((line = reader.readLine()) != null) {
167b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge                    Log.d(TAG, "| " + reader.readLine());
168b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge                }
169b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge                reader.close();
170b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge                return success;
171b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge            }
172b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge            file.delete();
173b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge            success = true;
174b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge            Log.d(TAG, "upload successful");
175b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge        } catch (Exception e) {
176b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge            e.printStackTrace();
177b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge        } finally {
178b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge            if (fileInputStream != null) {
179b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge                try {
180b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge                    fileInputStream.close();
181b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge                } catch (IOException e) {
182b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge                    e.printStackTrace();
183b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge                }
184b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge            }
185b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge            if (connection != null) {
186b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge                connection.disconnect();
187b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge            }
188b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge        }
189b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge        return success;
190b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge    }
191b5ace11a2e7b88e6b89b6297d5d540f351e48a4aKurt Partridge}
192