blob: dc913158bd096e776ad00a1ebef91e22cfcfc8d8 [file] [log] [blame]
Vivek Sekharb53a5952015-07-17 14:14:38 -07001/*
2 * Copyright (c) 2015, The Linux Foundation. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above
10 * copyright notice, this list of conditions and the following
11 * disclaimer in the documentation and/or other materials provided
12 * with the distribution.
13 * * Neither the name of The Linux Foundation nor the names of its
14 * contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
18 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
24 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
26 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28*/
29
30package com.android.browser;
31
32import android.app.IntentService;
33import android.app.Notification;
34import android.app.NotificationManager;
35import android.app.PendingIntent;
36import android.app.TaskStackBuilder;
37import android.content.Intent;
38import android.content.Context;
39import android.content.SharedPreferences;
40import android.content.pm.ApplicationInfo;
41import android.content.pm.PackageInfo;
42import android.content.pm.PackageManager;
43import android.net.Uri;
44import android.support.v4.app.NotificationCompat;
45import android.util.Log;
46
47
48import org.codeaurora.swe.BrowserCommandLine;
49import org.codeaurora.swe.Engine;
50import org.json.JSONException;
51import org.json.JSONObject;
52import org.json.JSONTokener;
53
54import java.io.BufferedReader;
55import java.io.IOException;
56import java.io.InputStream;
57import java.io.InputStreamReader;
58import java.net.URL;
59import java.net.URLConnection;
60
61public class UpdateNotificationService extends IntentService {
62 private static final String LOGTAG = "UpdateNotificationService";
63 private static final String ACTION_CHECK_UPDATES = BrowserConfig.AUTHORITY +
64 ".action.check.update";
65 public static final int DEFAULT_UPDATE_INTERVAL = 604800000; // one week
66 public static final String UPDATE_SERVICE_PREF = "browser_update_service";
67 public static final String UPDATE_JSON_VERSION_CODE = "versioncode";
68 public static final String UPDATE_JSON_VERSION_STRING = "versionstring";
69 public static final String UPDATE_JSON_MIN_INTERVAL = "interval";
70 public static final String UPDATE_INTERVAL = "update_interval";
71 public static final String UPDATE_VERSION_CODE = "version_code";
72 public static final String UPDATE_VERSION = "update_version";
73 public static final String UPDATE_URL = "update_url";
74 public static final String UPDATE_TIMESTAMP = "update_timestamp";
75 private static int NOTIFICATION_ID = 1000;
76 private static boolean sIntentServiceInitialized = false;
77 private static boolean sNotifyAlways = false;
78
79 @Override
80 public void onCreate() {
81 super.onCreate();
82 initEngine(this);
83 }
84
85 @Override
86 public void onDestroy() {
87 super.onDestroy();
88 if (sIntentServiceInitialized)
89 Engine.pauseTracing(this);
90 }
91
92 private static void initEngine(Context context) {
93 if (!EngineInitializer.isInitialized()) {
94 sIntentServiceInitialized = true;
95 EngineInitializer.initializeSync((Context) context);
96 }
97 }
98
99 public static void startActionUpdateNotificationService(Context context) {
100 Intent intent = new Intent(context, UpdateNotificationService.class);
101 intent.setAction(ACTION_CHECK_UPDATES);
102 context.startService(intent);
103 }
104
105 public static String getFlavor(Context ctx) {
106 String flavor = "";
107 try {
108 ApplicationInfo ai = ctx.getPackageManager().getApplicationInfo(
109 ctx.getPackageName(),PackageManager.GET_META_DATA);
110 String compiler = (String) ai.metaData.get("Compiler");
111 String arch = (String) ai.metaData.get("Architecture");
112 flavor = "url-" + compiler + "-" + arch;
113 } catch (Exception e) {
114 Log.e(LOGTAG, "getFlavor Exception : " + e.toString());
115 }
116 return flavor;
117 }
118
119 public static void updateCheck(Context context) {
120 initEngine(context.getApplicationContext());
121 if (!BrowserCommandLine.hasSwitch(BrowserSwitches.AUTO_UPDATE_SERVER_CMD)) {
122 if (Browser.LOGV_ENABLED)
123 Log.v(LOGTAG, "skip no command line: ");
124 return;
125 }
126 long interval = getInterval(context);
127 Long last_update_time = getLastUpdateTimestamp(context);
128 if ((last_update_time + interval) < System.currentTimeMillis()) {
129 if (Browser.LOGV_ENABLED)
130 Log.v(LOGTAG, "check for update now: ");
131 startActionUpdateNotificationService(context);
132 }
133 }
134
135 public static int getLatestVersionCode(Context ctx) {
136 SharedPreferences sharedPref = ctx.getSharedPreferences(
137 UPDATE_SERVICE_PREF, Context.MODE_PRIVATE);
138 return sharedPref.getInt(UPDATE_VERSION_CODE, 0);
139 }
140
141 public static String getLatestDownloadUrl(Context ctx) {
142 SharedPreferences sharedPref = ctx.getSharedPreferences(
143 UPDATE_SERVICE_PREF, Context.MODE_PRIVATE);
144 return sharedPref.getString(UPDATE_URL,"");
145 }
146
147 public static String getLatestVersion(Context ctx) {
148 SharedPreferences sharedPref = ctx.getSharedPreferences(
149 UPDATE_SERVICE_PREF, Context.MODE_PRIVATE);
150 return sharedPref.getString(UPDATE_VERSION, "");
151 }
152
153 private static long getLastUpdateTimestamp(Context ctx) {
154 SharedPreferences sharedPref = ctx.getSharedPreferences(
155 UPDATE_SERVICE_PREF, Context.MODE_PRIVATE);
156 return sharedPref.getLong(UPDATE_TIMESTAMP, 0);
157 }
158
159 private static int getInterval(Context ctx) {
160 SharedPreferences sharedPref = ctx.getSharedPreferences(
161 UPDATE_SERVICE_PREF, Context.MODE_PRIVATE);
162 return sharedPref.getInt(UPDATE_INTERVAL, DEFAULT_UPDATE_INTERVAL);
163 }
164
165 public UpdateNotificationService() {
166 super("UpdateNotificationService");
167 }
168
169 @Override
170 protected void onHandleIntent(Intent intent) {
171 if (intent != null) {
172 final String action = intent.getAction();
173 if (ACTION_CHECK_UPDATES.equals(action)) {
174 handleUpdateCheck();
175 }
176 }
177 }
178
179 private void updateTimeStamp() {
180 SharedPreferences sharedPref = getSharedPreferences(
181 UPDATE_SERVICE_PREF, Context.MODE_PRIVATE);
182 SharedPreferences.Editor editor = sharedPref.edit();
183 editor.putLong(UPDATE_TIMESTAMP, System.currentTimeMillis());
184 editor.commit();
185 }
186
187 private void persist(int versionCode, String url, String version, int interval) {
188 SharedPreferences sharedPref = getSharedPreferences(
189 UPDATE_SERVICE_PREF, Context.MODE_PRIVATE);
190 SharedPreferences.Editor editor = sharedPref.edit();
191 editor.putInt(UPDATE_VERSION_CODE, versionCode);
192 editor.putInt(UPDATE_INTERVAL, interval);
193 editor.putString(UPDATE_VERSION, version);
194 editor.putString(UPDATE_URL, url);
195 if (Browser.LOGV_ENABLED) {
196 Log.v(LOGTAG, "persist version code : " + versionCode);
197 Log.v(LOGTAG, "persist version : " + version);
198 Log.v(LOGTAG, "persist download url : " + url);
199 }
200 editor.commit();
201 }
202
203 private void handleUpdateCheck() {
204 String server_url = BrowserCommandLine.getSwitchValue(
205 BrowserSwitches.AUTO_UPDATE_SERVER_CMD) + "/" + getPackageName();
206 int interval = DEFAULT_UPDATE_INTERVAL;
207 InputStream stream = null;
208 if (server_url != null && !server_url.isEmpty()) {
209 try {
210 URLConnection connection = new URL(server_url).openConnection();
211 stream = connection.getInputStream();
212 String result = readContents(stream);
213 if (Browser.LOGV_ENABLED)
214 Log.v(LOGTAG, "handleUpdateCheck result : " + result);
215 JSONObject jsonResult = (JSONObject) new JSONTokener(result).nextValue();
216 int versionCode = Integer.parseInt((String) jsonResult.get(UPDATE_JSON_VERSION_CODE));
217 String url = (String) jsonResult.get(getFlavor(this));
218 String version = (String) jsonResult.get(UPDATE_JSON_VERSION_STRING);
219 if (jsonResult.has(UPDATE_JSON_MIN_INTERVAL))
220 interval = Integer.parseInt((String) jsonResult.get(UPDATE_JSON_MIN_INTERVAL));
221 if (getCurrentVersionCode(this) < versionCode &&
222 (sNotifyAlways || getLatestVersionCode(this) != versionCode)) {
223 persist(versionCode, url, version, interval);
224 // notify only once per version change
225 showNotification(this, url, version);
226 }
227 stream.close();
228 } catch (JSONException e) {
229 Log.e(LOGTAG, "handleUpdateCheck JSONException : " + e.toString());
230 } catch (IOException e) {
231 Log.e(LOGTAG, "handleUpdateCheck IOException : " + e.toString());
232 } finally {
233 // always update the timestamp
234 updateTimeStamp();
235 }
236 }
237 }
238
239 public static int getCurrentVersionCode(Context ctx) {
240 PackageInfo pInfo = null;
241 try {
242 pInfo = ctx.getPackageManager().getPackageInfo(ctx.getPackageName(), 0);
243 } catch (PackageManager.NameNotFoundException e) {
244 Log.e(LOGTAG, "getCurrentVersionCode Exception : " + e.toString());
245 }
246 return pInfo.versionCode;
247 }
248
249 private static void showNotification(Context ctx, String url, String version) {
250 NotificationCompat.Builder builder =
251 new NotificationCompat.Builder(ctx)
252 .setSmallIcon(R.drawable.img_notify_update_white)
253 .setContentTitle(ctx.getString(R.string.update))
254 .setContentText(ctx.getString(R.string.update_msg) + version);
255 Intent resultIntent = new Intent(ctx, BrowserActivity.class);
256 resultIntent.setAction(Intent.ACTION_VIEW);
257 resultIntent.setData(Uri.parse(url));
258 TaskStackBuilder stackBuilder = TaskStackBuilder.create(ctx);
259 stackBuilder.addParentStack(BrowserActivity.class);
260 stackBuilder.addNextIntent(resultIntent);
261 PendingIntent resultPendingIntent =
262 stackBuilder.getPendingIntent(
263 0,
264 PendingIntent.FLAG_UPDATE_CURRENT
265 );
266 builder.setContentIntent(resultPendingIntent);
267 NotificationManager mNotificationManager =
268 (NotificationManager) ctx.getSystemService(Context.NOTIFICATION_SERVICE);
269 Notification notification = builder.build();
270 notification.flags = Notification.DEFAULT_LIGHTS | Notification.FLAG_AUTO_CANCEL;
271 mNotificationManager.notify(NOTIFICATION_ID, notification);
272 }
273
274 private static void removeNotification(Context ctx) {
275 NotificationManager mNotificationManager =
276 (NotificationManager) ctx.getSystemService(Context.NOTIFICATION_SERVICE);
277 mNotificationManager.cancel(NOTIFICATION_ID);
278 }
279
280 private static String readContents(InputStream is) {
281 String line = null;
282 BufferedReader reader = new BufferedReader(new InputStreamReader(is));
283 StringBuilder sb = new StringBuilder();
284 try {
285 line = reader.readLine();
286 while (line != null) {
287 line = line.replaceFirst("channel = ","");
288 sb.append(line + "\n");
289 line = reader.readLine();
290 }
291 } catch (Exception e) {
292 Log.e(LOGTAG, "convertStreamToString Exception : " + e.toString());
293 } finally {
294 try {
295 is.close();
296 } catch (Exception e) {
297 Log.e(LOGTAG, "convertStreamToString Exception : " + e.toString());
298 }
299 }
300 return sb.toString();
301 }
302
303}