summaryrefslogtreecommitdiffstats
path: root/core/java/android/app/Service.java
diff options
context:
space:
mode:
Diffstat (limited to 'core/java/android/app/Service.java')
-rw-r--r--core/java/android/app/Service.java216
1 files changed, 194 insertions, 22 deletions
diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java
index d2fb605..30e1712 100644
--- a/core/java/android/app/Service.java
+++ b/core/java/android/app/Service.java
@@ -22,8 +22,10 @@ import android.content.Intent;
import android.content.ContextWrapper;
import android.content.Context;
import android.content.res.Configuration;
+import android.os.Build;
import android.os.RemoteException;
import android.os.IBinder;
+import android.util.Log;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -62,18 +64,28 @@ import java.io.PrintWriter;
* <p>There are two reasons that a service can be run by the system. If someone
* calls {@link android.content.Context#startService Context.startService()} then the system will
* retrieve the service (creating it and calling its {@link #onCreate} method
- * if needed) and then call its {@link #onStart} method with the
+ * if needed) and then call its {@link #onStartCommand} method with the
* arguments supplied by the client. The service will at this point continue
* running until {@link android.content.Context#stopService Context.stopService()} or
* {@link #stopSelf()} is called. Note that multiple calls to
* Context.startService() do not nest (though they do result in multiple corresponding
- * calls to onStart()), so no matter how many times it is started a service
- * will be stopped once Context.stopService() or stopSelf() is called.
+ * calls to onStartCommand()), so no matter how many times it is started a service
+ * will be stopped once Context.stopService() or stopSelf() is called; however,
+ * services can use their {@link #stopSelf(int)} method to ensure the service is
+ * not stopped until started intents have been processed.
+ *
+ * <p>For started services, there are two additional major modes of operation
+ * they can decide to run in, depending on the value they return from
+ * onStartCommand(): {@link #START_STICKY} is used for services that are
+ * explicitly started and stopped as needed, while {@link #START_NOT_STICKY}
+ * or {@link #START_REDELIVER_INTENT} are used for services that should only
+ * remain running while processing any commands sent to them. See the linked
+ * documentation for more detail on the semantics.
*
* <p>Clients can also use {@link android.content.Context#bindService Context.bindService()} to
* obtain a persistent connection to a service. This likewise creates the
* service if it is not already running (calling {@link #onCreate} while
- * doing so), but does not call onStart(). The client will receive the
+ * doing so), but does not call onStartCommand(). The client will receive the
* {@link android.os.IBinder} object that the service returns from its
* {@link #onBind} method, allowing the client to then make calls back
* to the service. The service will remain running as long as the connection
@@ -120,7 +132,7 @@ import java.io.PrintWriter;
*
* <ul>
* <li><p>If the service is currently executing code in its
- * {@link #onCreate onCreate()}, {@link #onStart onStart()},
+ * {@link #onCreate onCreate()}, {@link #onStartCommand onStartCommand()},
* or {@link #onDestroy onDestroy()} methods, then the hosting process will
* be a foreground process to ensure this code can execute without
* being killed.
@@ -133,16 +145,22 @@ import java.io.PrintWriter;
* process is never less important than the most important client. That is,
* if one of its clients is visible to the user, then the service itself is
* considered to be visible.
+ * <li><p>A started service can use the {@link #startForeground(int, Notification)}
+ * API to put the service in a foreground state, where the system considers
+ * it to be something the user is actively aware of and thus not a candidate
+ * for killing when low on memory. (It is still theoretically possible for
+ * the service to be killed under extreme memory pressure from the current
+ * foreground application, but in practice this should not be a concern.)
* </ul>
*
* <p>Note this means that most of the time your service is running, it may
* be killed by the system if it is under heavy memory pressure. If this
* happens, the system will later try to restart the service. An important
- * consequence of this is that if you implement {@link #onStart onStart()}
+ * consequence of this is that if you implement {@link #onStartCommand onStartCommand()}
* to schedule work to be done asynchronously or in another thread, then you
- * may want to write information about that work into persistent storage
- * during the onStart() call so that it does not get lost if the service later
- * gets killed.
+ * may want to use {@link #START_FLAG_REDELIVERY} to have the system
+ * re-deliver an Intent for you so that it does not get lost if your service
+ * is killed while processing it.
*
* <p>Other application components running in the same process as the service
* (such as an {@link android.app.Activity}) can, of course, increase the
@@ -168,20 +186,127 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac
}
/**
+ * @deprecated Implement {@link #onStartCommand(Intent, int, int)} instead.
+ */
+ @Deprecated
+ public void onStart(Intent intent, int startId) {
+ }
+
+ /**
+ * Bits returned by {@link #onStartCommand} describing how to continue
+ * the service if it is killed. May be {@link #START_STICKY},
+ * {@link #START_NOT_STICKY}, {@link #START_REDELIVER_INTENT},
+ * or {@link #START_STICKY_COMPATIBILITY}.
+ */
+ public static final int START_CONTINUATION_MASK = 0xf;
+
+ /**
+ * Constant to return from {@link #onStartCommand}: compatibility
+ * version of {@link #START_STICKY} that does not guarantee that
+ * {@link #onStartCommand} will be called again after being killed.
+ */
+ public static final int START_STICKY_COMPATIBILITY = 0;
+
+ /**
+ * Constant to return from {@link #onStartCommand}: if this service's
+ * process is killed while it is started (after returning from
+ * {@link #onStartCommand}), then leave it in the started state but
+ * don't retain this delivered intent. Later the system will try to
+ * re-create the service. Because it is in the started state, it will
+ * guarantee to call {@link #onStartCommand} after creating the new
+ * service instance; if there are not any pending start commands to be
+ * delivered to the service, it will be called with a null intent
+ * object, so you must take care to check for this.
+ *
+ * <p>This mode makes sense for things that will be explicitly started
+ * and stopped to run for arbitrary periods of time, such as a service
+ * performing background music playback.
+ */
+ public static final int START_STICKY = 1;
+
+ /**
+ * Constant to return from {@link #onStartCommand}: if this service's
+ * process is killed while it is started (after returning from
+ * {@link #onStartCommand}), and there are no new start intents to
+ * deliver to it, then take the service out of the started state and
+ * don't recreate until a future explicit call to
+ * {@link Context#startService Context.startService(Intent)}. The
+ * service will not receive a {@link #onStartCommand(Intent, int, int)}
+ * call with a null Intent because it will not be re-started if there
+ * are no pending Intents to deliver.
+ *
+ * <p>This mode makes sense for things that want to do some work as a
+ * result of being started, but can be stopped when under memory pressure
+ * and will explicit start themselves again later to do more work. An
+ * example of such a service would be one that polls for data from
+ * a server: it could schedule an alarm to poll every N minutes by having
+ * the alarm start its service. When its {@link #onStartCommand} is
+ * called from the alarm, it schedules a new alarm for N minutes later,
+ * and spawns a thread to do its networking. If its process is killed
+ * while doing that check, the service will not be restarted until the
+ * alarm goes off.
+ */
+ public static final int START_NOT_STICKY = 2;
+
+ /**
+ * Constant to return from {@link #onStartCommand}: if this service's
+ * process is killed while it is started (after returning from
+ * {@link #onStartCommand}), then it will be scheduled for a restart
+ * and the last delivered Intent re-delivered to it again via
+ * {@link #onStartCommand}. This Intent will remain scheduled for
+ * redelivery until the service calls {@link #stopSelf(int)} with the
+ * start ID provided to {@link #onStartCommand}. The
+ * service will not receive a {@link #onStartCommand(Intent, int, int)}
+ * call with a null Intent because it will will only be re-started if
+ * it is not finished processing all Intents sent to it (and any such
+ * pending events will be delivered at the point of restart).
+ */
+ public static final int START_REDELIVER_INTENT = 3;
+
+ /**
+ * This flag is set in {@link #onStartCommand} if the Intent is a
+ * re-delivery of a previously delivered intent, because the service
+ * had previously returned {@link #START_REDELIVER_INTENT} but had been
+ * killed before calling {@link #stopSelf(int)} for that Intent.
+ */
+ public static final int START_FLAG_REDELIVERY = 0x0001;
+
+ /**
+ * This flag is set in {@link #onStartCommand} if the Intent is a
+ * a retry because the original attempt never got to or returned from
+ * {@link #onStartCommand(Intent, int, int)}.
+ */
+ public static final int START_FLAG_RETRY = 0x0002;
+
+ /**
* Called by the system every time a client explicitly starts the service by calling
* {@link android.content.Context#startService}, providing the arguments it supplied and a
* unique integer token representing the start request. Do not call this method directly.
- *
+ *
+ * <p>For backwards compatibility, the default implementation calls
+ * {@link #onStart} and returns either {@link #START_STICKY}
+ * or {@link #START_STICKY_COMPATIBILITY}.
+ *
* @param intent The Intent supplied to {@link android.content.Context#startService},
- * as given.
+ * as given. This may be null if the service is being restarted after
+ * its process has gone away, and it had previously returned anything
+ * except {@link #START_STICKY_COMPATIBILITY}.
+ * @param flags Additional data about this start request. Currently either
+ * 0, {@link #START_FLAG_REDELIVERY}, or {@link #START_FLAG_RETRY}.
* @param startId A unique integer representing this specific request to
- * start. Use with {@link #stopSelfResult(int)}.
+ * start. Use with {@link #stopSelfResult(int)}.
+ *
+ * @return The return value indicates what semantics the system should
+ * use for the service's current started state. It may be one of the
+ * constants associated with the {@link #START_CONTINUATION_MASK} bits.
*
* @see #stopSelfResult(int)
*/
- public void onStart(Intent intent, int startId) {
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ onStart(intent, startId);
+ return mStartCompatibility ? START_STICKY_COMPATIBILITY : START_STICKY;
}
-
+
/**
* Called by the system to notify a Service that it is no longer used and is being removed. The
* service should clean up an resources it holds (threads, registered
@@ -284,6 +409,13 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac
* safely avoid stopping if there is a start request from a client that you
* haven't yet seen in {@link #onStart}.
*
+ * <p><em>Be careful about ordering of your calls to this function.</em>.
+ * If you call this function with the most-recently received ID before
+ * you have called it for previously received IDs, the service will be
+ * immediately stopped anyway. If you may end up processing IDs out
+ * of order (such as by dispatching them on separate threads), then you
+ * are responsible for stopping them in the same order you received them.</p>
+ *
* @param startId The most recent start identifier received in {@link
* #onStart}.
* @return Returns true if the startId matches the last start request
@@ -304,24 +436,61 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac
}
/**
- * Control whether this service is considered to be a foreground service.
+ * @deprecated This is a now a no-op, use
+ * {@link #startForeground(int, Notification)} instead. This method
+ * has been turned into a no-op rather than simply being deprecated
+ * because analysis of numerous poorly behaving devices has shown that
+ * increasingly often the trouble is being caused in part by applications
+ * that are abusing it. Thus, given a choice between introducing
+ * problems in existing applications using this API (by allowing them to
+ * be killed when they would like to avoid it), vs allowing the performance
+ * of the entire system to be decreased, this method was deemed less
+ * important.
+ */
+ @Deprecated
+ public final void setForeground(boolean isForeground) {
+ Log.w(TAG, "setForeground: ignoring old API call on " + getClass().getName());
+ }
+
+ /**
+ * Make this service run in the foreground, supplying the ongoing
+ * notification to be shown to the user while in this state.
* By default services are background, meaning that if the system needs to
* kill them to reclaim more memory (such as to display a large page in a
* web browser), they can be killed without too much harm. You can set this
- * flag if killing your service would be disruptive to the user: such as
+ * flag if killing your service would be disruptive to the user, such as
* if your service is performing background music playback, so the user
* would notice if their music stopped playing.
*
- * @param isForeground Determines whether this service is considered to
- * be foreground (true) or background (false).
+ * @param id The identifier for this notification as per
+ * {@link NotificationManager#notify(int, Notification)
+ * NotificationManager.notify(int, Notification)}.
+ * @param notification The Notification to be displayed.
+ *
+ * @see #stopForeground(boolean)
*/
- public final void setForeground(boolean isForeground) {
- if (mActivityManager == null) {
- return;
+ public final void startForeground(int id, Notification notification) {
+ try {
+ mActivityManager.setServiceForeground(
+ new ComponentName(this, mClassName), mToken, id,
+ notification, true);
+ } catch (RemoteException ex) {
}
+ }
+
+ /**
+ * Remove this service from foreground state, allowing it to be killed if
+ * more memory is needed.
+ * @param removeNotification If true, the notification previously provided
+ * to {@link #startForeground} will be removed. Otherwise it will remain
+ * until a later call removes it (or the service is destroyed).
+ * @see #startForeground(int, Notification)
+ */
+ public final void stopForeground(boolean removeNotification) {
try {
mActivityManager.setServiceForeground(
- new ComponentName(this, mClassName), mToken, isForeground);
+ new ComponentName(this, mClassName), mToken, 0, null,
+ removeNotification);
} catch (RemoteException ex) {
}
}
@@ -363,6 +532,8 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac
mToken = token;
mApplication = application;
mActivityManager = (IActivityManager)activityManager;
+ mStartCompatibility = getApplicationInfo().targetSdkVersion
+ < Build.VERSION_CODES.ECLAIR;
}
final String getClassName() {
@@ -375,4 +546,5 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac
private IBinder mToken = null;
private Application mApplication = null;
private IActivityManager mActivityManager = null;
+ private boolean mStartCompatibility = false;
}