fix crash when UpdateService receives null Intent

IntentServices can get a null Intent if they are restarted after being
killed.  So this should be properly handled.

"[The intent] may be null if the service is being restarted after its
process has gone away, and it had previously returned anything except
START_STICKY_COMPATIBILITY."
https://developer.android.com/reference/android/app/IntentService.html#onStartCommand(android.content.Intent,%20int,%20int)

ANDROID_VERSION=5.1.1
APP_VERSION_NAME=0.99.2
BRAND=samsung
PHONE_MODEL=SM-G901F
java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.content.Intent.getStringExtra(java.lang.String)' on a null object reference
	at org.fdroid.fdroid.UpdateService.onHandleIntent(UpdateService.java:342)
	at android.app.IntentService$ServiceHandler.handleMessage(IntentService.java:65)
	at android.os.Handler.dispatchMessage(Handler.java:102)
	at android.os.Looper.loop(Looper.java:135)
	at android.os.HandlerThread.run(HandlerThread.java:61)
This commit is contained in:
Hans-Christoph Steiner 2016-05-30 12:05:19 +02:00
parent 6166d9afc9
commit 7385d320b4
3 changed files with 26 additions and 10 deletions

View File

@ -47,6 +47,9 @@ public class CleanCacheService extends IntentService {
@Override @Override
protected void onHandleIntent(Intent intent) { protected void onHandleIntent(Intent intent) {
if (intent == null) {
return;
}
Process.setThreadPriority(Process.THREAD_PRIORITY_LOWEST); Process.setThreadPriority(Process.THREAD_PRIORITY_LOWEST);
Utils.clearOldFiles(Utils.getApkCacheDir(this), Preferences.get().getKeepCacheTime()); Utils.clearOldFiles(Utils.getApkCacheDir(this), Preferences.get().getKeepCacheTime());
deleteStrayIndexFiles(); deleteStrayIndexFiles();

View File

@ -313,8 +313,12 @@ public class UpdateService extends IntentService {
Process.setThreadPriority(Process.THREAD_PRIORITY_LOWEST); Process.setThreadPriority(Process.THREAD_PRIORITY_LOWEST);
final long startTime = System.currentTimeMillis(); final long startTime = System.currentTimeMillis();
String address = intent.getStringExtra(EXTRA_ADDRESS); boolean manualUpdate = false;
boolean manualUpdate = intent.getBooleanExtra(EXTRA_MANUAL_UPDATE, false); String address = null;
if (intent != null) {
address = intent.getStringExtra(EXTRA_ADDRESS);
manualUpdate = intent.getBooleanExtra(EXTRA_MANUAL_UPDATE, false);
}
try { try {
// See if it's time to actually do anything yet... // See if it's time to actually do anything yet...

View File

@ -33,7 +33,6 @@ import android.os.Process;
import android.support.v4.content.IntentCompat; import android.support.v4.content.IntentCompat;
import android.support.v4.content.LocalBroadcastManager; import android.support.v4.content.LocalBroadcastManager;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log;
import org.fdroid.fdroid.ProgressListener; import org.fdroid.fdroid.ProgressListener;
import org.fdroid.fdroid.Utils; import org.fdroid.fdroid.Utils;
@ -46,9 +45,14 @@ import java.net.URL;
/** /**
* DownloaderService is a service that handles asynchronous download requests * DownloaderService is a service that handles asynchronous download requests
* (expressed as {@link Intent}s) on demand. Clients send download requests * (expressed as {@link Intent}s) on demand. Clients send download requests
* through {@link android.content.Context#startService(Intent)} calls; the * through {@link #queue(Context, String)} calls. The
* service is started as needed, handles each Intent in turn using a worker * service is started as needed, it handles each {@code Intent} using a worker
* thread, and stops itself when it runs out of work. * thread, and stops itself when it runs out of work. Requests can be canceled
* using {@link #cancel(Context, String)}. If this service is killed during
* operation, it will receive the queued {@link #queue(Context, String)} and
* {@link #cancel(Context, String)} requests again due to
* {@link Service#START_REDELIVER_INTENT}. Bad requests will be ignored,
* including on restart after killing via {@link Service#START_NOT_STICKY}.
* <p> * <p>
* This "work queue processor" pattern is commonly used to offload tasks * This "work queue processor" pattern is commonly used to offload tasks
* from an application's main thread. The DownloaderService class exists to * from an application's main thread. The DownloaderService class exists to
@ -112,10 +116,15 @@ public class DownloaderService extends Service {
@Override @Override
public int onStartCommand(Intent intent, int flags, int startId) { public int onStartCommand(Intent intent, int flags, int startId) {
Utils.debugLog(TAG, "Received Intent for downloading: " + intent + " (with a startId of " + startId + ")"); Utils.debugLog(TAG, "Received Intent for downloading: " + intent + " (with a startId of " + startId + ")");
if (intent == null) {
return START_NOT_STICKY;
}
String uriString = intent.getDataString(); String uriString = intent.getDataString();
if (uriString == null) { if (uriString == null) {
Log.e(TAG, "Received Intent with no URI: " + intent); Utils.debugLog(TAG, "Received Intent with no URI: " + intent);
return START_STICKY; return START_NOT_STICKY;
} }
if (ACTION_CANCEL.equals(intent.getAction())) { if (ACTION_CANCEL.equals(intent.getAction())) {
@ -128,7 +137,7 @@ public class DownloaderService extends Service {
} else if (isActive(uriString)) { } else if (isActive(uriString)) {
downloader.cancelDownload(); downloader.cancelDownload();
} else { } else {
Log.e(TAG, "ACTION_CANCEL called on something not queued or running (expected to find message with ID of " + whatToRemove + " in queue)."); Utils.debugLog(TAG, "ACTION_CANCEL called on something not queued or running (expected to find message with ID of " + whatToRemove + " in queue).");
} }
} else if (ACTION_QUEUE.equals(intent.getAction())) { } else if (ACTION_QUEUE.equals(intent.getAction())) {
Message msg = serviceHandler.obtainMessage(); Message msg = serviceHandler.obtainMessage();
@ -138,7 +147,7 @@ public class DownloaderService extends Service {
serviceHandler.sendMessage(msg); serviceHandler.sendMessage(msg);
Utils.debugLog(TAG, "Queued download of " + uriString); Utils.debugLog(TAG, "Queued download of " + uriString);
} else { } else {
Log.e(TAG, "Received Intent with unknown action: " + intent); Utils.debugLog(TAG, "Received Intent with unknown action: " + intent);
} }
return START_REDELIVER_INTENT; // if killed before completion, retry Intent return START_REDELIVER_INTENT; // if killed before completion, retry Intent