Merge change 22400 into eclair

* changes:
  Add more control over a service's start state.
This commit is contained in:
Android (Google) Code Review
2009-08-24 17:01:50 -07:00
11 changed files with 550 additions and 45 deletions

View File

@@ -608,7 +608,10 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
case SERVICE_DONE_EXECUTING_TRANSACTION: {
data.enforceInterface(IActivityManager.descriptor);
IBinder token = data.readStrongBinder();
serviceDoneExecuting(token);
int type = data.readInt();
int startId = data.readInt();
int res = data.readInt();
serviceDoneExecuting(token, type, startId, res);
reply.writeNoException();
return true;
}
@@ -1746,11 +1749,15 @@ class ActivityManagerProxy implements IActivityManager
reply.recycle();
}
public void serviceDoneExecuting(IBinder token) throws RemoteException {
public void serviceDoneExecuting(IBinder token, int type, int startId,
int res) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(token);
data.writeInt(type);
data.writeInt(startId);
data.writeInt(res);
mRemote.transact(SERVICE_DONE_EXECUTING_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY);
reply.readException();
data.recycle();

View File

@@ -1215,6 +1215,7 @@ public final class ActivityThread {
private static final class ServiceArgsData {
IBinder token;
int startId;
int flags;
Intent args;
public String toString() {
return "ServiceArgsData{token=" + token + " startId=" + startId
@@ -1417,10 +1418,11 @@ public final class ActivityThread {
}
public final void scheduleServiceArgs(IBinder token, int startId,
Intent args) {
int flags ,Intent args) {
ServiceArgsData s = new ServiceArgsData();
s.token = token;
s.startId = startId;
s.flags = flags;
s.args = args;
queueOrSendMessage(H.SERVICE_ARGS, s);
@@ -2684,7 +2686,8 @@ public final class ActivityThread {
service.onCreate();
mServices.put(data.token, service);
try {
ActivityManagerNative.getDefault().serviceDoneExecuting(data.token);
ActivityManagerNative.getDefault().serviceDoneExecuting(
data.token, 0, 0, 0);
} catch (RemoteException e) {
// nothing to do.
}
@@ -2710,7 +2713,7 @@ public final class ActivityThread {
} else {
s.onRebind(data.intent);
ActivityManagerNative.getDefault().serviceDoneExecuting(
data.token);
data.token, 0, 0, 0);
}
} catch (RemoteException ex) {
}
@@ -2736,7 +2739,7 @@ public final class ActivityThread {
data.token, data.intent, doRebind);
} else {
ActivityManagerNative.getDefault().serviceDoneExecuting(
data.token);
data.token, 0, 0, 0);
}
} catch (RemoteException ex) {
}
@@ -2773,9 +2776,10 @@ public final class ActivityThread {
if (data.args != null) {
data.args.setExtrasClassLoader(s.getClassLoader());
}
s.onStart(data.args, data.startId);
int res = s.onStartCommand(data.args, data.flags, data.startId);
try {
ActivityManagerNative.getDefault().serviceDoneExecuting(data.token);
ActivityManagerNative.getDefault().serviceDoneExecuting(
data.token, 1, data.startId, res);
} catch (RemoteException e) {
// nothing to do.
}
@@ -2801,7 +2805,8 @@ public final class ActivityThread {
((ApplicationContext) context).scheduleFinalCleanup(who, "Service");
}
try {
ActivityManagerNative.getDefault().serviceDoneExecuting(token);
ActivityManagerNative.getDefault().serviceDoneExecuting(
token, 0, 0, 0);
} catch (RemoteException e) {
// nothing to do.
}

View File

@@ -206,8 +206,14 @@ public abstract class ApplicationThreadNative extends Binder
data.enforceInterface(IApplicationThread.descriptor);
IBinder token = data.readStrongBinder();
int startId = data.readInt();
Intent args = Intent.CREATOR.createFromParcel(data);
scheduleServiceArgs(token, startId, args);
int fl = data.readInt();
Intent args;
if (data.readInt() != 0) {
args = Intent.CREATOR.createFromParcel(data);
} else {
args = null;
}
scheduleServiceArgs(token, startId, fl, args);
return true;
}
@@ -573,12 +579,18 @@ class ApplicationThreadProxy implements IApplicationThread {
}
public final void scheduleServiceArgs(IBinder token, int startId,
Intent args) throws RemoteException {
int flags, Intent args) throws RemoteException {
Parcel data = Parcel.obtain();
data.writeInterfaceToken(IApplicationThread.descriptor);
data.writeStrongBinder(token);
data.writeInt(startId);
args.writeToParcel(data, 0);
data.writeInt(flags);
if (args != null) {
data.writeInt(1);
args.writeToParcel(data, 0);
} else {
data.writeInt(0);
}
mRemote.transact(SCHEDULE_SERVICE_ARGS_TRANSACTION, data, null,
IBinder.FLAG_ONEWAY);
data.recycle();

View File

@@ -149,7 +149,8 @@ public interface IActivityManager extends IInterface {
public void unbindFinished(IBinder token, Intent service,
boolean doRebind) throws RemoteException;
/* oneway */
public void serviceDoneExecuting(IBinder token) throws RemoteException;
public void serviceDoneExecuting(IBinder token, int type, int startId,
int res) throws RemoteException;
public IBinder peekService(Intent service, String resolvedType) throws RemoteException;
public boolean bindBackupAgent(ApplicationInfo appInfo, int backupRestoreMode)

View File

@@ -71,7 +71,8 @@ public interface IApplicationThread extends IInterface {
Intent intent, boolean rebind) throws RemoteException;
void scheduleUnbindService(IBinder token,
Intent intent) throws RemoteException;
void scheduleServiceArgs(IBinder token, int startId, Intent args) throws RemoteException;
void scheduleServiceArgs(IBinder token, int startId, int flags, Intent args)
throws RemoteException;
void scheduleStopService(IBinder token) throws RemoteException;
static final int DEBUG_OFF = 0;
static final int DEBUG_ON = 1;

View File

@@ -18,6 +18,7 @@ public abstract class IntentService extends Service {
private volatile Looper mServiceLooper;
private volatile ServiceHandler mServiceHandler;
private String mName;
private boolean mRedelivery;
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
@@ -36,6 +37,19 @@ public abstract class IntentService extends Service {
mName = name;
}
/**
* Control redelivery of intents. If called with true,
* {@link #onStartCommand(Intent, int, int)} will return
* {@link Service#START_REDELIVER_INTENT} instead of
* {@link Service#START_NOT_STICKY}, so that if this service's process
* is called while it is executing the Intent in
* {@link #onHandleIntent(Intent)}, then when later restarted the same Intent
* will be re-delivered to it, to retry its execution.
*/
public void setIntentRedelivery(boolean enabled) {
mRedelivery = enabled;
}
@Override
public void onCreate() {
super.onCreate();
@@ -48,13 +62,18 @@ public abstract class IntentService extends Service {
@Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
@Override
public void onDestroy() {
mServiceLooper.quit();

View File

@@ -22,6 +22,7 @@ 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;
@@ -169,20 +170,119 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac
}
/**
* 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.
*
* @param intent The Intent supplied to {@link android.content.Context#startService},
* as given.
* @param startId A unique integer representing this specific request to
* start. Use with {@link #stopSelfResult(int)}.
*
* @see #stopSelfResult(int)
* @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, but it will <em>not</em> call
* {@link #onStartCommand} unless there has been a new call to
* {@link Context#startService Context.startService(Intent)} with a new
* Intent to deliver.
*
* <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)}.
*
* <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}.
*/
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. 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)}.
*
* @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 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
@@ -393,6 +493,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() {
@@ -405,4 +507,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;
}

View File

@@ -132,6 +132,19 @@ public class Build {
* </ul>
*/
public static final int DONUT = 4;
/**
* Current work on "Eclair" development branch.
*
* <p>Applications targeting this or a later release will get these
* new changes in behavior:</p>
* <ul>
* <li> The {@link android.app.Service#onStartCommand
* Service.onStartCommand} function will return the new
* {@link android.app.Service#START_STICKY} behavior instead of the
* old compatibility {@link android.app.Service#START_STICKY_COMPATIBILITY}.
* </ul>
*/
public static final int ECLAIR = CUR_DEVELOPMENT;
}
/** The type of build, like "user" or "eng". */