mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 786380 - Implement new Java-based updater for Android r=cpeterson,mfinkle
--HG-- extra : rebase_source : 167db7e145d5e0cfa8c74e6e85570c6096538f64
This commit is contained in:
parent
fba649bee9
commit
4bf23f013d
@ -490,31 +490,11 @@ pref("app.update.timerMinimumDelay", 30); // seconds
|
||||
|
||||
#ifdef MOZ_UPDATER
|
||||
/* prefs used specifically for updating the app */
|
||||
pref("app.update.enabled", true);
|
||||
pref("app.update.auto", false);
|
||||
pref("app.update.enabled", false);
|
||||
pref("app.update.channel", "@MOZ_UPDATE_CHANNEL@");
|
||||
pref("app.update.mode", 1);
|
||||
pref("app.update.silent", false);
|
||||
#ifdef MOZ_PKG_SPECIAL
|
||||
pref("app.update.url", "https://aus2.mozilla.org/update/4/%PRODUCT%/%VERSION%/%BUILD_ID%/%BUILD_TARGET%-@MOZ_PKG_SPECIAL@/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/%PLATFORM_VERSION%/update.xml");
|
||||
#else
|
||||
pref("app.update.url", "https://aus2.mozilla.org/update/4/%PRODUCT%/%VERSION%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/%PLATFORM_VERSION%/update.xml");
|
||||
#endif
|
||||
pref("app.update.promptWaitTime", 43200);
|
||||
pref("app.update.idletime", 60);
|
||||
pref("app.update.showInstalledUI", false);
|
||||
pref("app.update.incompatible.mode", 0);
|
||||
pref("app.update.download.backgroundInterval", 0);
|
||||
|
||||
#ifdef MOZ_OFFICIAL_BRANDING
|
||||
pref("app.update.interval", 86400);
|
||||
pref("app.update.url.manual", "http://www.mozilla.com/%LOCALE%/m/");
|
||||
pref("app.update.url.details", "http://www.mozilla.com/%LOCALE%/mobile/releases/");
|
||||
#else
|
||||
pref("app.update.interval", 3600);
|
||||
pref("app.update.url.manual", "http://www.mozilla.com/%LOCALE%/mobile/");
|
||||
pref("app.update.url.details", "http://www.mozilla.com/%LOCALE%/mobile/");
|
||||
#endif
|
||||
// If you are looking for app.update.url, we no longer use it.
|
||||
// See mobile/android/base/UpdateServiceHelper.java.in
|
||||
#endif
|
||||
|
||||
// replace newlines with spaces on paste into single-line text boxes
|
||||
|
@ -148,6 +148,12 @@
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<receiver android:name="org.mozilla.gecko.GeckoUpdateReceiver">
|
||||
<intent-filter>
|
||||
<action android:name="@ANDROID_PACKAGE_NAME@.CHECK_UPDATE_RESULT" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<receiver android:name="org.mozilla.gecko.GeckoMessageReceiver"
|
||||
android:permission="@ANDROID_PACKAGE_NAME@.permissions.PASSWORD_PROVIDER">
|
||||
<intent-filter>
|
||||
@ -224,6 +230,13 @@
|
||||
android:authorities="@ANDROID_PACKAGE_NAME@.db.tabs"
|
||||
android:permission="@ANDROID_PACKAGE_NAME@.permissions.BROWSER_PROVIDER"/>
|
||||
|
||||
<service
|
||||
android:exported="false"
|
||||
android:name="org.mozilla.gecko.updater.UpdateService"
|
||||
android:process=":updater">
|
||||
</service>
|
||||
|
||||
|
||||
#include ../sync/manifests/SyncAndroidManifest_services.xml.in
|
||||
</application>
|
||||
|
||||
|
@ -16,6 +16,8 @@ import org.mozilla.gecko.util.GeckoBackgroundThread;
|
||||
import org.mozilla.gecko.util.GeckoEventListener;
|
||||
import org.mozilla.gecko.util.GeckoEventResponder;
|
||||
import org.mozilla.gecko.GeckoAccessibility;
|
||||
import org.mozilla.gecko.updater.UpdateServiceHelper;
|
||||
import org.mozilla.gecko.updater.UpdateService;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
@ -127,7 +129,6 @@ abstract public class GeckoApp
|
||||
public static final String ACTION_DEBUG = "org.mozilla.gecko.DEBUG";
|
||||
public static final String ACTION_BOOKMARK = "org.mozilla.gecko.BOOKMARK";
|
||||
public static final String ACTION_LOAD = "org.mozilla.gecko.LOAD";
|
||||
public static final String ACTION_UPDATE = "org.mozilla.gecko.UPDATE";
|
||||
public static final String ACTION_INIT_PW = "org.mozilla.gecko.INIT_PW";
|
||||
public static final String SAVED_STATE_TITLE = "title";
|
||||
public static final String SAVED_STATE_IN_BACKGROUND = "inBackground";
|
||||
@ -910,8 +911,6 @@ abstract public class GeckoApp
|
||||
String host = message.getString("host");
|
||||
JSONArray permissions = message.getJSONArray("permissions");
|
||||
showSiteSettingsDialog(host, permissions);
|
||||
} else if (event.equals("Update:Restart")) {
|
||||
doRestart("org.mozilla.gecko.restart_update");
|
||||
} else if (event.equals("Tab:ViewportMetadata")) {
|
||||
int tabId = message.getInt("tabID");
|
||||
Tab tab = Tabs.getInstance().getTab(tabId);
|
||||
@ -1000,6 +999,8 @@ abstract public class GeckoApp
|
||||
GeckoAppShell.shareImage(src, type);
|
||||
} else if (event.equals("Sanitize:ClearHistory")) {
|
||||
handleClearHistory();
|
||||
} else if (event.equals("Update:Check")) {
|
||||
startService(new Intent(UpdateServiceHelper.ACTION_CHECK_FOR_UPDATE, null, this, UpdateService.class));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(LOGTAG, "Exception handling message \"" + event + "\":", e);
|
||||
@ -1487,11 +1488,6 @@ abstract public class GeckoApp
|
||||
|
||||
BrowserDB.initialize(getProfile().getName());
|
||||
|
||||
if (ACTION_UPDATE.equals(action) || args != null && args.contains("-alert update-app")) {
|
||||
Log.i(LOGTAG,"onCreate: Update request");
|
||||
checkAndLaunchUpdate();
|
||||
}
|
||||
|
||||
String passedUri = null;
|
||||
String uri = getURIFromIntent(intent);
|
||||
if (uri != null && uri.length() > 0) {
|
||||
@ -1586,7 +1582,6 @@ abstract public class GeckoApp
|
||||
registerEventListener("ToggleChrome:Show");
|
||||
registerEventListener("ToggleChrome:Focus");
|
||||
registerEventListener("Permissions:Data");
|
||||
registerEventListener("Update:Restart");
|
||||
registerEventListener("Tab:HasTouchListener");
|
||||
registerEventListener("Tab:ViewportMetadata");
|
||||
registerEventListener("Session:StatePurged");
|
||||
@ -1601,6 +1596,7 @@ abstract public class GeckoApp
|
||||
registerEventListener("Share:Text");
|
||||
registerEventListener("Share:Image");
|
||||
registerEventListener("Sanitize:ClearHistory");
|
||||
registerEventListener("Update:Check");
|
||||
|
||||
if (SmsManager.getInstance() != null) {
|
||||
SmsManager.getInstance().start();
|
||||
@ -1621,6 +1617,8 @@ abstract public class GeckoApp
|
||||
GeckoNetworkManager.getInstance().init();
|
||||
GeckoNetworkManager.getInstance().start();
|
||||
|
||||
UpdateServiceHelper.registerForUpdates(this);
|
||||
|
||||
GeckoScreenOrientationListener.getInstance().start();
|
||||
|
||||
final GeckoApp self = this;
|
||||
@ -2029,7 +2027,6 @@ abstract public class GeckoApp
|
||||
unregisterEventListener("ToggleChrome:Show");
|
||||
unregisterEventListener("ToggleChrome:Focus");
|
||||
unregisterEventListener("Permissions:Data");
|
||||
unregisterEventListener("Update:Restart");
|
||||
unregisterEventListener("Tab:HasTouchListener");
|
||||
unregisterEventListener("Tab:ViewportMetadata");
|
||||
unregisterEventListener("Session:StatePurged");
|
||||
@ -2044,6 +2041,7 @@ abstract public class GeckoApp
|
||||
unregisterEventListener("Share:Text");
|
||||
unregisterEventListener("Share:Image");
|
||||
unregisterEventListener("Sanitize:ClearHistory");
|
||||
unregisterEventListener("Update:Check");
|
||||
|
||||
deleteTempFiles();
|
||||
|
||||
@ -2214,76 +2212,6 @@ abstract public class GeckoApp
|
||||
GeckoAppShell.handleNotification(action, alertName, alertCookie);
|
||||
}
|
||||
|
||||
private void checkAndLaunchUpdate() {
|
||||
Log.i(LOGTAG, "Checking for an update");
|
||||
|
||||
int statusCode = 8; // UNEXPECTED_ERROR
|
||||
File baseUpdateDir = null;
|
||||
if (Build.VERSION.SDK_INT >= 8)
|
||||
baseUpdateDir = getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS);
|
||||
else
|
||||
baseUpdateDir = new File(Environment.getExternalStorageDirectory().getPath(), "download");
|
||||
|
||||
File updateDir = new File(new File(baseUpdateDir, "updates"),"0");
|
||||
|
||||
File updateFile = new File(updateDir, "update.apk");
|
||||
File statusFile = new File(updateDir, "update.status");
|
||||
|
||||
if (!statusFile.exists() || !readUpdateStatus(statusFile).equals("pending"))
|
||||
return;
|
||||
|
||||
if (!updateFile.exists())
|
||||
return;
|
||||
|
||||
Log.i(LOGTAG, "Update is available!");
|
||||
|
||||
// Launch APK
|
||||
File updateFileToRun = new File(updateDir, getPackageName() + "-update.apk");
|
||||
try {
|
||||
if (updateFile.renameTo(updateFileToRun)) {
|
||||
String amCmd = "/system/bin/am start -a android.intent.action.VIEW " +
|
||||
"-n com.android.packageinstaller/.PackageInstallerActivity -d file://" +
|
||||
updateFileToRun.getPath();
|
||||
Log.i(LOGTAG, amCmd);
|
||||
Runtime.getRuntime().exec(amCmd);
|
||||
statusCode = 0; // OK
|
||||
} else {
|
||||
Log.i(LOGTAG, "Cannot rename the update file!");
|
||||
statusCode = 7; // WRITE_ERROR
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.i(LOGTAG, "error launching installer to update", e);
|
||||
}
|
||||
|
||||
// Update the status file
|
||||
String status = statusCode == 0 ? "succeeded\n" : "failed: "+ statusCode + "\n";
|
||||
|
||||
OutputStream outStream;
|
||||
try {
|
||||
byte[] buf = status.getBytes("UTF-8");
|
||||
outStream = new FileOutputStream(statusFile);
|
||||
outStream.write(buf, 0, buf.length);
|
||||
outStream.close();
|
||||
} catch (Exception e) {
|
||||
Log.i(LOGTAG, "error writing status file", e);
|
||||
}
|
||||
|
||||
if (statusCode == 0)
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
private String readUpdateStatus(File statusFile) {
|
||||
String status = "";
|
||||
try {
|
||||
BufferedReader reader = new BufferedReader(new FileReader(statusFile));
|
||||
status = reader.readLine();
|
||||
reader.close();
|
||||
} catch (Exception e) {
|
||||
Log.i(LOGTAG, "error reading update status", e);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
private void checkMigrateProfile() {
|
||||
final File profileDir = getProfile().getDir();
|
||||
final long currentTime = SystemClock.uptimeMillis();
|
||||
@ -2566,6 +2494,10 @@ abstract public class GeckoApp
|
||||
}
|
||||
}
|
||||
|
||||
public void notifyCheckUpdateResult(boolean result) {
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Update:CheckResult", result ? "true" : "false"));
|
||||
}
|
||||
|
||||
private void connectGeckoLayerClient() {
|
||||
mLayerView.getLayerClient().notifyGeckoReady();
|
||||
|
||||
|
@ -2297,6 +2297,11 @@ public class GeckoAppShell
|
||||
public static void unregisterSurfaceTextureFrameListener(Object surfaceTexture) {
|
||||
((SurfaceTexture)surfaceTexture).setOnFrameAvailableListener(null);
|
||||
}
|
||||
|
||||
public static void notifyCheckUpdateResult(boolean result) {
|
||||
if (GeckoApp.mAppContext != null)
|
||||
GeckoApp.mAppContext.notifyCheckUpdateResult(result);
|
||||
}
|
||||
}
|
||||
|
||||
class ScreenshotHandler implements Runnable {
|
||||
|
22
mobile/android/base/GeckoUpdateReceiver.java
Normal file
22
mobile/android/base/GeckoUpdateReceiver.java
Normal file
@ -0,0 +1,22 @@
|
||||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.gecko;
|
||||
|
||||
import org.mozilla.gecko.updater.UpdateServiceHelper;
|
||||
|
||||
import android.content.*;
|
||||
import android.net.*;
|
||||
|
||||
public class GeckoUpdateReceiver
|
||||
extends BroadcastReceiver
|
||||
{
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (UpdateServiceHelper.ACTION_CHECK_UPDATE_RESULT.equals(intent.getAction())) {
|
||||
GeckoAppShell.notifyCheckUpdateResult(intent.getBooleanExtra("result", false));
|
||||
}
|
||||
}
|
||||
}
|
@ -159,6 +159,8 @@ FENNEC_JAVA_FILES = \
|
||||
ui/SubdocumentScrollHelper.java \
|
||||
GeckoNetworkManager.java \
|
||||
GeckoScreenOrientationListener.java \
|
||||
UpdateService.java \
|
||||
GeckoUpdateReceiver.java \
|
||||
$(MOZGLUE_JAVA_FILES) \
|
||||
$(UTIL_JAVA_FILES) \
|
||||
$(NULL)
|
||||
@ -183,6 +185,7 @@ FENNEC_PP_JAVA_FILES = \
|
||||
db/TabsProvider.java \
|
||||
db/GeckoProvider.java \
|
||||
SmsManager.java \
|
||||
UpdateServiceHelper.java \
|
||||
$(NULL)
|
||||
|
||||
FENNEC_PP_XML_FILES = \
|
||||
@ -207,6 +210,8 @@ else
|
||||
MIN_CPU_VERSION=5
|
||||
endif
|
||||
|
||||
MOZ_APP_BUILDID=$(shell cat $(DEPTH)/config/buildid)
|
||||
|
||||
ifeq (,$(ANDROID_VERSION_CODE))
|
||||
ifeq ($(MIN_CPU_VERSION),7)
|
||||
ANDROID_VERSION_CODE=$(shell cat $(DEPTH)/config/buildid | cut -c1-10)
|
||||
@ -231,6 +236,10 @@ DEFINES += \
|
||||
-DANDROID_VERSION_CODE=$(ANDROID_VERSION_CODE) \
|
||||
-DMOZILLA_OFFICIAL=$(MOZILLA_OFFICIAL) \
|
||||
-DUA_BUILDID=$(UA_BUILDID) \
|
||||
-DMOZ_APP_BASENAME=$(MOZ_APP_BASENAME) \
|
||||
-DMOZ_APP_BUILDID=$(MOZ_APP_BUILDID) \
|
||||
-DMOZ_APP_ABI=$(TARGET_XPCOM_ABI) \
|
||||
-DMOZ_UPDATER=$(MOZ_UPDATER) \
|
||||
$(NULL)
|
||||
|
||||
ifdef MOZ_LINKER_EXTRACT
|
||||
|
@ -43,10 +43,7 @@ public class Restarter extends Activity {
|
||||
Log.i(LOGTAG, e.toString());
|
||||
}
|
||||
try {
|
||||
String action = "org.mozilla.gecko.restart_update".equals(getIntent().getAction()) ?
|
||||
App.ACTION_UPDATE : Intent.ACTION_MAIN;
|
||||
|
||||
Intent intent = new Intent(action);
|
||||
Intent intent = new Intent(Intent.ACTION_MAIN);
|
||||
intent.setClassName("@ANDROID_PACKAGE_NAME@",
|
||||
"@ANDROID_PACKAGE_NAME@.App");
|
||||
Bundle b = getIntent().getExtras();
|
||||
|
638
mobile/android/base/UpdateService.java
Normal file
638
mobile/android/base/UpdateService.java
Normal file
@ -0,0 +1,638 @@
|
||||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.gecko.updater;
|
||||
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.GeckoApp;
|
||||
|
||||
import org.mozilla.apache.commons.codec.binary.Hex;
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import android.app.AlarmManager;
|
||||
import android.app.IntentService;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.app.Service;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
import android.net.Uri;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.os.IBinder;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import android.widget.RemoteViews;
|
||||
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.Locale;
|
||||
import java.util.Random;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
|
||||
|
||||
public class UpdateService extends IntentService {
|
||||
private static final int BUFSIZE = 8192;
|
||||
private static final int NOTIFICATION_ID = 0x3e40ddbd;
|
||||
|
||||
private static final String LOGTAG = "UpdateService";
|
||||
|
||||
private static final int INTERVAL_LONG = 86400000; // in milliseconds
|
||||
private static final int INTERVAL_SHORT = 14400000; // again, in milliseconds
|
||||
private static final int INTERVAL_RETRY = 3600000;
|
||||
|
||||
private static final String PREFS_NAME = "UpdateService";
|
||||
private static final String KEY_LAST_BUILDID = "UpdateService.lastBuildID";
|
||||
private static final String KEY_LAST_HASH_FUNCTION = "UpdateService.lastHashFunction";
|
||||
private static final String KEY_LAST_HASH_VALUE = "UpdateService.lastHashValue";
|
||||
private static final String KEY_LAST_ATTEMPT_DATE = "UpdateService.lastAttemptDate";
|
||||
|
||||
private SharedPreferences mPrefs;
|
||||
|
||||
private NotificationManager mNotificationManager;
|
||||
private ConnectivityManager mConnectivityManager;
|
||||
|
||||
private boolean mDownloading;
|
||||
private boolean mApplyImmediately;
|
||||
|
||||
public UpdateService() {
|
||||
super("updater");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate () {
|
||||
super.onCreate();
|
||||
|
||||
mPrefs = getSharedPreferences(PREFS_NAME, 0);
|
||||
mNotificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
mConnectivityManager = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized int onStartCommand (Intent intent, int flags, int startId) {
|
||||
// If we are busy doing a download, the new Intent here would normally be queued for
|
||||
// execution once that is done. In this case, however, we want to flip the boolean
|
||||
// while that is running, so handle that now.
|
||||
if (mDownloading && UpdateServiceHelper.ACTION_APPLY_UPDATE.equals(intent.getAction())) {
|
||||
Log.i(LOGTAG, "will apply update when download finished");
|
||||
|
||||
mApplyImmediately = true;
|
||||
showDownloadNotification();
|
||||
} else {
|
||||
super.onStartCommand(intent, flags, startId);
|
||||
}
|
||||
|
||||
return Service.START_REDELIVER_INTENT;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onHandleIntent (Intent intent) {
|
||||
if (UpdateServiceHelper.ACTION_REGISTER_FOR_UPDATES.equals(intent.getAction())) {
|
||||
registerForUpdates(false);
|
||||
} if (UpdateServiceHelper.ACTION_CHECK_FOR_UPDATE.equals(intent.getAction())) {
|
||||
startUpdate(intent.getIntExtra(UpdateServiceHelper.EXTRA_UPDATE_FLAGS_NAME, 0));
|
||||
} else if (UpdateServiceHelper.ACTION_APPLY_UPDATE.equals(intent.getAction())) {
|
||||
applyUpdate(intent.getStringExtra(UpdateServiceHelper.EXTRA_PACKAGE_PATH_NAME));
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean hasFlag(int flags, int flag) {
|
||||
return (flags & flag) == flag;
|
||||
}
|
||||
|
||||
private void sendCheckUpdateResult(boolean result) {
|
||||
Intent resultIntent = new Intent(UpdateServiceHelper.ACTION_CHECK_UPDATE_RESULT);
|
||||
resultIntent.putExtra("result", result);
|
||||
sendBroadcast(resultIntent);
|
||||
}
|
||||
|
||||
private int getUpdateInterval(boolean isRetry) {
|
||||
int interval;
|
||||
if (isRetry) {
|
||||
interval = INTERVAL_RETRY;
|
||||
} else if (UpdateServiceHelper.UPDATE_CHANNEL.equals("nightly") ||
|
||||
UpdateServiceHelper.UPDATE_CHANNEL.equals("aurora")) {
|
||||
interval = INTERVAL_SHORT;
|
||||
} else {
|
||||
interval = INTERVAL_LONG;
|
||||
}
|
||||
|
||||
return interval;
|
||||
}
|
||||
|
||||
private void registerForUpdates(boolean isRetry) {
|
||||
Calendar lastAttempt = getLastAttemptDate();
|
||||
Calendar now = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
|
||||
|
||||
int interval = getUpdateInterval(isRetry);
|
||||
|
||||
if (lastAttempt == null || (now.getTimeInMillis() - lastAttempt.getTimeInMillis()) > interval) {
|
||||
// We've either never attempted an update, or we are passed the desired
|
||||
// time. Start an update now.
|
||||
Log.i(LOGTAG, "no update has ever been attempted, checking now");
|
||||
startUpdate(0);
|
||||
return;
|
||||
}
|
||||
|
||||
AlarmManager manager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
|
||||
if (manager == null)
|
||||
return;
|
||||
|
||||
PendingIntent pending = PendingIntent.getService(this, 0, new Intent(UpdateServiceHelper.ACTION_CHECK_FOR_UPDATE, null, this, UpdateService.class), 0);
|
||||
manager.cancel(pending);
|
||||
|
||||
lastAttempt.setTimeInMillis(lastAttempt.getTimeInMillis() + interval);
|
||||
Log.i(LOGTAG, "next update will be at: " + lastAttempt.getTime());
|
||||
|
||||
manager.set(AlarmManager.RTC_WAKEUP, lastAttempt.getTimeInMillis(), pending);
|
||||
}
|
||||
|
||||
private void startUpdate(int flags) {
|
||||
setLastAttemptDate();
|
||||
|
||||
NetworkInfo netInfo = mConnectivityManager.getActiveNetworkInfo();
|
||||
if (netInfo == null || !netInfo.isConnected()) {
|
||||
Log.i(LOGTAG, "not connected to the network");
|
||||
registerForUpdates(true);
|
||||
sendCheckUpdateResult(false);
|
||||
return;
|
||||
}
|
||||
|
||||
registerForUpdates(false);
|
||||
|
||||
UpdateInfo info = findUpdate(hasFlag(flags, UpdateServiceHelper.FLAG_REINSTALL));
|
||||
boolean haveUpdate = (info != null);
|
||||
sendCheckUpdateResult(haveUpdate);
|
||||
|
||||
if (!haveUpdate) {
|
||||
Log.i(LOGTAG, "no update available");
|
||||
return;
|
||||
}
|
||||
|
||||
Log.i(LOGTAG, "update available, buildID = " + info.buildID);
|
||||
|
||||
int connectionType = netInfo.getType();
|
||||
if (!hasFlag(flags, UpdateServiceHelper.FLAG_FORCE_DOWNLOAD) &&
|
||||
connectionType != ConnectivityManager.TYPE_WIFI &&
|
||||
connectionType != ConnectivityManager.TYPE_ETHERNET) {
|
||||
Log.i(LOGTAG, "not connected via wifi or ethernet");
|
||||
|
||||
// We aren't autodownloading here, so prompt to start the update
|
||||
Notification notification = new Notification(R.drawable.icon, getResources().getString(R.string.updater_start_ticker), System.currentTimeMillis());
|
||||
|
||||
Intent notificationIntent = new Intent(UpdateServiceHelper.ACTION_CHECK_FOR_UPDATE);
|
||||
notificationIntent.setClass(this, UpdateService.class);
|
||||
notificationIntent.putExtra(UpdateServiceHelper.EXTRA_UPDATE_FLAGS_NAME, UpdateServiceHelper.FLAG_FORCE_DOWNLOAD);
|
||||
|
||||
PendingIntent contentIntent = PendingIntent.getService(this, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
notification.flags = Notification.FLAG_AUTO_CANCEL;
|
||||
|
||||
notification.setLatestEventInfo(this, getResources().getString(R.string.updater_start_title),
|
||||
getResources().getString(R.string.updater_start_select),
|
||||
contentIntent);
|
||||
|
||||
mNotificationManager.notify(NOTIFICATION_ID, notification);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
File pkg = downloadUpdatePackage(info, hasFlag(flags, UpdateServiceHelper.FLAG_OVERWRITE_EXISTING));
|
||||
if (pkg == null)
|
||||
return;
|
||||
|
||||
Log.i(LOGTAG, "have update package at " + pkg);
|
||||
|
||||
saveUpdateInfo(info);
|
||||
|
||||
// If we have root, we always apply the update immediately because it happens in the background
|
||||
if (mApplyImmediately || checkRoot()) {
|
||||
applyUpdate(pkg);
|
||||
} else {
|
||||
// Prompt to apply the update
|
||||
Notification notification = new Notification(R.drawable.icon, getResources().getString(R.string.updater_apply_ticker), System.currentTimeMillis());
|
||||
|
||||
Intent notificationIntent = new Intent(UpdateServiceHelper.ACTION_APPLY_UPDATE);
|
||||
notificationIntent.setClass(this, UpdateService.class);
|
||||
notificationIntent.putExtra(UpdateServiceHelper.EXTRA_PACKAGE_PATH_NAME, pkg.getAbsolutePath());
|
||||
|
||||
PendingIntent contentIntent = PendingIntent.getService(this, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
notification.flags = Notification.FLAG_AUTO_CANCEL;
|
||||
|
||||
notification.setLatestEventInfo(this, getResources().getString(R.string.updater_apply_title),
|
||||
getResources().getString(R.string.updater_apply_select),
|
||||
contentIntent);
|
||||
|
||||
mNotificationManager.notify(NOTIFICATION_ID, notification);
|
||||
}
|
||||
}
|
||||
|
||||
private UpdateInfo findUpdate(boolean force) {
|
||||
try {
|
||||
URL url = UpdateServiceHelper.getUpdateUrl(force);
|
||||
|
||||
DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
|
||||
Document dom = builder.parse(url.openConnection().getInputStream());
|
||||
|
||||
NodeList nodes = dom.getElementsByTagName("update");
|
||||
if (nodes == null || nodes.getLength() == 0)
|
||||
return null;
|
||||
|
||||
Node updateNode = nodes.item(0);
|
||||
Node buildIdNode = updateNode.getAttributes().getNamedItem("buildID");
|
||||
if (buildIdNode == null)
|
||||
return null;
|
||||
|
||||
nodes = dom.getElementsByTagName("patch");
|
||||
if (nodes == null || nodes.getLength() == 0)
|
||||
return null;
|
||||
|
||||
Node patchNode = nodes.item(0);
|
||||
Node urlNode = patchNode.getAttributes().getNamedItem("URL");
|
||||
Node hashFunctionNode = patchNode.getAttributes().getNamedItem("hashFunction");
|
||||
Node hashValueNode = patchNode.getAttributes().getNamedItem("hashValue");
|
||||
Node sizeNode = patchNode.getAttributes().getNamedItem("size");
|
||||
|
||||
if (urlNode == null || hashFunctionNode == null ||
|
||||
hashValueNode == null || sizeNode == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Fill in UpdateInfo from the XML data
|
||||
UpdateInfo info = new UpdateInfo();
|
||||
info.url = new URL(urlNode.getTextContent());
|
||||
info.buildID = buildIdNode.getTextContent();
|
||||
info.hashFunction = hashFunctionNode.getTextContent();
|
||||
info.hashValue = hashValueNode.getTextContent();
|
||||
|
||||
try {
|
||||
info.size = Integer.parseInt(sizeNode.getTextContent());
|
||||
} catch (NumberFormatException e) {
|
||||
Log.e(LOGTAG, "Failed to find APK size: ", e);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Make sure we have all the stuff we need to apply the update
|
||||
if (!info.isValid()) {
|
||||
Log.e(LOGTAG, "missing some required update information, have: " + info);
|
||||
return null;
|
||||
}
|
||||
|
||||
return info;
|
||||
} catch (Exception e) {
|
||||
Log.e(LOGTAG, "failed to check for update: ", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private MessageDigest createMessageDigest(String hashFunction) {
|
||||
String javaHashFunction = null;
|
||||
|
||||
if ("sha512".equals(hashFunction)) {
|
||||
javaHashFunction = "SHA-512";
|
||||
} else {
|
||||
Log.e(LOGTAG, "Unhandled hash function: " + hashFunction);
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return MessageDigest.getInstance(javaHashFunction);
|
||||
} catch (java.security.NoSuchAlgorithmException e) {
|
||||
Log.e(LOGTAG, "Couldn't find algorithm " + javaHashFunction, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void showDownloadNotification() {
|
||||
showDownloadNotification(null);
|
||||
}
|
||||
|
||||
private void showDownloadNotification(File downloadFile) {
|
||||
Notification notification = new Notification(android.R.drawable.stat_sys_download, getResources().getString(R.string.updater_downloading_ticker), System.currentTimeMillis());
|
||||
|
||||
Intent notificationIntent = new Intent(UpdateServiceHelper.ACTION_APPLY_UPDATE);
|
||||
notificationIntent.setClass(this, UpdateService.class);
|
||||
|
||||
if (downloadFile != null)
|
||||
notificationIntent.putExtra(UpdateServiceHelper.EXTRA_PACKAGE_PATH_NAME, downloadFile.getAbsolutePath());
|
||||
|
||||
PendingIntent contentIntent = PendingIntent.getService(this, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
|
||||
notification.setLatestEventInfo(this, getResources().getString(R.string.updater_downloading_title),
|
||||
mApplyImmediately ? getResources().getString(R.string.updater_downloading_willapply) :
|
||||
getResources().getString(R.string.updater_downloading_select),
|
||||
contentIntent);
|
||||
|
||||
mNotificationManager.notify(NOTIFICATION_ID, notification);
|
||||
}
|
||||
|
||||
private void showDownloadFailure() {
|
||||
Notification notification = new Notification(android.R.drawable.stat_sys_warning, getResources().getString(R.string.updater_downloading_ticker_failed), System.currentTimeMillis());
|
||||
|
||||
Intent notificationIntent = new Intent(UpdateServiceHelper.ACTION_CHECK_FOR_UPDATE);
|
||||
notificationIntent.setClass(this, UpdateService.class);
|
||||
|
||||
PendingIntent contentIntent = PendingIntent.getService(this, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
|
||||
notification.setLatestEventInfo(this, getResources().getString(R.string.updater_downloading_title),
|
||||
getResources().getString(R.string.updater_downloading_retry),
|
||||
contentIntent);
|
||||
|
||||
mNotificationManager.notify(NOTIFICATION_ID, notification);
|
||||
}
|
||||
|
||||
private File downloadUpdatePackage(UpdateInfo info, boolean overwriteExisting) {
|
||||
File downloadFile = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), new File(info.url.getFile()).getName());
|
||||
|
||||
if (!overwriteExisting && info.buildID.equals(getLastBuildID()) && downloadFile.exists()) {
|
||||
// The last saved buildID is the same as the one for the current update. We also have a file
|
||||
// already downloaded, so it's probably the package we want. Verify it to be sure and just
|
||||
// return that if it matches.
|
||||
|
||||
if (verifyDownloadedPackage(downloadFile)) {
|
||||
Log.i(LOGTAG, "using existing update package");
|
||||
return downloadFile;
|
||||
} else {
|
||||
// Didn't match, so we're going to download a new one.
|
||||
downloadFile.delete();
|
||||
}
|
||||
}
|
||||
|
||||
Log.i(LOGTAG, "downloading update package");
|
||||
|
||||
OutputStream output = null;
|
||||
InputStream input = null;
|
||||
|
||||
mDownloading = true;
|
||||
showDownloadNotification(downloadFile);
|
||||
|
||||
try {
|
||||
URLConnection conn = info.url.openConnection();
|
||||
int length = conn.getContentLength();
|
||||
|
||||
output = new BufferedOutputStream(new FileOutputStream(downloadFile));
|
||||
input = new BufferedInputStream(conn.getInputStream());
|
||||
|
||||
byte[] buf = new byte[BUFSIZE];
|
||||
int len = 0;
|
||||
|
||||
int bytesRead = 0;
|
||||
float lastPercent = 0.0f;
|
||||
|
||||
while ((len = input.read(buf, 0, BUFSIZE)) > 0) {
|
||||
output.write(buf, 0, len);
|
||||
bytesRead += len;
|
||||
}
|
||||
|
||||
Log.i(LOGTAG, "completed update download!");
|
||||
|
||||
mNotificationManager.cancel(NOTIFICATION_ID);
|
||||
|
||||
return downloadFile;
|
||||
} catch (Exception e) {
|
||||
downloadFile.delete();
|
||||
showDownloadFailure();
|
||||
|
||||
Log.e(LOGTAG, "failed to download update: ", e);
|
||||
return null;
|
||||
} finally {
|
||||
try {
|
||||
if (input != null)
|
||||
input.close();
|
||||
} catch (java.io.IOException e) {}
|
||||
|
||||
try {
|
||||
if (output != null)
|
||||
output.close();
|
||||
} catch (java.io.IOException e) {}
|
||||
|
||||
mDownloading = false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean verifyDownloadedPackage(File updateFile) {
|
||||
MessageDigest digest = createMessageDigest(getLastHashFunction());
|
||||
if (digest == null)
|
||||
return false;
|
||||
|
||||
InputStream input = null;
|
||||
|
||||
try {
|
||||
input = new BufferedInputStream(new FileInputStream(updateFile));
|
||||
|
||||
byte[] buf = new byte[BUFSIZE];
|
||||
int len;
|
||||
while ((len = input.read(buf, 0, BUFSIZE)) > 0) {
|
||||
digest.update(buf, 0, len);
|
||||
}
|
||||
} catch (java.io.IOException e) {
|
||||
Log.e(LOGTAG, "Failed to verify update package: ", e);
|
||||
return false;
|
||||
} finally {
|
||||
try {
|
||||
if (input != null)
|
||||
input.close();
|
||||
} catch(java.io.IOException e) {}
|
||||
}
|
||||
|
||||
String hex = Hex.encodeHexString(digest.digest());
|
||||
if (!hex.equals(getLastHashValue())) {
|
||||
Log.e(LOGTAG, "Package hash does not match");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void applyUpdate(String updatePath) {
|
||||
applyUpdate(new File(updatePath));
|
||||
}
|
||||
|
||||
private void applyUpdate(File updateFile) {
|
||||
mApplyImmediately = false;
|
||||
|
||||
if (!updateFile.exists())
|
||||
return;
|
||||
|
||||
Log.i(LOGTAG, "Verifying package: " + updateFile);
|
||||
|
||||
if (!verifyDownloadedPackage(updateFile)) {
|
||||
Log.e(LOGTAG, "Not installing update, failed verification");
|
||||
return;
|
||||
}
|
||||
|
||||
if (checkRoot())
|
||||
applyUpdateWithRoot(updateFile);
|
||||
else
|
||||
applyUpdateWithActivity(updateFile);
|
||||
}
|
||||
|
||||
private void applyUpdateWithRoot(File updateFile) {
|
||||
mNotificationManager.cancel(NOTIFICATION_ID);
|
||||
|
||||
Notification notification = new Notification(R.drawable.icon, getResources().getString(R.string.updater_installing_ticker), System.currentTimeMillis());
|
||||
notification.flags = Notification.FLAG_NO_CLEAR;
|
||||
|
||||
Intent notificationIntent = new Intent("org.mozilla.gecko.ACTION_NOOP");
|
||||
notificationIntent.setClass(this, UpdateService.class);
|
||||
|
||||
PendingIntent contentIntent = PendingIntent.getService(this, 0, notificationIntent, 0);
|
||||
notification.flags = Notification.FLAG_NO_CLEAR;
|
||||
|
||||
notification.setLatestEventInfo(this, getResources().getString(R.string.updater_installing_title),
|
||||
getResources().getString(R.string.updater_installing_text),
|
||||
contentIntent);
|
||||
|
||||
mNotificationManager.notify(NOTIFICATION_ID, notification);
|
||||
|
||||
int result = runAsRoot("pm install " + updateFile.getAbsolutePath());
|
||||
Log.i(LOGTAG, "install result = " + result);
|
||||
|
||||
updateFile.delete();
|
||||
|
||||
int tickerText = result == 0 ? R.string.updater_installing_ticker_success : R.string.updater_installing_ticker_fail;
|
||||
int contentText = result == 0 ? R.string.updater_installing_text_success : R.string.updater_installing_text_fail;
|
||||
|
||||
mNotificationManager.cancel(NOTIFICATION_ID);
|
||||
|
||||
notification = new Notification(R.drawable.icon, getResources().getString(tickerText), System.currentTimeMillis());
|
||||
notification.flags = Notification.FLAG_NO_CLEAR;
|
||||
|
||||
notificationIntent = new Intent(Intent.ACTION_MAIN);
|
||||
notificationIntent.setClassName(getPackageName(), getPackageName() + ".App");
|
||||
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
|
||||
contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
|
||||
notification.flags = Notification.FLAG_NO_CLEAR;
|
||||
|
||||
notification.setLatestEventInfo(this, getResources().getString(R.string.updater_installing_title),
|
||||
getResources().getString(result == 0 ? R.string.updater_installing_text_success : R.string.updater_installing_text_fail),
|
||||
contentIntent);
|
||||
|
||||
mNotificationManager.notify(NOTIFICATION_ID, notification);
|
||||
|
||||
|
||||
notification.setLatestEventInfo(this, getResources().getString(R.string.updater_installing_title),
|
||||
getResources().getString(result == 0 ? R.string.updater_installing_text_success : R.string.updater_installing_text_fail),
|
||||
contentIntent);
|
||||
notification.flags = Notification.FLAG_AUTO_CANCEL;
|
||||
mNotificationManager.notify(NOTIFICATION_ID, notification);
|
||||
}
|
||||
|
||||
private void applyUpdateWithActivity(File updateFile) {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW);
|
||||
intent.setDataAndType(Uri.fromFile(updateFile), "application/vnd.android.package-archive");
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
private String getLastBuildID() {
|
||||
return mPrefs.getString(KEY_LAST_BUILDID, null);
|
||||
}
|
||||
|
||||
private String getLastHashFunction() {
|
||||
return mPrefs.getString(KEY_LAST_HASH_FUNCTION, null);
|
||||
}
|
||||
|
||||
private String getLastHashValue() {
|
||||
return mPrefs.getString(KEY_LAST_HASH_VALUE, null);
|
||||
}
|
||||
|
||||
private Calendar getLastAttemptDate() {
|
||||
long lastAttempt = mPrefs.getLong(KEY_LAST_ATTEMPT_DATE, -1);
|
||||
if (lastAttempt < 0)
|
||||
return null;
|
||||
|
||||
GregorianCalendar cal = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
|
||||
cal.setTimeInMillis(lastAttempt);
|
||||
return cal;
|
||||
}
|
||||
|
||||
private void setLastAttemptDate() {
|
||||
SharedPreferences.Editor editor = mPrefs.edit();
|
||||
editor.putLong(KEY_LAST_ATTEMPT_DATE, System.currentTimeMillis());
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
private void saveUpdateInfo(UpdateInfo info) {
|
||||
SharedPreferences.Editor editor = mPrefs.edit();
|
||||
editor.putString(KEY_LAST_BUILDID, info.buildID);
|
||||
editor.putString(KEY_LAST_HASH_FUNCTION, info.hashFunction);
|
||||
editor.putString(KEY_LAST_HASH_VALUE, info.hashValue);
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
private int runAsRoot(String command) {
|
||||
Process p = null;
|
||||
try {
|
||||
p = Runtime.getRuntime().exec("su");
|
||||
OutputStream output = p.getOutputStream();
|
||||
output.write(command.getBytes());
|
||||
output.write(new String("; exit\n").getBytes());
|
||||
output.flush();
|
||||
p.waitFor();
|
||||
|
||||
return p.exitValue();
|
||||
} catch (Exception e) {
|
||||
return -1;
|
||||
} finally {
|
||||
if (p != null)
|
||||
p.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkRoot() {
|
||||
return runAsRoot("echo woooooo") == 0;
|
||||
}
|
||||
|
||||
private class UpdateInfo {
|
||||
public URL url;
|
||||
public String buildID;
|
||||
public String hashFunction;
|
||||
public String hashValue;
|
||||
public int size;
|
||||
|
||||
private boolean isNonEmpty(String s) {
|
||||
return s != null && s.length() > 0;
|
||||
}
|
||||
|
||||
public boolean isValid() {
|
||||
return url != null && isNonEmpty(buildID) &&
|
||||
isNonEmpty(hashFunction) && isNonEmpty(hashValue) && size > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "url = " + url + ", buildID = " + buildID + ", hashFunction = " + hashFunction + ", hashValue = " + hashValue + ", size = " + size;
|
||||
}
|
||||
}
|
||||
}
|
79
mobile/android/base/UpdateServiceHelper.java.in
Normal file
79
mobile/android/base/UpdateServiceHelper.java.in
Normal file
@ -0,0 +1,79 @@
|
||||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#filter substitution
|
||||
|
||||
package org.mozilla.gecko.updater;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import android.os.Build;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import java.net.URL;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
public class UpdateServiceHelper {
|
||||
public static final String ACTION_REGISTER_FOR_UPDATES = "@ANDROID_PACKAGE_NAME@.REGISTER_FOR_UPDATES";
|
||||
public static final String ACTION_UNREGISTER_FOR_UPDATES = "@ANDROID_PACKAGE_NAME@.UNREGISTER_FOR_UPDATES";
|
||||
public static final String ACTION_CHECK_FOR_UPDATE = "@ANDROID_PACKAGE_NAME@.CHECK_FOR_UPDATE";
|
||||
public static final String ACTION_CHECK_UPDATE_RESULT = "@ANDROID_PACKAGE_NAME@.CHECK_UPDATE_RESULT";
|
||||
public static final String ACTION_APPLY_UPDATE = "@ANDROID_PACKAGE_NAME@.APPLY_UPDATE";
|
||||
|
||||
// Flags for ACTION_CHECK_FOR_UPDATE
|
||||
public static final int FLAG_FORCE_DOWNLOAD = 1;
|
||||
public static final int FLAG_OVERWRITE_EXISTING = 1 << 1;
|
||||
public static final int FLAG_REINSTALL = 1 << 2;
|
||||
public static final int FLAG_RETRY = 1 << 3;
|
||||
|
||||
// Name of the Intent extra that holds the flags for ACTION_CHECK_FOR_UPDATE
|
||||
public static final String EXTRA_UPDATE_FLAGS_NAME = "updateFlags";
|
||||
|
||||
// Name of the Intent extra that holds the APK path, used with ACTION_APPLY_UPDATE
|
||||
public static final String EXTRA_PACKAGE_PATH_NAME = "packagePath";
|
||||
|
||||
public static final String UPDATE_CHANNEL = "@MOZ_UPDATE_CHANNEL@";
|
||||
|
||||
private static final String LOGTAG = "UpdateServiceHelper";
|
||||
private static final String BUILDID = "@MOZ_APP_BUILDID@";
|
||||
|
||||
#ifdef MOZ_PKG_SPECIAL
|
||||
private static final String UPDATE_URL = "https://aus2.mozilla.org/update/4/@MOZ_APP_BASENAME@/@MOZ_APP_VERSION@/%BUILDID%/Android_@MOZ_APP_ABI@-@MOZ_PKG_SPECIAL@/%LOCALE%/@MOZ_UPDATE_CHANNEL@/%OS_VERSION%/default/default/@MOZ_APP_VERSION@/update.xml";
|
||||
#else
|
||||
private static final String UPDATE_URL = "https://aus2.mozilla.org/update/4/@MOZ_APP_BASENAME@/@MOZ_APP_VERSION@/%BUILDID%/Android_@MOZ_APP_ABI@/%LOCALE%/@MOZ_UPDATE_CHANNEL@/%OS_VERSION%/default/default/@MOZ_APP_VERSION@/update.xml";
|
||||
#endif
|
||||
|
||||
public static URL getUpdateUrl(boolean force) {
|
||||
Locale locale = Locale.getDefault();
|
||||
String url = UPDATE_URL.replace("%LOCALE%", locale.getLanguage() + "-" + locale.getCountry()).
|
||||
replace("%OS_VERSION%", Build.VERSION.RELEASE).
|
||||
replace("%BUILDID%", force ? "0" : BUILDID);
|
||||
|
||||
try {
|
||||
return new URL(url);
|
||||
} catch (java.net.MalformedURLException e) {
|
||||
Log.e(LOGTAG, "Failed to create update url: ", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isUpdaterEnabled() {
|
||||
#ifdef MOZ_UPDATER
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void registerForUpdates(Context context) {
|
||||
if (!isUpdaterEnabled())
|
||||
return;
|
||||
|
||||
context.startService(new Intent(UpdateServiceHelper.ACTION_REGISTER_FOR_UPDATES, null, context, UpdateService.class));
|
||||
}
|
||||
}
|
@ -224,3 +224,27 @@ just addresses the organization to follow, e.g. "This site is run by " -->
|
||||
<!ENTITY bookmarkhistory_import_wait "Please wait...">
|
||||
|
||||
<!ENTITY webapp_generic_name "App">
|
||||
|
||||
<!-- Updater notifications -->
|
||||
<!ENTITY updater_start_title "&brandShortName;">
|
||||
<!ENTITY updater_start_ticker "&brandShortName; update available…">
|
||||
<!ENTITY updater_start_select "Select to download update.">
|
||||
|
||||
<!ENTITY updater_downloading_title "Downloading &brandShortName;">
|
||||
<!ENTITY updater_downloading_ticker "Downloading &brandShortName; update…">
|
||||
<!ENTITY updater_downloading_ticker_failed "Failed to download &brandShortName; update…">
|
||||
<!ENTITY updater_downloading_select "Select to apply update when complete.">
|
||||
<!ENTITY updater_downloading_retry "Select to retry update download.">
|
||||
<!ENTITY updater_downloading_willapply "Waiting for download to complete.">
|
||||
|
||||
<!ENTITY updater_apply_title "&brandShortName;">
|
||||
<!ENTITY updater_apply_ticker "&brandShortName; update available…">
|
||||
<!ENTITY updater_apply_select "Select to apply downloaded update.">
|
||||
|
||||
<!ENTITY updater_installing_title "&brandShortName;">
|
||||
<!ENTITY updater_installing_ticker "Updating &brandShortName;…">
|
||||
<!ENTITY updater_installing_ticker_success "Successfully updated &brandShortName;">
|
||||
<!ENTITY updater_installing_ticker_fail "Failed to update &brandShortName;">
|
||||
<!ENTITY updater_installing_text "Installing update…">
|
||||
<!ENTITY updater_installing_text_success "Succesfully updated.">
|
||||
<!ENTITY updater_installing_text_fail "Failed to install update.">
|
||||
|
@ -210,4 +210,29 @@
|
||||
<string name="bookmarkhistory_import_wait">&bookmarkhistory_import_wait;</string>
|
||||
|
||||
<string name="webapp_generic_name">&webapp_generic_name;</string>
|
||||
|
||||
<!-- Updater notifications -->
|
||||
<string name="updater_start_title">&updater_start_title;</string>
|
||||
<string name="updater_start_ticker">&updater_start_ticker;</string>
|
||||
<string name="updater_start_select">&updater_start_select;</string>
|
||||
|
||||
<string name="updater_downloading_title">&updater_downloading_title;</string>
|
||||
<string name="updater_downloading_ticker">&updater_downloading_ticker;</string>
|
||||
<string name="updater_downloading_ticker_failed">&updater_downloading_ticker_failed;</string>
|
||||
<string name="updater_downloading_select">&updater_downloading_select;</string>
|
||||
<string name="updater_downloading_retry">&updater_downloading_retry;</string>
|
||||
<string name="updater_downloading_willapply">&updater_downloading_willapply;</string>
|
||||
|
||||
<string name="updater_apply_title">&updater_apply_title;</string>
|
||||
<string name="updater_apply_ticker">&updater_apply_ticker;</string>
|
||||
<string name="updater_apply_select">&updater_apply_select;</string>
|
||||
|
||||
<string name="updater_installing_title">&updater_installing_title;</string>
|
||||
<string name="updater_installing_ticker">&updater_installing_ticker;</string>
|
||||
<string name="updater_installing_ticker_success">&updater_installing_ticker_success;</string>
|
||||
<string name="updater_installing_ticker_fail">&updater_installing_ticker_fail;</string>
|
||||
<string name="updater_installing_text">&updater_installing_text;</string>
|
||||
<string name="updater_installing_text_success">&updater_installing_text_success;</string>
|
||||
<string name="updater_installing_text_fail">&updater_installing_text_fail;</string>
|
||||
|
||||
</resources>
|
||||
|
@ -101,80 +101,29 @@
|
||||
isChecking: false,
|
||||
update: null,
|
||||
|
||||
get updateEnabled() {
|
||||
try {
|
||||
return Services.prefs.getBoolPref("app.update.enabled");
|
||||
}
|
||||
catch (e) { }
|
||||
return true; // Mobile default is true
|
||||
init: function() {
|
||||
Services.obs.addObserver(this, "Update:CheckResult", false);
|
||||
},
|
||||
|
||||
startUpdate: function() {
|
||||
if (!this.update)
|
||||
this.update = this.um.activeUpdate;
|
||||
|
||||
this.aus.pauseDownload();
|
||||
|
||||
let updateTimerCallback = this.aus.QueryInterface(Ci.nsITimerCallback);
|
||||
updateTimerCallback.notify(null);
|
||||
}
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
if (aTopic == "Update:CheckResult") {
|
||||
showUpdateMessage(aData == "true");
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(Updater, "aus",
|
||||
"@mozilla.org/updates/update-service;1",
|
||||
"nsIApplicationUpdateService");
|
||||
XPCOMUtils.defineLazyServiceGetter(Updater, "checker",
|
||||
"@mozilla.org/updates/update-checker;1",
|
||||
"nsIUpdateChecker");
|
||||
XPCOMUtils.defineLazyServiceGetter(Updater, "um",
|
||||
"@mozilla.org/updates/update-manager;1",
|
||||
"nsIUpdateManager");
|
||||
|
||||
let UpdateCheckListener = {
|
||||
onProgress: function(aRequest, aPosition, aTotalSize) {
|
||||
},
|
||||
|
||||
onCheckComplete: function(aRequest, aUpdates, aUpdateCount) {
|
||||
Updater.isChecking = false;
|
||||
|
||||
Updater.update = Updater.aus.selectUpdate(aUpdates, aUpdates.length);
|
||||
if (!Updater.update || !Updater.aus.canApplyUpdates) {
|
||||
showUpdateMessage(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Updater.update.appVersion || Services.vc.compare(Updater.update.appVersion, Services.appinfo.version) < 0) {
|
||||
showUpdateMessage(false);
|
||||
return;
|
||||
}
|
||||
|
||||
showUpdateMessage(true);
|
||||
Updater.startUpdate();
|
||||
},
|
||||
|
||||
onError: function(aRequest, aUpdate) {
|
||||
// Errors in the update check are treated as no updates found. If the
|
||||
// update check fails repeatedly without a success the user will be
|
||||
// notified with the normal app update user interface so this is safe.
|
||||
Updater.isChecking = false;
|
||||
showUpdateMessage(false);
|
||||
},
|
||||
|
||||
QueryInterface: function(aIID) {
|
||||
if (!aIID.equals(Ci.nsIUpdateCheckListener) && !aIID.equals(Ci.nsISupports))
|
||||
throw Cr.NS_ERROR_NO_INTERFACE;
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
if (!Updater.updateEnabled)
|
||||
document.getElementById("updateBox").style.display = "none";
|
||||
Updater.init();
|
||||
|
||||
function checkForUpdates() {
|
||||
Updater.isChecking = true;
|
||||
showCheckingMessage();
|
||||
|
||||
Updater.checker.checkForUpdates(UpdateCheckListener, true);
|
||||
let bridge = Cc["@mozilla.org/android/bridge;1"].getService(Ci.nsIAndroidBridge);
|
||||
bridge.handleGeckoMessage(JSON.stringify({
|
||||
gecko: {
|
||||
type: "Update:Check",
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
let updateLink = document.getElementById("updateLink");
|
||||
|
@ -43,8 +43,4 @@ ifdef MOZ_SAFE_BROWSING
|
||||
EXTRA_COMPONENTS += SafeBrowsing.js
|
||||
endif
|
||||
|
||||
ifdef MOZ_UPDATER
|
||||
EXTRA_COMPONENTS += UpdatePrompt.js
|
||||
endif
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
@ -96,9 +96,3 @@ contract @mozilla.org/safebrowsing/application;1 {aadaed90-6c03-42d0-924a-fc6119
|
||||
category app-startup SafeBrowsing service,@mozilla.org/safebrowsing/application;1
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_UPDATER
|
||||
# UpdatePrompt.js
|
||||
component {88b3eb21-d072-4e3b-886d-f89d8c49fe59} UpdatePrompt.js
|
||||
contract @mozilla.org/updates/update-prompt;1 {88b3eb21-d072-4e3b-886d-f89d8c49fe59}
|
||||
#endif
|
||||
|
||||
|
@ -1,316 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
|
||||
const UPDATE_NOTIFICATION_NAME = "update-app";
|
||||
const UPDATE_NOTIFICATION_ICON = "drawable://alert_download";
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "gUpdateBundle", function aus_gUpdateBundle() {
|
||||
return Services.strings.createBundle("chrome://mozapps/locale/update/updates.properties");
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "gBrandBundle", function aus_gBrandBundle() {
|
||||
return Services.strings.createBundle("chrome://branding/locale/brand.properties");
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "gBrowserBundle", function aus_gBrowserBundle() {
|
||||
return Services.strings.createBundle("chrome://browser/locale/browser.properties");
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "AddonManager", function() {
|
||||
Cu.import("resource://gre/modules/AddonManager.jsm");
|
||||
return AddonManager;
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "LocaleRepository", function() {
|
||||
Cu.import("resource://gre/modules/LocaleRepository.jsm");
|
||||
return LocaleRepository;
|
||||
});
|
||||
|
||||
function getPref(func, preference, defaultValue) {
|
||||
try {
|
||||
return Services.prefs[func](preference);
|
||||
} catch (e) {}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
function sendMessageToJava(aMsg) {
|
||||
let data = Cc["@mozilla.org/android/bridge;1"].getService(Ci.nsIAndroidBridge).handleGeckoMessage(JSON.stringify(aMsg));
|
||||
return JSON.parse(data);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Update Prompt
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
function UpdatePrompt() { }
|
||||
|
||||
UpdatePrompt.prototype = {
|
||||
classID: Components.ID("{88b3eb21-d072-4e3b-886d-f89d8c49fe59}"),
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIUpdatePrompt, Ci.nsIRequestObserver, Ci.nsIProgressEventSink]),
|
||||
|
||||
get _enabled() {
|
||||
return !getPref("getBoolPref", "app.update.silent", false);
|
||||
},
|
||||
|
||||
_showNotification: function UP__showNotif(aUpdate, aTitle, aText, aImageUrl, aMode) {
|
||||
let observer = {
|
||||
updatePrompt: this,
|
||||
observe: function (aSubject, aTopic, aData) {
|
||||
switch (aTopic) {
|
||||
case "alertclickcallback":
|
||||
this.updatePrompt._handleUpdate(aUpdate, aMode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let notifier = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService);
|
||||
notifier.showAlertNotification(aImageUrl, aTitle, aText, true, "", observer, UPDATE_NOTIFICATION_NAME);
|
||||
},
|
||||
|
||||
_handleUpdate: function UP__handleUpdate(aUpdate, aMode) {
|
||||
if (aMode == "available") {
|
||||
let window = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
let title = gUpdateBundle.GetStringFromName("updatesfound_" + aUpdate.type + ".title");
|
||||
let brandName = gBrandBundle.GetStringFromName("brandShortName");
|
||||
|
||||
// Unconditionally use the "major" type here as for now it is always a new version
|
||||
// without additional description required for a minor update message
|
||||
let message = gUpdateBundle.formatStringFromName("intro_major", [brandName, aUpdate.displayVersion], 2);
|
||||
let button0 = gUpdateBundle.GetStringFromName("okButton");
|
||||
let button1 = gUpdateBundle.GetStringFromName("askLaterButton");
|
||||
let prompt = Services.prompt;
|
||||
let flags = prompt.BUTTON_POS_0 * prompt.BUTTON_TITLE_IS_STRING + prompt.BUTTON_POS_1 * prompt.BUTTON_TITLE_IS_STRING;
|
||||
|
||||
let download = (prompt.confirmEx(window, title, message, flags, button0, button1, null, null, {value: false}) == 0);
|
||||
if (download) {
|
||||
// Start downloading the update in the background
|
||||
let aus = Cc["@mozilla.org/updates/update-service;1"].getService(Ci.nsIApplicationUpdateService);
|
||||
if (aus.downloadUpdate(aUpdate, true) != "failed") {
|
||||
let title = gBrowserBundle.formatStringFromName("alertDownloadsStart", [aUpdate.name], 1);
|
||||
this._showNotification(aUpdate, title, "", UPDATE_NOTIFICATION_ICON, "download");
|
||||
|
||||
// Add this UI as a listener for active downloads
|
||||
aus.addDownloadListener(this);
|
||||
}
|
||||
}
|
||||
} else if(aMode == "downloaded") {
|
||||
// Notify all windows that an application quit has been requested
|
||||
let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"].createInstance(Ci.nsISupportsPRBool);
|
||||
Services.obs.notifyObservers(cancelQuit, "quit-application-requested", "restart");
|
||||
|
||||
// If nothing aborted, restart the app
|
||||
if (cancelQuit.data == false) {
|
||||
sendMessageToJava({
|
||||
gecko: {
|
||||
type: "Update:Restart"
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_updateDownloadProgress: function UP__updateDownloadProgress(aProgress, aTotal) {
|
||||
let alertsService = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService);
|
||||
let progressListener = alertsService.QueryInterface(Ci.nsIAlertsProgressListener);
|
||||
if (progressListener)
|
||||
progressListener.onProgress(UPDATE_NOTIFICATION_NAME, aProgress, aTotal);
|
||||
},
|
||||
|
||||
// -------------------------
|
||||
// nsIUpdatePrompt interface
|
||||
// -------------------------
|
||||
|
||||
// Right now this is used only to check for updates in progress
|
||||
checkForUpdates: function UP_checkForUpdates() {
|
||||
if (!this._enabled)
|
||||
return;
|
||||
|
||||
let aus = Cc["@mozilla.org/updates/update-service;1"].getService(Ci.nsIApplicationUpdateService);
|
||||
if (!aus.isDownloading)
|
||||
return;
|
||||
|
||||
let updateManager = Cc["@mozilla.org/updates/update-manager;1"].getService(Ci.nsIUpdateManager);
|
||||
|
||||
let updateName = updateManager.activeUpdate ? updateManager.activeUpdate.name : gBrandBundle.GetStringFromName("brandShortName");
|
||||
let title = gBrowserBundle.formatStringFromName("alertDownloadsStart", [updateName], 1);
|
||||
|
||||
this._showNotification(updateManager.activeUpdate, title, "", UPDATE_NOTIFICATION_ICON, "downloading");
|
||||
|
||||
aus.removeDownloadListener(this); // just in case it's already added
|
||||
aus.addDownloadListener(this);
|
||||
},
|
||||
|
||||
showUpdateAvailable: function UP_showUpdateAvailable(aUpdate) {
|
||||
if (!this._enabled)
|
||||
return;
|
||||
|
||||
const PREF_APP_UPDATE_SKIPNOTIFICATION = "app.update.skipNotification";
|
||||
|
||||
if (Services.prefs.prefHasUserValue(PREF_APP_UPDATE_SKIPNOTIFICATION) &&
|
||||
Services.prefs.getBoolPref(PREF_APP_UPDATE_SKIPNOTIFICATION)) {
|
||||
Services.prefs.setBoolPref(PREF_APP_UPDATE_SKIPNOTIFICATION, false);
|
||||
|
||||
// Notification was already displayed and clicked, so jump to the next step:
|
||||
// ask the user about downloading update
|
||||
this._handleUpdate(aUpdate, "available");
|
||||
return;
|
||||
}
|
||||
|
||||
let stringsPrefix = "updateAvailable_" + aUpdate.type + ".";
|
||||
let title = gUpdateBundle.formatStringFromName(stringsPrefix + "title", [aUpdate.name], 1);
|
||||
let text = gUpdateBundle.GetStringFromName(stringsPrefix + "text");
|
||||
let imageUrl = "";
|
||||
this._showNotification(aUpdate, title, text, imageUrl, "available");
|
||||
},
|
||||
|
||||
showUpdateDownloaded: function UP_showUpdateDownloaded(aUpdate, aBackground) {
|
||||
if (!this._enabled)
|
||||
return;
|
||||
|
||||
// uninstall all installed locales
|
||||
AddonManager.getAddonsByTypes(["locale"], (function (aAddons) {
|
||||
if (aAddons.length > 0) {
|
||||
let listener = this.getAddonListener(aUpdate, this);
|
||||
AddonManager.addAddonListener(listener);
|
||||
aAddons.forEach(function(aAddon) {
|
||||
listener._uninstalling.push(aAddon.id);
|
||||
aAddon.uninstall();
|
||||
}, this);
|
||||
} else {
|
||||
this._showDownloadedNotification(aUpdate);
|
||||
}
|
||||
}).bind(this));
|
||||
},
|
||||
|
||||
_showDownloadedNotification: function UP_showDlNotification(aUpdate) {
|
||||
let stringsPrefix = "updateDownloaded_" + aUpdate.type + ".";
|
||||
let title = gUpdateBundle.formatStringFromName(stringsPrefix + "title", [aUpdate.name], 1);
|
||||
let text = gUpdateBundle.GetStringFromName(stringsPrefix + "text");
|
||||
let imageUrl = "";
|
||||
this._showNotification(aUpdate, title, text, imageUrl, "downloaded");
|
||||
},
|
||||
|
||||
_uninstalling: [],
|
||||
_installing: [],
|
||||
_currentUpdate: null,
|
||||
|
||||
_reinstallLocales: function UP_reinstallLocales(aUpdate, aListener, aPending) {
|
||||
LocaleRepository.getLocales((function(aLocales) {
|
||||
aLocales.forEach(function(aLocale, aIndex, aArray) {
|
||||
let index = aPending.indexOf(aLocale.addon.id);
|
||||
if (index > -1) {
|
||||
aListener._installing.push(aLocale.addon.id);
|
||||
aLocale.addon.install.install();
|
||||
}
|
||||
}, this);
|
||||
// store the buildid of these locales so that we can disable locales when the
|
||||
// user updates through a non-updater channel
|
||||
Services.prefs.setCharPref("extensions.compatability.locales.buildid", aUpdate.buildID);
|
||||
}).bind(this), { buildID: aUpdate.buildID });
|
||||
},
|
||||
|
||||
showUpdateInstalled: function UP_showUpdateInstalled() {
|
||||
if (!this._enabled || !getPref("getBoolPref", "app.update.showInstalledUI", false))
|
||||
return;
|
||||
|
||||
let title = gBrandBundle.GetStringFromName("brandShortName");
|
||||
let text = gUpdateBundle.GetStringFromName("installSuccess");
|
||||
let imageUrl = "";
|
||||
this._showNotification(aUpdate, title, text, imageUrl, "installed");
|
||||
},
|
||||
|
||||
showUpdateError: function UP_showUpdateError(aUpdate) {
|
||||
if (!this._enabled)
|
||||
return;
|
||||
|
||||
if (aUpdate.state == "failed") {
|
||||
var title = gBrandBundle.GetStringFromName("brandShortName");
|
||||
let text = gUpdateBundle.GetStringFromName("updaterIOErrorTitle");
|
||||
let imageUrl = "";
|
||||
this._showNotification(aUpdate, title, text, imageUrl, "error");
|
||||
}
|
||||
},
|
||||
|
||||
showUpdateHistory: function UP_showUpdateHistory(aParent) {
|
||||
// NOT IMPL
|
||||
},
|
||||
|
||||
// ----------------------------
|
||||
// nsIRequestObserver interface
|
||||
// ----------------------------
|
||||
|
||||
// When the data transfer begins
|
||||
onStartRequest: function(request, context) {
|
||||
// NOT IMPL
|
||||
},
|
||||
|
||||
// When the data transfer ends
|
||||
onStopRequest: function(request, context, status) {
|
||||
let alertsService = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService);
|
||||
let progressListener = alertsService.QueryInterface(Ci.nsIAlertsProgressListener);
|
||||
if (progressListener)
|
||||
progressListener.onCancel(UPDATE_NOTIFICATION_NAME);
|
||||
|
||||
|
||||
let aus = Cc["@mozilla.org/updates/update-service;1"].getService(Ci.nsIApplicationUpdateService);
|
||||
aus.removeDownloadListener(this);
|
||||
},
|
||||
|
||||
// ------------------------------
|
||||
// nsIProgressEventSink interface
|
||||
// ------------------------------
|
||||
|
||||
// When new data has been downloaded
|
||||
onProgress: function(request, context, progress, maxProgress) {
|
||||
this._updateDownloadProgress(progress, maxProgress);
|
||||
},
|
||||
|
||||
// When we have new status text
|
||||
onStatus: function(request, context, status, statusText) {
|
||||
// NOT IMPL
|
||||
},
|
||||
|
||||
// -------------------------------
|
||||
// AddonListener
|
||||
// -------------------------------
|
||||
getAddonListener: function(aUpdate, aUpdatePrompt) {
|
||||
return {
|
||||
_installing: [],
|
||||
_uninstalling: [],
|
||||
onInstalling: function(aAddon, aNeedsRestart) {
|
||||
let index = this._installing.indexOf(aAddon.id);
|
||||
if (index > -1)
|
||||
this._installing.splice(index, 1);
|
||||
|
||||
if (this._installing.length == 0) {
|
||||
aUpdatePrompt._showDownloadedNotification(aUpdate);
|
||||
AddonManager.removeAddonListener(this);
|
||||
}
|
||||
},
|
||||
|
||||
onUninstalling: function(aAddon, aNeedsRestart) {
|
||||
let pending = [];
|
||||
let index = this._uninstalling.indexOf(aAddon.id);
|
||||
if (index > -1) {
|
||||
pending.push(aAddon.id);
|
||||
this._uninstalling.splice(index, 1);
|
||||
}
|
||||
if (this._uninstalling.length == 0)
|
||||
aUpdatePrompt._reinstallLocales(aUpdate, this, pending);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const NSGetFactory = XPCOMUtils.generateNSGetFactory([UpdatePrompt]);
|
@ -516,9 +516,6 @@ bin/components/@DLL_PREFIX@nkgnomevfs@DLL_SUFFIX@
|
||||
#ifdef MOZ_SAFE_BROWSING
|
||||
@BINPATH@/components/SafeBrowsing.js
|
||||
#endif
|
||||
#ifdef MOZ_UPDATER
|
||||
@BINPATH@/components/UpdatePrompt.js
|
||||
#endif
|
||||
@BINPATH@/components/XPIDialogService.js
|
||||
@BINPATH@/components/browsercomps.xpt
|
||||
@BINPATH@/extensions/feedback@mobile.mozilla.org.xpi
|
||||
|
Loading…
Reference in New Issue
Block a user