/* * Copyright (c) 2015, The Linux Foundation. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of The Linux Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package com.android.browser; import android.app.IntentService; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.TaskStackBuilder; import android.content.Intent; import android.content.Context; import android.content.SharedPreferences; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.net.Uri; import android.support.v4.app.NotificationCompat; import org.codeaurora.swe.BrowserCommandLine; import org.codeaurora.swe.Engine; import org.codeaurora.swe.utils.Logger; import org.json.JSONObject; import org.json.JSONTokener; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; import java.net.URLConnection; public class UpdateNotificationService extends IntentService { private static final String LOGTAG = "UpdateNotificationService"; private static final String ACTION_CHECK_UPDATES = BrowserConfig.AUTHORITY + ".action.check.update"; public static final int DEFAULT_UPDATE_INTERVAL = 172800000; // two days public static final String UPDATE_SERVICE_PREF = "browser_update_service"; public static final String UPDATE_JSON_VERSION_CODE = "versioncode"; public static final String UPDATE_JSON_VERSION_STRING = "versionstring"; public static final String UPDATE_JSON_MIN_INTERVAL = "interval"; public static final String UPDATE_INTERVAL = "update_interval"; public static final String UPDATE_VERSION_CODE = "version_code"; public static final String UPDATE_VERSION = "update_version"; public static final String UPDATE_URL = "update_url"; public static final String UPDATE_TIMESTAMP = "update_timestamp"; private static int NOTIFICATION_ID = 1000; private static boolean sIntentServiceInitialized = false; private static boolean sNotifyAlways = false; @Override public void onCreate() { super.onCreate(); initEngine(this); } @Override public void onDestroy() { super.onDestroy(); if (sIntentServiceInitialized) Engine.pauseTracing(this); } private static void initEngine(Context context) { if (!EngineInitializer.isInitialized()) { sIntentServiceInitialized = true; EngineInitializer.initializeSync((Context) context); } } public static void startActionUpdateNotificationService(Context context) { Intent intent = new Intent(context, UpdateNotificationService.class); intent.setAction(ACTION_CHECK_UPDATES); context.startService(intent); } public static String getFlavor(Context ctx) { String flavor = ""; try { ApplicationInfo ai = ctx.getPackageManager().getApplicationInfo( ctx.getPackageName(),PackageManager.GET_META_DATA); String compiler = (String) ai.metaData.get("Compiler"); String arch = (String) ai.metaData.get("Architecture"); flavor = "url-" + compiler + "-" + arch; } catch (Exception e) { Logger.e(LOGTAG, "getFlavor Exception : " + e.toString()); } return flavor; } public static void updateCheck(Context context) { initEngine(context.getApplicationContext()); if (!BrowserCommandLine.hasSwitch(BrowserSwitches.AUTO_UPDATE_SERVER_CMD)) { Logger.v(LOGTAG, "skip no command line: "); return; } long interval = getInterval(context); Long last_update_time = getLastUpdateTimestamp(context); if ((last_update_time + interval) < System.currentTimeMillis()) { Logger.v(LOGTAG, "check for update now: "); startActionUpdateNotificationService(context); } } public static int getLatestVersionCode(Context ctx) { SharedPreferences sharedPref = ctx.getSharedPreferences( UPDATE_SERVICE_PREF, Context.MODE_PRIVATE); return sharedPref.getInt(UPDATE_VERSION_CODE, 0); } public static String getLatestDownloadUrl(Context ctx) { SharedPreferences sharedPref = ctx.getSharedPreferences( UPDATE_SERVICE_PREF, Context.MODE_PRIVATE); return sharedPref.getString(UPDATE_URL,""); } public static String getLatestVersion(Context ctx) { SharedPreferences sharedPref = ctx.getSharedPreferences( UPDATE_SERVICE_PREF, Context.MODE_PRIVATE); return sharedPref.getString(UPDATE_VERSION, ""); } private static long getLastUpdateTimestamp(Context ctx) { SharedPreferences sharedPref = ctx.getSharedPreferences( UPDATE_SERVICE_PREF, Context.MODE_PRIVATE); return sharedPref.getLong(UPDATE_TIMESTAMP, 0); } private static int getInterval(Context ctx) { SharedPreferences sharedPref = ctx.getSharedPreferences( UPDATE_SERVICE_PREF, Context.MODE_PRIVATE); return sharedPref.getInt(UPDATE_INTERVAL, DEFAULT_UPDATE_INTERVAL); } public UpdateNotificationService() { super("UpdateNotificationService"); } @Override protected void onHandleIntent(Intent intent) { if (intent != null) { final String action = intent.getAction(); if (ACTION_CHECK_UPDATES.equals(action)) { handleUpdateCheck(); } } } private void updateTimeStamp() { SharedPreferences sharedPref = getSharedPreferences( UPDATE_SERVICE_PREF, Context.MODE_PRIVATE); SharedPreferences.Editor editor = sharedPref.edit(); editor.putLong(UPDATE_TIMESTAMP, System.currentTimeMillis()); editor.commit(); } private void persist(int versionCode, String url, String version, int interval) { SharedPreferences sharedPref = getSharedPreferences( UPDATE_SERVICE_PREF, Context.MODE_PRIVATE); SharedPreferences.Editor editor = sharedPref.edit(); editor.putInt(UPDATE_VERSION_CODE, versionCode); editor.putInt(UPDATE_INTERVAL, interval); editor.putString(UPDATE_VERSION, version); editor.putString(UPDATE_URL, url); Logger.v(LOGTAG, "persist version code : " + versionCode); Logger.v(LOGTAG, "persist version : " + version); Logger.v(LOGTAG, "persist download url : " + url); editor.commit(); } private void handleUpdateCheck() { String server_url = BrowserCommandLine.getSwitchValue( BrowserSwitches.AUTO_UPDATE_SERVER_CMD); int interval = DEFAULT_UPDATE_INTERVAL; InputStream stream = null; if (server_url != null && !server_url.isEmpty()) { try { URLConnection connection = new URL(server_url).openConnection(); stream = connection.getInputStream(); String result = readContents(stream); Logger.v(LOGTAG, "handleUpdateCheck result : " + result); JSONObject jsonResult = (JSONObject) new JSONTokener(result).nextValue(); int versionCode = Integer.parseInt((String) jsonResult.get(UPDATE_JSON_VERSION_CODE)); String url = (String) jsonResult.get(getFlavor(this)); String version = (String) jsonResult.get(UPDATE_JSON_VERSION_STRING); if (jsonResult.has(UPDATE_JSON_MIN_INTERVAL)) interval = Integer.parseInt((String) jsonResult.get(UPDATE_JSON_MIN_INTERVAL)); if (getCurrentVersionCode(this) < versionCode && (sNotifyAlways || getLatestVersionCode(this) != versionCode)) { persist(versionCode, url, version, interval); // notify only once per version change showNotification(this, url, version); } stream.close(); } catch (Exception e) { Logger.e(LOGTAG, "handleUpdateCheck Exception : " + e.toString()); } finally { // always update the timestamp updateTimeStamp(); } } } public static int getCurrentVersionCode(Context ctx) { PackageInfo pInfo = null; try { pInfo = ctx.getPackageManager().getPackageInfo(ctx.getPackageName(), 0); } catch (PackageManager.NameNotFoundException e) { Logger.e(LOGTAG, "getCurrentVersionCode Exception : " + e.toString()); } return pInfo.versionCode; } private static void showNotification(Context ctx, String url, String version) { NotificationCompat.Builder builder = new NotificationCompat.Builder(ctx) .setSmallIcon(R.drawable.img_notify_update_white) .setContentTitle(ctx.getString(R.string.update)) .setContentText(ctx.getString(R.string.update_msg) + version); Intent resultIntent = new Intent(ctx, BrowserActivity.class); resultIntent.setAction(Intent.ACTION_VIEW); resultIntent.setData(Uri.parse(url)); TaskStackBuilder stackBuilder = TaskStackBuilder.create(ctx); stackBuilder.addParentStack(BrowserActivity.class); stackBuilder.addNextIntent(resultIntent); PendingIntent resultPendingIntent = stackBuilder.getPendingIntent( 0, PendingIntent.FLAG_UPDATE_CURRENT ); builder.setContentIntent(resultPendingIntent); NotificationManager mNotificationManager = (NotificationManager) ctx.getSystemService(Context.NOTIFICATION_SERVICE); Notification notification = builder.build(); notification.flags = Notification.DEFAULT_LIGHTS | Notification.FLAG_AUTO_CANCEL; mNotificationManager.notify(NOTIFICATION_ID, notification); } private static void removeNotification(Context ctx) { NotificationManager mNotificationManager = (NotificationManager) ctx.getSystemService(Context.NOTIFICATION_SERVICE); mNotificationManager.cancel(NOTIFICATION_ID); } private static String readContents(InputStream is) { String line = null; BufferedReader reader = new BufferedReader(new InputStreamReader(is)); StringBuilder sb = new StringBuilder(); try { line = reader.readLine(); while (line != null) { line = line.replaceFirst("channel = ",""); sb.append(line + "\n"); line = reader.readLine(); } } catch (Exception e) { Logger.e(LOGTAG, "convertStreamToString Exception : " + e.toString()); } finally { try { is.close(); } catch (Exception e) { Logger.e(LOGTAG, "convertStreamToString Exception : " + e.toString()); } } return sb.toString(); } }