mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-08 00:38:39 +09:00
pt 1 in reviving the android build (copied from pm 28a1, wish me luck)
This commit is contained in:
parent
efa9662725
commit
d7788a6d6d
4249 changed files with 468189 additions and 0 deletions
|
|
@ -0,0 +1,120 @@
|
|||
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; 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 android.content.Context;
|
||||
import android.content.res.AssetManager;
|
||||
import android.content.SharedPreferences;
|
||||
import android.util.Log;
|
||||
|
||||
import com.keepsafe.switchboard.SwitchBoard;
|
||||
|
||||
import org.mozilla.gecko.AppConstants;
|
||||
import org.mozilla.gecko.BrowserApp;
|
||||
import org.mozilla.gecko.delegates.BrowserAppDelegateWithReference;
|
||||
import org.mozilla.gecko.GeckoSharedPrefs;
|
||||
import org.mozilla.gecko.preferences.GeckoPreferences;
|
||||
import org.mozilla.gecko.util.IOUtils;
|
||||
import org.mozilla.gecko.util.ThreadUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Enumeration;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
/**
|
||||
* Perform tasks in the background after the app has been installed/updated.
|
||||
*/
|
||||
public class PostUpdateHandler extends BrowserAppDelegateWithReference {
|
||||
private static final String LOGTAG = "PostUpdateHandler";
|
||||
|
||||
@Override
|
||||
public void onStart(final BrowserApp browserApp) {
|
||||
ThreadUtils.postToBackgroundThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
final SharedPreferences prefs = GeckoSharedPrefs.forApp(browserApp);
|
||||
|
||||
// Check if this is a new installation or if the app has been updated since the last start.
|
||||
if (!AppConstants.MOZ_APP_BUILDID.equals(prefs.getString(GeckoPreferences.PREFS_APP_UPDATE_LAST_BUILD_ID, null))) {
|
||||
Log.d(LOGTAG, "Build ID changed since last start: '" + AppConstants.MOZ_APP_BUILDID + "', '" + prefs.getString(GeckoPreferences.PREFS_APP_UPDATE_LAST_BUILD_ID, null) + "'");
|
||||
|
||||
// Copy the bundled system add-ons from the APK to the data directory.
|
||||
copyFeaturesFromAPK(browserApp);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies the /assets/features folder out of the APK and into the app's data directory.
|
||||
*/
|
||||
private void copyFeaturesFromAPK(BrowserApp browserApp) {
|
||||
Log.d(LOGTAG, "Copying system add-ons from APK to dataDir");
|
||||
|
||||
final String dataDir = browserApp.getApplicationInfo().dataDir;
|
||||
final SharedPreferences prefs = GeckoSharedPrefs.forApp(browserApp);
|
||||
final AssetManager assetManager = browserApp.getContext().getAssets();
|
||||
|
||||
try {
|
||||
final String[] assetNames = assetManager.list("features");
|
||||
|
||||
for (int i = 0; i < assetNames.length; i++) {
|
||||
final String assetPath = "features/" + assetNames[i];
|
||||
|
||||
Log.d(LOGTAG, "Copying '" + assetPath + "' from APK to dataDir");
|
||||
|
||||
final InputStream assetStream = assetManager.open(assetPath);
|
||||
final File outFile = getDataFile(dataDir, assetPath);
|
||||
|
||||
if (outFile == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final OutputStream outStream = new FileOutputStream(outFile);
|
||||
|
||||
try {
|
||||
IOUtils.copy(assetStream, outStream);
|
||||
} catch (IOException e) {
|
||||
Log.e(LOGTAG, "Error copying '" + assetPath + "' from APK to dataDir");
|
||||
} finally {
|
||||
outStream.close();
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.e(LOGTAG, "Error retrieving packaged system add-ons from APK", e);
|
||||
}
|
||||
|
||||
// Save the Build ID so we don't perform post-update operations again until the app is updated.
|
||||
prefs.edit().putString(GeckoPreferences.PREFS_APP_UPDATE_LAST_BUILD_ID, AppConstants.MOZ_APP_BUILDID).apply();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a File instance in the data directory, ensuring
|
||||
* that the parent exists.
|
||||
*
|
||||
* @return null if the parents could not be created.
|
||||
*/
|
||||
private File getDataFile(final String dataDir, final String name) {
|
||||
File outFile = new File(dataDir, name);
|
||||
File dir = outFile.getParentFile();
|
||||
|
||||
if (!dir.exists()) {
|
||||
Log.d(LOGTAG, "Creating " + dir.getAbsolutePath());
|
||||
if (!dir.mkdirs()) {
|
||||
Log.e(LOGTAG, "Unable to create directories: " + dir.getAbsolutePath());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return outFile;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,795 @@
|
|||
/* -*- 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.AppConstants;
|
||||
import org.mozilla.gecko.CrashHandler;
|
||||
import org.mozilla.gecko.R;
|
||||
|
||||
import org.mozilla.apache.commons.codec.binary.Hex;
|
||||
|
||||
import org.mozilla.gecko.permissions.Permissions;
|
||||
import org.mozilla.gecko.util.ProxySelector;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
|
||||
import android.Manifest;
|
||||
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.net.wifi.WifiManager;
|
||||
import android.net.wifi.WifiManager.WifiLock;
|
||||
import android.os.Environment;
|
||||
import android.provider.Settings;
|
||||
import android.support.v4.app.NotificationManagerCompat;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v4.net.ConnectivityManagerCompat;
|
||||
import android.support.v4.app.NotificationCompat;
|
||||
import android.support.v4.app.NotificationCompat.Builder;
|
||||
import android.util.Log;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.Calendar;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.List;
|
||||
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_FILE_NAME = "UpdateService.lastFileName";
|
||||
private static final String KEY_LAST_ATTEMPT_DATE = "UpdateService.lastAttemptDate";
|
||||
private static final String KEY_AUTODOWNLOAD_POLICY = "UpdateService.autoDownloadPolicy";
|
||||
private static final String KEY_UPDATE_URL = "UpdateService.updateUrl";
|
||||
|
||||
private SharedPreferences mPrefs;
|
||||
|
||||
private NotificationManagerCompat mNotificationManager;
|
||||
private ConnectivityManager mConnectivityManager;
|
||||
private Builder mBuilder;
|
||||
|
||||
private volatile WifiLock mWifiLock;
|
||||
|
||||
private boolean mDownloading;
|
||||
private boolean mCancelDownload;
|
||||
private boolean mApplyImmediately;
|
||||
|
||||
private CrashHandler mCrashHandler;
|
||||
|
||||
public enum AutoDownloadPolicy {
|
||||
NONE(-1),
|
||||
WIFI(0),
|
||||
DISABLED(1),
|
||||
ENABLED(2);
|
||||
|
||||
public final int value;
|
||||
|
||||
private AutoDownloadPolicy(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
private final static AutoDownloadPolicy[] sValues = AutoDownloadPolicy.values();
|
||||
|
||||
public static AutoDownloadPolicy get(int value) {
|
||||
for (AutoDownloadPolicy id: sValues) {
|
||||
if (id.value == value) {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
return NONE;
|
||||
}
|
||||
|
||||
public static AutoDownloadPolicy get(String name) {
|
||||
for (AutoDownloadPolicy id: sValues) {
|
||||
if (name.equalsIgnoreCase(id.toString())) {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
return NONE;
|
||||
}
|
||||
}
|
||||
|
||||
private enum CheckUpdateResult {
|
||||
// Keep these in sync with mobile/android/chrome/content/about.xhtml
|
||||
NOT_AVAILABLE,
|
||||
AVAILABLE,
|
||||
DOWNLOADING,
|
||||
DOWNLOADED
|
||||
}
|
||||
|
||||
|
||||
public UpdateService() {
|
||||
super("updater");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate () {
|
||||
mCrashHandler = CrashHandler.createDefaultCrashHandler(getApplicationContext());
|
||||
|
||||
super.onCreate();
|
||||
|
||||
mPrefs = getSharedPreferences(PREFS_NAME, 0);
|
||||
mNotificationManager = NotificationManagerCompat.from(this);
|
||||
mConnectivityManager = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
mWifiLock = ((WifiManager)getSystemService(Context.WIFI_SERVICE))
|
||||
.createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, PREFS_NAME);
|
||||
mCancelDownload = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
mCrashHandler.unregister();
|
||||
mCrashHandler = null;
|
||||
|
||||
if (mWifiLock.isHeld()) {
|
||||
mWifiLock.release();
|
||||
}
|
||||
}
|
||||
|
||||
@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 if (UpdateServiceHelper.ACTION_CANCEL_DOWNLOAD.equals(intent.getAction())) {
|
||||
mCancelDownload = true;
|
||||
} else {
|
||||
super.onStartCommand(intent, flags, startId);
|
||||
}
|
||||
|
||||
return Service.START_REDELIVER_INTENT;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onHandleIntent (final Intent intent) {
|
||||
if (UpdateServiceHelper.ACTION_REGISTER_FOR_UPDATES.equals(intent.getAction())) {
|
||||
AutoDownloadPolicy policy = AutoDownloadPolicy.get(
|
||||
intent.getIntExtra(UpdateServiceHelper.EXTRA_AUTODOWNLOAD_NAME,
|
||||
AutoDownloadPolicy.NONE.value));
|
||||
|
||||
if (policy != AutoDownloadPolicy.NONE) {
|
||||
setAutoDownloadPolicy(policy);
|
||||
}
|
||||
|
||||
String url = intent.getStringExtra(UpdateServiceHelper.EXTRA_UPDATE_URL_NAME);
|
||||
if (url != null) {
|
||||
setUpdateUrl(url);
|
||||
}
|
||||
|
||||
registerForUpdates(false);
|
||||
} else if (UpdateServiceHelper.ACTION_CHECK_FOR_UPDATE.equals(intent.getAction())) {
|
||||
startUpdate(intent.getIntExtra(UpdateServiceHelper.EXTRA_UPDATE_FLAGS_NAME, 0));
|
||||
// Use this instead for forcing a download from about:fennec
|
||||
// startUpdate(UpdateServiceHelper.FLAG_FORCE_DOWNLOAD | UpdateServiceHelper.FLAG_REINSTALL);
|
||||
} else if (UpdateServiceHelper.ACTION_DOWNLOAD_UPDATE.equals(intent.getAction())) {
|
||||
// We always want to do the download and apply it here
|
||||
mApplyImmediately = true;
|
||||
startUpdate(UpdateServiceHelper.FLAG_FORCE_DOWNLOAD);
|
||||
} 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(CheckUpdateResult result) {
|
||||
Intent resultIntent = new Intent(UpdateServiceHelper.ACTION_CHECK_UPDATE_RESULT);
|
||||
resultIntent.putExtra("result", result.toString());
|
||||
sendBroadcast(resultIntent);
|
||||
}
|
||||
|
||||
private int getUpdateInterval(boolean isRetry) {
|
||||
int interval;
|
||||
if (isRetry) {
|
||||
interval = INTERVAL_RETRY;
|
||||
} else if (!AppConstants.RELEASE_OR_BETA) {
|
||||
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), PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
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(final int flags) {
|
||||
setLastAttemptDate();
|
||||
|
||||
NetworkInfo netInfo = mConnectivityManager.getActiveNetworkInfo();
|
||||
if (netInfo == null || !netInfo.isConnected()) {
|
||||
Log.i(LOGTAG, "not connected to the network");
|
||||
registerForUpdates(true);
|
||||
sendCheckUpdateResult(CheckUpdateResult.NOT_AVAILABLE);
|
||||
return;
|
||||
}
|
||||
|
||||
registerForUpdates(false);
|
||||
|
||||
final UpdateInfo info = findUpdate(hasFlag(flags, UpdateServiceHelper.FLAG_REINSTALL));
|
||||
boolean haveUpdate = (info != null);
|
||||
|
||||
if (!haveUpdate) {
|
||||
Log.i(LOGTAG, "no update available");
|
||||
sendCheckUpdateResult(CheckUpdateResult.NOT_AVAILABLE);
|
||||
return;
|
||||
}
|
||||
|
||||
Log.i(LOGTAG, "update available, buildID = " + info.buildID);
|
||||
|
||||
Permissions.from(this)
|
||||
.withPermissions(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
.doNotPrompt()
|
||||
.andFallback(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
showPermissionNotification();
|
||||
sendCheckUpdateResult(CheckUpdateResult.NOT_AVAILABLE);
|
||||
}})
|
||||
.run(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
startDownload(info, flags);
|
||||
}});
|
||||
}
|
||||
|
||||
private void startDownload(UpdateInfo info, int flags) {
|
||||
AutoDownloadPolicy policy = getAutoDownloadPolicy();
|
||||
|
||||
// We only start a download automatically if one of following criteria are met:
|
||||
//
|
||||
// - We have a FORCE_DOWNLOAD flag passed in
|
||||
// - The preference is set to 'always'
|
||||
// - The preference is set to 'wifi' and we are using a non-metered network (i.e. the user
|
||||
// is OK with large data transfers occurring)
|
||||
//
|
||||
boolean shouldStartDownload = hasFlag(flags, UpdateServiceHelper.FLAG_FORCE_DOWNLOAD) ||
|
||||
policy == AutoDownloadPolicy.ENABLED ||
|
||||
(policy == AutoDownloadPolicy.WIFI && !ConnectivityManagerCompat.isActiveNetworkMetered(mConnectivityManager));
|
||||
|
||||
if (!shouldStartDownload) {
|
||||
Log.i(LOGTAG, "not initiating automatic update download due to policy " + policy.toString());
|
||||
sendCheckUpdateResult(CheckUpdateResult.AVAILABLE);
|
||||
|
||||
// We aren't autodownloading here, so prompt to start the update
|
||||
Intent notificationIntent = new Intent(UpdateServiceHelper.ACTION_DOWNLOAD_UPDATE);
|
||||
notificationIntent.setClass(this, UpdateService.class);
|
||||
PendingIntent contentIntent = PendingIntent.getService(this, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
|
||||
builder.setSmallIcon(R.drawable.ic_status_logo);
|
||||
builder.setWhen(System.currentTimeMillis());
|
||||
builder.setAutoCancel(true);
|
||||
builder.setContentTitle(getString(R.string.updater_start_title));
|
||||
builder.setContentText(getString(R.string.updater_start_select));
|
||||
builder.setContentIntent(contentIntent);
|
||||
|
||||
mNotificationManager.notify(NOTIFICATION_ID, builder.build());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
File pkg = downloadUpdatePackage(info, hasFlag(flags, UpdateServiceHelper.FLAG_OVERWRITE_EXISTING));
|
||||
if (pkg == null) {
|
||||
sendCheckUpdateResult(CheckUpdateResult.NOT_AVAILABLE);
|
||||
return;
|
||||
}
|
||||
|
||||
Log.i(LOGTAG, "have update package at " + pkg);
|
||||
|
||||
saveUpdateInfo(info, pkg);
|
||||
sendCheckUpdateResult(CheckUpdateResult.DOWNLOADED);
|
||||
|
||||
if (mApplyImmediately) {
|
||||
applyUpdate(pkg);
|
||||
} else {
|
||||
// Prompt to apply the update
|
||||
|
||||
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);
|
||||
|
||||
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
|
||||
builder.setSmallIcon(R.drawable.ic_status_logo);
|
||||
builder.setWhen(System.currentTimeMillis());
|
||||
builder.setAutoCancel(true);
|
||||
builder.setContentTitle(getString(R.string.updater_apply_title));
|
||||
builder.setContentText(getString(R.string.updater_apply_select));
|
||||
builder.setContentIntent(contentIntent);
|
||||
|
||||
mNotificationManager.notify(NOTIFICATION_ID, builder.build());
|
||||
}
|
||||
}
|
||||
|
||||
private UpdateInfo findUpdate(boolean force) {
|
||||
try {
|
||||
URI uri = getUpdateURI(force);
|
||||
|
||||
if (uri == null) {
|
||||
Log.e(LOGTAG, "failed to get update URI");
|
||||
return null;
|
||||
}
|
||||
|
||||
DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
|
||||
Document dom = builder.parse(ProxySelector.openConnectionWithProxy(uri).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.uri = new URI(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".equalsIgnoreCase(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) {
|
||||
|
||||
Intent notificationIntent = new Intent(UpdateServiceHelper.ACTION_APPLY_UPDATE);
|
||||
notificationIntent.setClass(this, UpdateService.class);
|
||||
|
||||
Intent cancelIntent = new Intent(UpdateServiceHelper.ACTION_CANCEL_DOWNLOAD);
|
||||
cancelIntent.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);
|
||||
PendingIntent deleteIntent = PendingIntent.getService(this, 0, cancelIntent, PendingIntent.FLAG_CANCEL_CURRENT);
|
||||
|
||||
mBuilder = new NotificationCompat.Builder(this);
|
||||
mBuilder.setContentTitle(getResources().getString(R.string.updater_downloading_title))
|
||||
.setContentText(mApplyImmediately ? "" : getResources().getString(R.string.updater_downloading_select))
|
||||
.setSmallIcon(android.R.drawable.stat_sys_download)
|
||||
.setContentIntent(contentIntent)
|
||||
.setDeleteIntent(deleteIntent);
|
||||
|
||||
mBuilder.setProgress(100, 0, true);
|
||||
mNotificationManager.notify(NOTIFICATION_ID, mBuilder.build());
|
||||
}
|
||||
|
||||
private void showDownloadFailure() {
|
||||
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);
|
||||
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
|
||||
builder.setSmallIcon(R.drawable.ic_status_logo);
|
||||
builder.setWhen(System.currentTimeMillis());
|
||||
builder.setContentTitle(getString(R.string.updater_downloading_title_failed));
|
||||
builder.setContentText(getString(R.string.updater_downloading_retry));
|
||||
builder.setContentIntent(contentIntent);
|
||||
|
||||
mNotificationManager.notify(NOTIFICATION_ID, builder.build());
|
||||
}
|
||||
|
||||
private boolean deleteUpdatePackage(String path) {
|
||||
if (path == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
File pkg = new File(path);
|
||||
if (!pkg.exists()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
pkg.delete();
|
||||
Log.i(LOGTAG, "deleted update package: " + path);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private File downloadUpdatePackage(UpdateInfo info, boolean overwriteExisting) {
|
||||
URL url = null;
|
||||
try {
|
||||
url = info.uri.toURL();
|
||||
} catch (java.net.MalformedURLException e) {
|
||||
Log.e(LOGTAG, "failed to read URL: ", e);
|
||||
return null;
|
||||
}
|
||||
|
||||
File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
|
||||
path.mkdirs();
|
||||
String fileName = new File(url.getFile()).getName();
|
||||
File downloadFile = new File(path, fileName);
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
if (!info.buildID.equals(getLastBuildID())) {
|
||||
// Delete the previous package when a new version becomes available.
|
||||
deleteUpdatePackage(getLastFileName());
|
||||
}
|
||||
|
||||
Log.i(LOGTAG, "downloading update package");
|
||||
sendCheckUpdateResult(CheckUpdateResult.DOWNLOADING);
|
||||
|
||||
OutputStream output = null;
|
||||
InputStream input = null;
|
||||
|
||||
mDownloading = true;
|
||||
mCancelDownload = false;
|
||||
showDownloadNotification(downloadFile);
|
||||
|
||||
try {
|
||||
NetworkInfo netInfo = mConnectivityManager.getActiveNetworkInfo();
|
||||
if (netInfo != null && netInfo.isConnected() &&
|
||||
netInfo.getType() == ConnectivityManager.TYPE_WIFI) {
|
||||
mWifiLock.acquire();
|
||||
}
|
||||
|
||||
URLConnection conn = ProxySelector.openConnectionWithProxy(info.uri);
|
||||
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;
|
||||
int lastNotify = 0;
|
||||
|
||||
while ((len = input.read(buf, 0, BUFSIZE)) > 0 && !mCancelDownload) {
|
||||
output.write(buf, 0, len);
|
||||
bytesRead += len;
|
||||
// Updating the notification takes time so only do it every 1MB
|
||||
if (bytesRead - lastNotify > 1048576) {
|
||||
mBuilder.setProgress(length, bytesRead, false);
|
||||
mNotificationManager.notify(NOTIFICATION_ID, mBuilder.build());
|
||||
lastNotify = bytesRead;
|
||||
}
|
||||
}
|
||||
|
||||
mNotificationManager.cancel(NOTIFICATION_ID);
|
||||
|
||||
// if the download was canceled by the user
|
||||
// delete the update package
|
||||
if (mCancelDownload) {
|
||||
Log.i(LOGTAG, "download canceled by user!");
|
||||
downloadFile.delete();
|
||||
|
||||
return null;
|
||||
} else {
|
||||
Log.i(LOGTAG, "completed update download!");
|
||||
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;
|
||||
|
||||
if (mWifiLock.isHeld()) {
|
||||
mWifiLock.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
if (updatePath == null) {
|
||||
updatePath = getLastFileName();
|
||||
}
|
||||
|
||||
if (updatePath != null) {
|
||||
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;
|
||||
}
|
||||
|
||||
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 void showPermissionNotification() {
|
||||
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
|
||||
Uri.fromParts("package", getPackageName(), null));
|
||||
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);
|
||||
|
||||
NotificationCompat.BigTextStyle bigTextStyle = new NotificationCompat.BigTextStyle()
|
||||
.bigText(getString(R.string.updater_permission_text));
|
||||
|
||||
Notification notification = new NotificationCompat.Builder(this)
|
||||
.setContentTitle(getString(R.string.updater_permission_title))
|
||||
.setContentText(getString(R.string.updater_permission_text))
|
||||
.setStyle(bigTextStyle)
|
||||
.setAutoCancel(true)
|
||||
.setSmallIcon(R.drawable.ic_status_logo)
|
||||
.setColor(ContextCompat.getColor(this, R.color.rejection_red))
|
||||
.setContentIntent(pendingIntent)
|
||||
.build();
|
||||
|
||||
NotificationManagerCompat.from(this)
|
||||
.notify(R.id.updateServicePermissionNotification, notification);
|
||||
}
|
||||
|
||||
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 String getLastFileName() {
|
||||
return mPrefs.getString(KEY_LAST_FILE_NAME, 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 AutoDownloadPolicy getAutoDownloadPolicy() {
|
||||
return AutoDownloadPolicy.get(mPrefs.getInt(KEY_AUTODOWNLOAD_POLICY, AutoDownloadPolicy.WIFI.value));
|
||||
}
|
||||
|
||||
private void setAutoDownloadPolicy(AutoDownloadPolicy policy) {
|
||||
SharedPreferences.Editor editor = mPrefs.edit();
|
||||
editor.putInt(KEY_AUTODOWNLOAD_POLICY, policy.value);
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
private URI getUpdateURI(boolean force) {
|
||||
return UpdateServiceHelper.expandUpdateURI(this, mPrefs.getString(KEY_UPDATE_URL, null), force);
|
||||
}
|
||||
|
||||
private void setUpdateUrl(String url) {
|
||||
SharedPreferences.Editor editor = mPrefs.edit();
|
||||
editor.putString(KEY_UPDATE_URL, url);
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
private void saveUpdateInfo(UpdateInfo info, File downloaded) {
|
||||
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.putString(KEY_LAST_FILE_NAME, downloaded.toString());
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
private class UpdateInfo {
|
||||
public URI uri;
|
||||
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 uri != null && isNonEmpty(buildID) &&
|
||||
isNonEmpty(hashFunction) && isNonEmpty(hashValue) && size > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "uri = " + uri + ", buildID = " + buildID + ", hashFunction = " + hashFunction + ", hashValue = " + hashValue + ", size = " + size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,213 @@
|
|||
/* -*- 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.annotation.RobocopTarget;
|
||||
import org.mozilla.gecko.AppConstants;
|
||||
import org.mozilla.gecko.PrefsHelper;
|
||||
import org.mozilla.gecko.util.ContextUtils;
|
||||
import org.mozilla.gecko.util.GeckoJarReader;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
public class UpdateServiceHelper {
|
||||
public static final String ACTION_REGISTER_FOR_UPDATES = AppConstants.ANDROID_PACKAGE_NAME + ".REGISTER_FOR_UPDATES";
|
||||
public static final String ACTION_UNREGISTER_FOR_UPDATES = AppConstants.ANDROID_PACKAGE_NAME + ".UNREGISTER_FOR_UPDATES";
|
||||
public static final String ACTION_CHECK_FOR_UPDATE = AppConstants.ANDROID_PACKAGE_NAME + ".CHECK_FOR_UPDATE";
|
||||
public static final String ACTION_CHECK_UPDATE_RESULT = AppConstants.ANDROID_PACKAGE_NAME + ".CHECK_UPDATE_RESULT";
|
||||
public static final String ACTION_DOWNLOAD_UPDATE = AppConstants.ANDROID_PACKAGE_NAME + ".DOWNLOAD_UPDATE";
|
||||
public static final String ACTION_APPLY_UPDATE = AppConstants.ANDROID_PACKAGE_NAME + ".APPLY_UPDATE";
|
||||
public static final String ACTION_CANCEL_DOWNLOAD = AppConstants.ANDROID_PACKAGE_NAME + ".CANCEL_DOWNLOAD";
|
||||
|
||||
// Flags for ACTION_CHECK_FOR_UPDATE
|
||||
protected static final int FLAG_FORCE_DOWNLOAD = 1;
|
||||
protected static final int FLAG_OVERWRITE_EXISTING = 1 << 1;
|
||||
protected static final int FLAG_REINSTALL = 1 << 2;
|
||||
protected static final int FLAG_RETRY = 1 << 3;
|
||||
|
||||
// Name of the Intent extra for the autodownload policy, used with ACTION_REGISTER_FOR_UPDATES
|
||||
protected static final String EXTRA_AUTODOWNLOAD_NAME = "autodownload";
|
||||
|
||||
// Name of the Intent extra that holds the flags for ACTION_CHECK_FOR_UPDATE
|
||||
protected static final String EXTRA_UPDATE_FLAGS_NAME = "updateFlags";
|
||||
|
||||
// Name of the Intent extra that holds the APK path, used with ACTION_APPLY_UPDATE
|
||||
protected static final String EXTRA_PACKAGE_PATH_NAME = "packagePath";
|
||||
|
||||
// Name of the Intent extra for the update URL, used with ACTION_REGISTER_FOR_UPDATES
|
||||
protected static final String EXTRA_UPDATE_URL_NAME = "updateUrl";
|
||||
|
||||
private static final String LOGTAG = "UpdateServiceHelper";
|
||||
private static final String DEFAULT_UPDATE_LOCALE = "en-US";
|
||||
|
||||
// So that updates can be disabled by tests.
|
||||
private static volatile boolean isEnabled = true;
|
||||
|
||||
private enum Pref {
|
||||
AUTO_DOWNLOAD_POLICY("app.update.autodownload"),
|
||||
UPDATE_URL("app.update.url.android");
|
||||
|
||||
public final String name;
|
||||
|
||||
private Pref(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public final static String[] names;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
static {
|
||||
ArrayList<String> nameList = new ArrayList<String>();
|
||||
|
||||
for (Pref id: Pref.values()) {
|
||||
nameList.add(id.toString());
|
||||
}
|
||||
|
||||
names = nameList.toArray(new String[0]);
|
||||
}
|
||||
}
|
||||
|
||||
@RobocopTarget
|
||||
public static void setEnabled(final boolean enabled) {
|
||||
isEnabled = enabled;
|
||||
}
|
||||
|
||||
public static URI expandUpdateURI(Context context, String updateUri, boolean force) {
|
||||
if (updateUri == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
PackageManager pm = context.getPackageManager();
|
||||
|
||||
String pkgSpecial = AppConstants.MOZ_PKG_SPECIAL != null ?
|
||||
"-" + AppConstants.MOZ_PKG_SPECIAL :
|
||||
"";
|
||||
String locale = DEFAULT_UPDATE_LOCALE;
|
||||
|
||||
try {
|
||||
ApplicationInfo info = pm.getApplicationInfo(AppConstants.ANDROID_PACKAGE_NAME, 0);
|
||||
String updateLocaleUrl = "jar:jar:file://" + info.sourceDir + "!/" + AppConstants.OMNIJAR_NAME + "!/update.locale";
|
||||
|
||||
final String jarLocale = GeckoJarReader.getText(context, updateLocaleUrl);
|
||||
if (jarLocale != null) {
|
||||
locale = jarLocale.trim();
|
||||
}
|
||||
} catch (android.content.pm.PackageManager.NameNotFoundException e) {
|
||||
// Shouldn't really be possible, but fallback to default locale
|
||||
Log.i(LOGTAG, "Failed to read update locale file, falling back to " + locale);
|
||||
}
|
||||
|
||||
String url = updateUri.replace("%PRODUCT%", AppConstants.MOZ_APP_BASENAME)
|
||||
.replace("%VERSION%", AppConstants.MOZ_APP_VERSION)
|
||||
.replace("%BUILD_ID%", force ? "0" : AppConstants.MOZ_APP_BUILDID)
|
||||
.replace("%BUILD_TARGET%", "Android_" + AppConstants.MOZ_APP_ABI + pkgSpecial)
|
||||
.replace("%LOCALE%", locale)
|
||||
.replace("%CHANNEL%", AppConstants.MOZ_UPDATE_CHANNEL)
|
||||
.replace("%OS_VERSION%", Build.VERSION.RELEASE)
|
||||
.replace("%DISTRIBUTION%", "default")
|
||||
.replace("%DISTRIBUTION_VERSION%", "default")
|
||||
.replace("%MOZ_VERSION%", AppConstants.MOZILLA_VERSION);
|
||||
|
||||
try {
|
||||
return new URI(url);
|
||||
} catch (java.net.URISyntaxException e) {
|
||||
Log.e(LOGTAG, "Failed to create update url: ", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isUpdaterEnabled(final Context context) {
|
||||
return AppConstants.MOZ_UPDATER && isEnabled && !ContextUtils.isInstalledFromGooglePlay(context);
|
||||
}
|
||||
|
||||
public static void setUpdateUrl(Context context, String url) {
|
||||
registerForUpdates(context, null, url);
|
||||
}
|
||||
|
||||
public static void setAutoDownloadPolicy(Context context, UpdateService.AutoDownloadPolicy policy) {
|
||||
registerForUpdates(context, policy, null);
|
||||
}
|
||||
|
||||
public static void checkForUpdate(Context context) {
|
||||
if (context == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
context.startService(createIntent(context, ACTION_CHECK_FOR_UPDATE));
|
||||
}
|
||||
|
||||
public static void downloadUpdate(Context context) {
|
||||
if (context == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
context.startService(createIntent(context, ACTION_DOWNLOAD_UPDATE));
|
||||
}
|
||||
|
||||
public static void applyUpdate(Context context) {
|
||||
if (context == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
context.startService(createIntent(context, ACTION_APPLY_UPDATE));
|
||||
}
|
||||
|
||||
public static void registerForUpdates(final Context context) {
|
||||
if (!isUpdaterEnabled(context)) {
|
||||
return;
|
||||
}
|
||||
|
||||
final HashMap<String, Object> prefs = new HashMap<String, Object>();
|
||||
|
||||
PrefsHelper.getPrefs(Pref.names, new PrefsHelper.PrefHandlerBase() {
|
||||
@Override public void prefValue(String pref, String value) {
|
||||
prefs.put(pref, value);
|
||||
}
|
||||
|
||||
@Override public void finish() {
|
||||
UpdateServiceHelper.registerForUpdates(context,
|
||||
UpdateService.AutoDownloadPolicy.get(
|
||||
(String) prefs.get(Pref.AUTO_DOWNLOAD_POLICY.toString())),
|
||||
(String) prefs.get(Pref.UPDATE_URL.toString()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void registerForUpdates(Context context, UpdateService.AutoDownloadPolicy policy, String url) {
|
||||
if (!isUpdaterEnabled(context)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Intent intent = createIntent(context, ACTION_REGISTER_FOR_UPDATES);
|
||||
|
||||
if (policy != null) {
|
||||
intent.putExtra(EXTRA_AUTODOWNLOAD_NAME, policy.value);
|
||||
}
|
||||
|
||||
if (url != null) {
|
||||
intent.putExtra(EXTRA_UPDATE_URL_NAME, url);
|
||||
}
|
||||
|
||||
context.startService(intent);
|
||||
}
|
||||
|
||||
private static Intent createIntent(Context context, String action) {
|
||||
return new Intent(action, null, context, UpdateService.class);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue