am cf3a0830
: Merge change 22400 into eclair
Merge commit 'cf3a08307d1599eaa91d7cc4e7c601e5fa13037f' into eclair-plus-aosp * commit 'cf3a08307d1599eaa91d7cc4e7c601e5fa13037f': Add more control over a service's start state.
This commit is contained in:
118
api/current.xml
118
api/current.xml
@ -21568,6 +21568,19 @@
|
|||||||
<parameter name="intent" type="android.content.Intent">
|
<parameter name="intent" type="android.content.Intent">
|
||||||
</parameter>
|
</parameter>
|
||||||
</method>
|
</method>
|
||||||
|
<method name="setIntentRedelivery"
|
||||||
|
return="void"
|
||||||
|
abstract="false"
|
||||||
|
native="false"
|
||||||
|
synchronized="false"
|
||||||
|
static="false"
|
||||||
|
final="false"
|
||||||
|
deprecated="not deprecated"
|
||||||
|
visibility="public"
|
||||||
|
>
|
||||||
|
<parameter name="enabled" type="boolean">
|
||||||
|
</parameter>
|
||||||
|
</method>
|
||||||
</class>
|
</class>
|
||||||
<class name="KeyguardManager"
|
<class name="KeyguardManager"
|
||||||
extends="java.lang.Object"
|
extends="java.lang.Object"
|
||||||
@ -23767,11 +23780,28 @@
|
|||||||
synchronized="false"
|
synchronized="false"
|
||||||
static="false"
|
static="false"
|
||||||
final="false"
|
final="false"
|
||||||
|
deprecated="deprecated"
|
||||||
|
visibility="public"
|
||||||
|
>
|
||||||
|
<parameter name="intent" type="android.content.Intent">
|
||||||
|
</parameter>
|
||||||
|
<parameter name="startId" type="int">
|
||||||
|
</parameter>
|
||||||
|
</method>
|
||||||
|
<method name="onStartCommand"
|
||||||
|
return="int"
|
||||||
|
abstract="false"
|
||||||
|
native="false"
|
||||||
|
synchronized="false"
|
||||||
|
static="false"
|
||||||
|
final="false"
|
||||||
deprecated="not deprecated"
|
deprecated="not deprecated"
|
||||||
visibility="public"
|
visibility="public"
|
||||||
>
|
>
|
||||||
<parameter name="intent" type="android.content.Intent">
|
<parameter name="intent" type="android.content.Intent">
|
||||||
</parameter>
|
</parameter>
|
||||||
|
<parameter name="flags" type="int">
|
||||||
|
</parameter>
|
||||||
<parameter name="startId" type="int">
|
<parameter name="startId" type="int">
|
||||||
</parameter>
|
</parameter>
|
||||||
</method>
|
</method>
|
||||||
@ -23866,6 +23896,83 @@
|
|||||||
<parameter name="startId" type="int">
|
<parameter name="startId" type="int">
|
||||||
</parameter>
|
</parameter>
|
||||||
</method>
|
</method>
|
||||||
|
<field name="START_CONTINUATION_MASK"
|
||||||
|
type="int"
|
||||||
|
transient="false"
|
||||||
|
volatile="false"
|
||||||
|
value="15"
|
||||||
|
static="true"
|
||||||
|
final="true"
|
||||||
|
deprecated="not deprecated"
|
||||||
|
visibility="public"
|
||||||
|
>
|
||||||
|
</field>
|
||||||
|
<field name="START_FLAG_REDELIVERY"
|
||||||
|
type="int"
|
||||||
|
transient="false"
|
||||||
|
volatile="false"
|
||||||
|
value="1"
|
||||||
|
static="true"
|
||||||
|
final="true"
|
||||||
|
deprecated="not deprecated"
|
||||||
|
visibility="public"
|
||||||
|
>
|
||||||
|
</field>
|
||||||
|
<field name="START_FLAG_RETRY"
|
||||||
|
type="int"
|
||||||
|
transient="false"
|
||||||
|
volatile="false"
|
||||||
|
value="2"
|
||||||
|
static="true"
|
||||||
|
final="true"
|
||||||
|
deprecated="not deprecated"
|
||||||
|
visibility="public"
|
||||||
|
>
|
||||||
|
</field>
|
||||||
|
<field name="START_NOT_STICKY"
|
||||||
|
type="int"
|
||||||
|
transient="false"
|
||||||
|
volatile="false"
|
||||||
|
value="2"
|
||||||
|
static="true"
|
||||||
|
final="true"
|
||||||
|
deprecated="not deprecated"
|
||||||
|
visibility="public"
|
||||||
|
>
|
||||||
|
</field>
|
||||||
|
<field name="START_REDELIVER_INTENT"
|
||||||
|
type="int"
|
||||||
|
transient="false"
|
||||||
|
volatile="false"
|
||||||
|
value="3"
|
||||||
|
static="true"
|
||||||
|
final="true"
|
||||||
|
deprecated="not deprecated"
|
||||||
|
visibility="public"
|
||||||
|
>
|
||||||
|
</field>
|
||||||
|
<field name="START_STICKY"
|
||||||
|
type="int"
|
||||||
|
transient="false"
|
||||||
|
volatile="false"
|
||||||
|
value="1"
|
||||||
|
static="true"
|
||||||
|
final="true"
|
||||||
|
deprecated="not deprecated"
|
||||||
|
visibility="public"
|
||||||
|
>
|
||||||
|
</field>
|
||||||
|
<field name="START_STICKY_COMPATIBILITY"
|
||||||
|
type="int"
|
||||||
|
transient="false"
|
||||||
|
volatile="false"
|
||||||
|
value="0"
|
||||||
|
static="true"
|
||||||
|
final="true"
|
||||||
|
deprecated="not deprecated"
|
||||||
|
visibility="public"
|
||||||
|
>
|
||||||
|
</field>
|
||||||
</class>
|
</class>
|
||||||
<class name="TabActivity"
|
<class name="TabActivity"
|
||||||
extends="android.app.ActivityGroup"
|
extends="android.app.ActivityGroup"
|
||||||
@ -95009,6 +95116,17 @@
|
|||||||
visibility="public"
|
visibility="public"
|
||||||
>
|
>
|
||||||
</field>
|
</field>
|
||||||
|
<field name="ECLAIR"
|
||||||
|
type="int"
|
||||||
|
transient="false"
|
||||||
|
volatile="false"
|
||||||
|
value="10000"
|
||||||
|
static="true"
|
||||||
|
final="true"
|
||||||
|
deprecated="not deprecated"
|
||||||
|
visibility="public"
|
||||||
|
>
|
||||||
|
</field>
|
||||||
</class>
|
</class>
|
||||||
<class name="Bundle"
|
<class name="Bundle"
|
||||||
extends="java.lang.Object"
|
extends="java.lang.Object"
|
||||||
|
@ -608,7 +608,10 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
|
|||||||
case SERVICE_DONE_EXECUTING_TRANSACTION: {
|
case SERVICE_DONE_EXECUTING_TRANSACTION: {
|
||||||
data.enforceInterface(IActivityManager.descriptor);
|
data.enforceInterface(IActivityManager.descriptor);
|
||||||
IBinder token = data.readStrongBinder();
|
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();
|
reply.writeNoException();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -1746,11 +1749,15 @@ class ActivityManagerProxy implements IActivityManager
|
|||||||
reply.recycle();
|
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 data = Parcel.obtain();
|
||||||
Parcel reply = Parcel.obtain();
|
Parcel reply = Parcel.obtain();
|
||||||
data.writeInterfaceToken(IActivityManager.descriptor);
|
data.writeInterfaceToken(IActivityManager.descriptor);
|
||||||
data.writeStrongBinder(token);
|
data.writeStrongBinder(token);
|
||||||
|
data.writeInt(type);
|
||||||
|
data.writeInt(startId);
|
||||||
|
data.writeInt(res);
|
||||||
mRemote.transact(SERVICE_DONE_EXECUTING_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY);
|
mRemote.transact(SERVICE_DONE_EXECUTING_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY);
|
||||||
reply.readException();
|
reply.readException();
|
||||||
data.recycle();
|
data.recycle();
|
||||||
|
@ -1215,6 +1215,7 @@ public final class ActivityThread {
|
|||||||
private static final class ServiceArgsData {
|
private static final class ServiceArgsData {
|
||||||
IBinder token;
|
IBinder token;
|
||||||
int startId;
|
int startId;
|
||||||
|
int flags;
|
||||||
Intent args;
|
Intent args;
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "ServiceArgsData{token=" + token + " startId=" + startId
|
return "ServiceArgsData{token=" + token + " startId=" + startId
|
||||||
@ -1417,10 +1418,11 @@ public final class ActivityThread {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final void scheduleServiceArgs(IBinder token, int startId,
|
public final void scheduleServiceArgs(IBinder token, int startId,
|
||||||
Intent args) {
|
int flags ,Intent args) {
|
||||||
ServiceArgsData s = new ServiceArgsData();
|
ServiceArgsData s = new ServiceArgsData();
|
||||||
s.token = token;
|
s.token = token;
|
||||||
s.startId = startId;
|
s.startId = startId;
|
||||||
|
s.flags = flags;
|
||||||
s.args = args;
|
s.args = args;
|
||||||
|
|
||||||
queueOrSendMessage(H.SERVICE_ARGS, s);
|
queueOrSendMessage(H.SERVICE_ARGS, s);
|
||||||
@ -2684,7 +2686,8 @@ public final class ActivityThread {
|
|||||||
service.onCreate();
|
service.onCreate();
|
||||||
mServices.put(data.token, service);
|
mServices.put(data.token, service);
|
||||||
try {
|
try {
|
||||||
ActivityManagerNative.getDefault().serviceDoneExecuting(data.token);
|
ActivityManagerNative.getDefault().serviceDoneExecuting(
|
||||||
|
data.token, 0, 0, 0);
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
// nothing to do.
|
// nothing to do.
|
||||||
}
|
}
|
||||||
@ -2710,7 +2713,7 @@ public final class ActivityThread {
|
|||||||
} else {
|
} else {
|
||||||
s.onRebind(data.intent);
|
s.onRebind(data.intent);
|
||||||
ActivityManagerNative.getDefault().serviceDoneExecuting(
|
ActivityManagerNative.getDefault().serviceDoneExecuting(
|
||||||
data.token);
|
data.token, 0, 0, 0);
|
||||||
}
|
}
|
||||||
} catch (RemoteException ex) {
|
} catch (RemoteException ex) {
|
||||||
}
|
}
|
||||||
@ -2736,7 +2739,7 @@ public final class ActivityThread {
|
|||||||
data.token, data.intent, doRebind);
|
data.token, data.intent, doRebind);
|
||||||
} else {
|
} else {
|
||||||
ActivityManagerNative.getDefault().serviceDoneExecuting(
|
ActivityManagerNative.getDefault().serviceDoneExecuting(
|
||||||
data.token);
|
data.token, 0, 0, 0);
|
||||||
}
|
}
|
||||||
} catch (RemoteException ex) {
|
} catch (RemoteException ex) {
|
||||||
}
|
}
|
||||||
@ -2773,9 +2776,10 @@ public final class ActivityThread {
|
|||||||
if (data.args != null) {
|
if (data.args != null) {
|
||||||
data.args.setExtrasClassLoader(s.getClassLoader());
|
data.args.setExtrasClassLoader(s.getClassLoader());
|
||||||
}
|
}
|
||||||
s.onStart(data.args, data.startId);
|
int res = s.onStartCommand(data.args, data.flags, data.startId);
|
||||||
try {
|
try {
|
||||||
ActivityManagerNative.getDefault().serviceDoneExecuting(data.token);
|
ActivityManagerNative.getDefault().serviceDoneExecuting(
|
||||||
|
data.token, 1, data.startId, res);
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
// nothing to do.
|
// nothing to do.
|
||||||
}
|
}
|
||||||
@ -2801,7 +2805,8 @@ public final class ActivityThread {
|
|||||||
((ApplicationContext) context).scheduleFinalCleanup(who, "Service");
|
((ApplicationContext) context).scheduleFinalCleanup(who, "Service");
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
ActivityManagerNative.getDefault().serviceDoneExecuting(token);
|
ActivityManagerNative.getDefault().serviceDoneExecuting(
|
||||||
|
token, 0, 0, 0);
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
// nothing to do.
|
// nothing to do.
|
||||||
}
|
}
|
||||||
|
@ -206,8 +206,14 @@ public abstract class ApplicationThreadNative extends Binder
|
|||||||
data.enforceInterface(IApplicationThread.descriptor);
|
data.enforceInterface(IApplicationThread.descriptor);
|
||||||
IBinder token = data.readStrongBinder();
|
IBinder token = data.readStrongBinder();
|
||||||
int startId = data.readInt();
|
int startId = data.readInt();
|
||||||
Intent args = Intent.CREATOR.createFromParcel(data);
|
int fl = data.readInt();
|
||||||
scheduleServiceArgs(token, startId, args);
|
Intent args;
|
||||||
|
if (data.readInt() != 0) {
|
||||||
|
args = Intent.CREATOR.createFromParcel(data);
|
||||||
|
} else {
|
||||||
|
args = null;
|
||||||
|
}
|
||||||
|
scheduleServiceArgs(token, startId, fl, args);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -573,12 +579,18 @@ class ApplicationThreadProxy implements IApplicationThread {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final void scheduleServiceArgs(IBinder token, int startId,
|
public final void scheduleServiceArgs(IBinder token, int startId,
|
||||||
Intent args) throws RemoteException {
|
int flags, Intent args) throws RemoteException {
|
||||||
Parcel data = Parcel.obtain();
|
Parcel data = Parcel.obtain();
|
||||||
data.writeInterfaceToken(IApplicationThread.descriptor);
|
data.writeInterfaceToken(IApplicationThread.descriptor);
|
||||||
data.writeStrongBinder(token);
|
data.writeStrongBinder(token);
|
||||||
data.writeInt(startId);
|
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,
|
mRemote.transact(SCHEDULE_SERVICE_ARGS_TRANSACTION, data, null,
|
||||||
IBinder.FLAG_ONEWAY);
|
IBinder.FLAG_ONEWAY);
|
||||||
data.recycle();
|
data.recycle();
|
||||||
|
@ -149,7 +149,8 @@ public interface IActivityManager extends IInterface {
|
|||||||
public void unbindFinished(IBinder token, Intent service,
|
public void unbindFinished(IBinder token, Intent service,
|
||||||
boolean doRebind) throws RemoteException;
|
boolean doRebind) throws RemoteException;
|
||||||
/* oneway */
|
/* 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 IBinder peekService(Intent service, String resolvedType) throws RemoteException;
|
||||||
|
|
||||||
public boolean bindBackupAgent(ApplicationInfo appInfo, int backupRestoreMode)
|
public boolean bindBackupAgent(ApplicationInfo appInfo, int backupRestoreMode)
|
||||||
|
@ -71,7 +71,8 @@ public interface IApplicationThread extends IInterface {
|
|||||||
Intent intent, boolean rebind) throws RemoteException;
|
Intent intent, boolean rebind) throws RemoteException;
|
||||||
void scheduleUnbindService(IBinder token,
|
void scheduleUnbindService(IBinder token,
|
||||||
Intent intent) throws RemoteException;
|
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;
|
void scheduleStopService(IBinder token) throws RemoteException;
|
||||||
static final int DEBUG_OFF = 0;
|
static final int DEBUG_OFF = 0;
|
||||||
static final int DEBUG_ON = 1;
|
static final int DEBUG_ON = 1;
|
||||||
|
@ -18,6 +18,7 @@ public abstract class IntentService extends Service {
|
|||||||
private volatile Looper mServiceLooper;
|
private volatile Looper mServiceLooper;
|
||||||
private volatile ServiceHandler mServiceHandler;
|
private volatile ServiceHandler mServiceHandler;
|
||||||
private String mName;
|
private String mName;
|
||||||
|
private boolean mRedelivery;
|
||||||
|
|
||||||
private final class ServiceHandler extends Handler {
|
private final class ServiceHandler extends Handler {
|
||||||
public ServiceHandler(Looper looper) {
|
public ServiceHandler(Looper looper) {
|
||||||
@ -36,6 +37,19 @@ public abstract class IntentService extends Service {
|
|||||||
mName = name;
|
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
|
@Override
|
||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
@ -48,13 +62,18 @@ public abstract class IntentService extends Service {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onStart(Intent intent, int startId) {
|
public void onStart(Intent intent, int startId) {
|
||||||
super.onStart(intent, startId);
|
|
||||||
Message msg = mServiceHandler.obtainMessage();
|
Message msg = mServiceHandler.obtainMessage();
|
||||||
msg.arg1 = startId;
|
msg.arg1 = startId;
|
||||||
msg.obj = intent;
|
msg.obj = intent;
|
||||||
mServiceHandler.sendMessage(msg);
|
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
|
@Override
|
||||||
public void onDestroy() {
|
public void onDestroy() {
|
||||||
mServiceLooper.quit();
|
mServiceLooper.quit();
|
||||||
|
@ -22,6 +22,7 @@ import android.content.Intent;
|
|||||||
import android.content.ContextWrapper;
|
import android.content.ContextWrapper;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
@ -168,19 +169,118 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac
|
|||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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
|
* 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
|
* {@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.
|
* 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},
|
* @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
|
* @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)
|
* @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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -393,6 +493,8 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac
|
|||||||
mToken = token;
|
mToken = token;
|
||||||
mApplication = application;
|
mApplication = application;
|
||||||
mActivityManager = (IActivityManager)activityManager;
|
mActivityManager = (IActivityManager)activityManager;
|
||||||
|
mStartCompatibility = getApplicationInfo().targetSdkVersion
|
||||||
|
< Build.VERSION_CODES.ECLAIR;
|
||||||
}
|
}
|
||||||
|
|
||||||
final String getClassName() {
|
final String getClassName() {
|
||||||
@ -405,4 +507,5 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac
|
|||||||
private IBinder mToken = null;
|
private IBinder mToken = null;
|
||||||
private Application mApplication = null;
|
private Application mApplication = null;
|
||||||
private IActivityManager mActivityManager = null;
|
private IActivityManager mActivityManager = null;
|
||||||
|
private boolean mStartCompatibility = false;
|
||||||
}
|
}
|
||||||
|
@ -132,6 +132,19 @@ public class Build {
|
|||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
public static final int DONUT = 4;
|
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". */
|
/** The type of build, like "user" or "eng". */
|
||||||
|
@ -42,6 +42,7 @@ import android.app.Instrumentation;
|
|||||||
import android.app.Notification;
|
import android.app.Notification;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.app.ResultInfo;
|
import android.app.ResultInfo;
|
||||||
|
import android.app.Service;
|
||||||
import android.backup.IBackupManager;
|
import android.backup.IBackupManager;
|
||||||
import android.content.ActivityNotFoundException;
|
import android.content.ActivityNotFoundException;
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
@ -9385,7 +9386,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
|||||||
sr.app = null;
|
sr.app = null;
|
||||||
sr.executeNesting = 0;
|
sr.executeNesting = 0;
|
||||||
mStoppingServices.remove(sr);
|
mStoppingServices.remove(sr);
|
||||||
if (sr.bindings.size() > 0) {
|
|
||||||
|
boolean hasClients = sr.bindings.size() > 0;
|
||||||
|
if (hasClients) {
|
||||||
Iterator<IntentBindRecord> bindings
|
Iterator<IntentBindRecord> bindings
|
||||||
= sr.bindings.values().iterator();
|
= sr.bindings.values().iterator();
|
||||||
while (bindings.hasNext()) {
|
while (bindings.hasNext()) {
|
||||||
@ -9406,7 +9409,20 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
|||||||
} else if (!allowRestart) {
|
} else if (!allowRestart) {
|
||||||
bringDownServiceLocked(sr, true);
|
bringDownServiceLocked(sr, true);
|
||||||
} else {
|
} else {
|
||||||
scheduleServiceRestartLocked(sr);
|
boolean canceled = scheduleServiceRestartLocked(sr, true);
|
||||||
|
|
||||||
|
// Should the service remain running? Note that in the
|
||||||
|
// extreme case of so many attempts to deliver a command
|
||||||
|
// that it failed, that we also will stop it here.
|
||||||
|
if (sr.startRequested && (sr.stopIfKilled || canceled)) {
|
||||||
|
if (sr.pendingStarts.size() == 0) {
|
||||||
|
sr.startRequested = false;
|
||||||
|
if (!hasClients) {
|
||||||
|
// Whoops, no reason to restart!
|
||||||
|
bringDownServiceLocked(sr, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -9845,35 +9861,55 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
|||||||
|
|
||||||
private final void sendServiceArgsLocked(ServiceRecord r,
|
private final void sendServiceArgsLocked(ServiceRecord r,
|
||||||
boolean oomAdjusted) {
|
boolean oomAdjusted) {
|
||||||
final int N = r.startArgs.size();
|
final int N = r.pendingStarts.size();
|
||||||
if (N == 0) {
|
if (N == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final int BASEID = r.lastStartId - N + 1;
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
while (i < N) {
|
while (i < N) {
|
||||||
try {
|
try {
|
||||||
Intent args = r.startArgs.get(i);
|
ServiceRecord.StartItem si = r.pendingStarts.get(i);
|
||||||
if (DEBUG_SERVICE) Log.v(TAG, "Sending arguments to service: "
|
if (DEBUG_SERVICE) Log.v(TAG, "Sending arguments to service: "
|
||||||
+ r.name + " " + r.intent + " args=" + args);
|
+ r.name + " " + r.intent + " args=" + si.intent);
|
||||||
|
if (si.intent == null && N > 0) {
|
||||||
|
// If somehow we got a dummy start at the front, then
|
||||||
|
// just drop it here.
|
||||||
|
i++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
bumpServiceExecutingLocked(r);
|
bumpServiceExecutingLocked(r);
|
||||||
if (!oomAdjusted) {
|
if (!oomAdjusted) {
|
||||||
oomAdjusted = true;
|
oomAdjusted = true;
|
||||||
updateOomAdjLocked(r.app);
|
updateOomAdjLocked(r.app);
|
||||||
}
|
}
|
||||||
r.app.thread.scheduleServiceArgs(r, BASEID+i, args);
|
int flags = 0;
|
||||||
|
if (si.deliveryCount > 0) {
|
||||||
|
flags |= Service.START_FLAG_RETRY;
|
||||||
|
}
|
||||||
|
if (si.doneExecutingCount > 0) {
|
||||||
|
flags |= Service.START_FLAG_REDELIVERY;
|
||||||
|
}
|
||||||
|
r.app.thread.scheduleServiceArgs(r, si.id, flags, si.intent);
|
||||||
|
si.deliveredTime = SystemClock.uptimeMillis();
|
||||||
|
r.deliveredStarts.add(si);
|
||||||
|
si.deliveryCount++;
|
||||||
i++;
|
i++;
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
// Remote process gone... we'll let the normal cleanup take
|
||||||
|
// care of this.
|
||||||
|
break;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
Log.w(TAG, "Unexpected exception", e);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (i == N) {
|
if (i == N) {
|
||||||
r.startArgs.clear();
|
r.pendingStarts.clear();
|
||||||
} else {
|
} else {
|
||||||
while (i > 0) {
|
while (i > 0) {
|
||||||
r.startArgs.remove(0);
|
|
||||||
i--;
|
i--;
|
||||||
|
r.pendingStarts.remove(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -9942,19 +9978,61 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
|||||||
} finally {
|
} finally {
|
||||||
if (!created) {
|
if (!created) {
|
||||||
app.services.remove(r);
|
app.services.remove(r);
|
||||||
scheduleServiceRestartLocked(r);
|
scheduleServiceRestartLocked(r, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
requestServiceBindingsLocked(r);
|
requestServiceBindingsLocked(r);
|
||||||
|
|
||||||
|
// If the service is in the started state, and there are no
|
||||||
|
// pending arguments, then fake up one so its onStartCommand() will
|
||||||
|
// be called.
|
||||||
|
if (r.startRequested && r.callStart && r.pendingStarts.size() == 0) {
|
||||||
|
r.lastStartId++;
|
||||||
|
if (r.lastStartId < 1) {
|
||||||
|
r.lastStartId = 1;
|
||||||
|
}
|
||||||
|
r.pendingStarts.add(new ServiceRecord.StartItem(r.lastStartId, null));
|
||||||
|
}
|
||||||
|
|
||||||
sendServiceArgsLocked(r, true);
|
sendServiceArgsLocked(r, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final void scheduleServiceRestartLocked(ServiceRecord r) {
|
private final boolean scheduleServiceRestartLocked(ServiceRecord r,
|
||||||
|
boolean allowCancel) {
|
||||||
|
boolean canceled = false;
|
||||||
|
|
||||||
|
long minDuration = SERVICE_RESTART_DURATION;
|
||||||
|
long resetTime = minDuration*2*2*2;
|
||||||
|
|
||||||
|
// Any delivered but not yet finished starts should be put back
|
||||||
|
// on the pending list.
|
||||||
|
final int N = r.deliveredStarts.size();
|
||||||
|
if (N > 0) {
|
||||||
|
for (int i=N-1; i>=0; i--) {
|
||||||
|
ServiceRecord.StartItem si = r.deliveredStarts.get(i);
|
||||||
|
if (si.intent == null) {
|
||||||
|
// We'll generate this again if needed.
|
||||||
|
} else if (!allowCancel || (si.deliveryCount < ServiceRecord.MAX_DELIVERY_COUNT
|
||||||
|
&& si.doneExecutingCount < ServiceRecord.MAX_DONE_EXECUTING_COUNT)) {
|
||||||
|
r.pendingStarts.add(0, si);
|
||||||
|
long dur = SystemClock.uptimeMillis() - si.deliveredTime;
|
||||||
|
dur *= 2;
|
||||||
|
if (minDuration < dur) minDuration = dur;
|
||||||
|
if (resetTime < dur) resetTime = dur;
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "Canceling start item " + si.intent + " in service "
|
||||||
|
+ r.name);
|
||||||
|
canceled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
r.deliveredStarts.clear();
|
||||||
|
}
|
||||||
|
|
||||||
r.totalRestartCount++;
|
r.totalRestartCount++;
|
||||||
if (r.restartDelay == 0) {
|
if (r.restartDelay == 0) {
|
||||||
r.restartCount++;
|
r.restartCount++;
|
||||||
r.restartDelay = SERVICE_RESTART_DURATION;
|
r.restartDelay = minDuration;
|
||||||
} else {
|
} else {
|
||||||
// If it has been a "reasonably long time" since the service
|
// If it has been a "reasonably long time" since the service
|
||||||
// was started, then reset our restart duration back to
|
// was started, then reset our restart duration back to
|
||||||
@ -9962,17 +10040,21 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
|||||||
// on a service that just occasionally gets killed (which is
|
// on a service that just occasionally gets killed (which is
|
||||||
// a normal case, due to process being killed to reclaim memory).
|
// a normal case, due to process being killed to reclaim memory).
|
||||||
long now = SystemClock.uptimeMillis();
|
long now = SystemClock.uptimeMillis();
|
||||||
if (now > (r.restartTime+(SERVICE_RESTART_DURATION*2*2*2))) {
|
if (now > (r.restartTime+resetTime)) {
|
||||||
r.restartCount = 1;
|
r.restartCount = 1;
|
||||||
r.restartDelay = SERVICE_RESTART_DURATION;
|
r.restartDelay = minDuration;
|
||||||
} else {
|
} else {
|
||||||
r.restartDelay *= 2;
|
r.restartDelay *= 4;
|
||||||
|
if (r.restartDelay < minDuration) {
|
||||||
|
r.restartDelay = minDuration;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!mRestartingServices.contains(r)) {
|
if (!mRestartingServices.contains(r)) {
|
||||||
mRestartingServices.add(r);
|
mRestartingServices.add(r);
|
||||||
}
|
}
|
||||||
r.cancelNotification();
|
r.cancelNotification();
|
||||||
|
|
||||||
mHandler.removeCallbacks(r.restarter);
|
mHandler.removeCallbacks(r.restarter);
|
||||||
mHandler.postDelayed(r.restarter, r.restartDelay);
|
mHandler.postDelayed(r.restarter, r.restartDelay);
|
||||||
r.nextRestartTime = SystemClock.uptimeMillis() + r.restartDelay;
|
r.nextRestartTime = SystemClock.uptimeMillis() + r.restartDelay;
|
||||||
@ -9985,6 +10067,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
|||||||
msg.what = SERVICE_ERROR_MSG;
|
msg.what = SERVICE_ERROR_MSG;
|
||||||
msg.obj = r;
|
msg.obj = r;
|
||||||
mHandler.sendMessage(msg);
|
mHandler.sendMessage(msg);
|
||||||
|
|
||||||
|
return canceled;
|
||||||
}
|
}
|
||||||
|
|
||||||
final void performServiceRestartLocked(ServiceRecord r) {
|
final void performServiceRestartLocked(ServiceRecord r) {
|
||||||
@ -10146,6 +10230,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
|||||||
r.foregroundId = 0;
|
r.foregroundId = 0;
|
||||||
r.foregroundNoti = null;
|
r.foregroundNoti = null;
|
||||||
|
|
||||||
|
// Clear start entries.
|
||||||
|
r.deliveredStarts.clear();
|
||||||
|
r.pendingStarts.clear();
|
||||||
|
|
||||||
if (r.app != null) {
|
if (r.app != null) {
|
||||||
synchronized (r.stats.getBatteryStats()) {
|
synchronized (r.stats.getBatteryStats()) {
|
||||||
r.stats.stopLaunchedLocked();
|
r.stats.stopLaunchedLocked();
|
||||||
@ -10207,11 +10295,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
|||||||
+ r.shortName);
|
+ r.shortName);
|
||||||
}
|
}
|
||||||
r.startRequested = true;
|
r.startRequested = true;
|
||||||
r.startArgs.add(service);
|
r.callStart = false;
|
||||||
r.lastStartId++;
|
r.lastStartId++;
|
||||||
if (r.lastStartId < 1) {
|
if (r.lastStartId < 1) {
|
||||||
r.lastStartId = 1;
|
r.lastStartId = 1;
|
||||||
}
|
}
|
||||||
|
r.pendingStarts.add(new ServiceRecord.StartItem(r.lastStartId, service));
|
||||||
r.lastActivity = SystemClock.uptimeMillis();
|
r.lastActivity = SystemClock.uptimeMillis();
|
||||||
synchronized (r.stats.getBatteryStats()) {
|
synchronized (r.stats.getBatteryStats()) {
|
||||||
r.stats.startRunningLocked();
|
r.stats.startRunningLocked();
|
||||||
@ -10279,6 +10368,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
|||||||
r.record.stats.stopRunningLocked();
|
r.record.stats.stopRunningLocked();
|
||||||
}
|
}
|
||||||
r.record.startRequested = false;
|
r.record.startRequested = false;
|
||||||
|
r.record.callStart = false;
|
||||||
final long origId = Binder.clearCallingIdentity();
|
final long origId = Binder.clearCallingIdentity();
|
||||||
bringDownServiceLocked(r.record, false);
|
bringDownServiceLocked(r.record, false);
|
||||||
Binder.restoreCallingIdentity(origId);
|
Binder.restoreCallingIdentity(origId);
|
||||||
@ -10327,10 +10417,35 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
|||||||
if (DEBUG_SERVICE) Log.v(TAG, "stopServiceToken: " + className
|
if (DEBUG_SERVICE) Log.v(TAG, "stopServiceToken: " + className
|
||||||
+ " " + token + " startId=" + startId);
|
+ " " + token + " startId=" + startId);
|
||||||
ServiceRecord r = findServiceLocked(className, token);
|
ServiceRecord r = findServiceLocked(className, token);
|
||||||
if (r != null && (startId < 0 || r.lastStartId == startId)) {
|
if (r != null) {
|
||||||
|
if (startId >= 0) {
|
||||||
|
// Asked to only stop if done with all work. Note that
|
||||||
|
// to avoid leaks, we will take this as dropping all
|
||||||
|
// start items up to and including this one.
|
||||||
|
ServiceRecord.StartItem si = r.findDeliveredStart(startId, false);
|
||||||
|
if (si != null) {
|
||||||
|
while (r.deliveredStarts.size() > 0) {
|
||||||
|
if (r.deliveredStarts.remove(0) == si) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (r.lastStartId != startId) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (r.deliveredStarts.size() > 0) {
|
||||||
|
Log.w(TAG, "stopServiceToken startId " + startId
|
||||||
|
+ " is last, but have " + r.deliveredStarts.size()
|
||||||
|
+ " remaining args");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
synchronized (r.stats.getBatteryStats()) {
|
synchronized (r.stats.getBatteryStats()) {
|
||||||
r.stats.stopRunningLocked();
|
r.stats.stopRunningLocked();
|
||||||
r.startRequested = false;
|
r.startRequested = false;
|
||||||
|
r.callStart = false;
|
||||||
}
|
}
|
||||||
final long origId = Binder.clearCallingIdentity();
|
final long origId = Binder.clearCallingIdentity();
|
||||||
bringDownServiceLocked(r, false);
|
bringDownServiceLocked(r, false);
|
||||||
@ -10674,7 +10789,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void serviceDoneExecuting(IBinder token) {
|
public void serviceDoneExecuting(IBinder token, int type, int startId, int res) {
|
||||||
synchronized(this) {
|
synchronized(this) {
|
||||||
if (!(token instanceof ServiceRecord)) {
|
if (!(token instanceof ServiceRecord)) {
|
||||||
throw new IllegalArgumentException("Invalid service token");
|
throw new IllegalArgumentException("Invalid service token");
|
||||||
@ -10692,6 +10807,51 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (type == 1) {
|
||||||
|
// This is a call from a service start... take care of
|
||||||
|
// book-keeping.
|
||||||
|
r.callStart = true;
|
||||||
|
switch (res) {
|
||||||
|
case Service.START_STICKY_COMPATIBILITY:
|
||||||
|
case Service.START_STICKY: {
|
||||||
|
// We are done with the associated start arguments.
|
||||||
|
r.findDeliveredStart(startId, true);
|
||||||
|
// Don't stop if killed.
|
||||||
|
r.stopIfKilled = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Service.START_NOT_STICKY: {
|
||||||
|
// We are done with the associated start arguments.
|
||||||
|
r.findDeliveredStart(startId, true);
|
||||||
|
if (r.lastStartId == startId) {
|
||||||
|
// There is no more work, and this service
|
||||||
|
// doesn't want to hang around if killed.
|
||||||
|
r.stopIfKilled = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Service.START_REDELIVER_INTENT: {
|
||||||
|
// We'll keep this item until they explicitly
|
||||||
|
// call stop for it, but keep track of the fact
|
||||||
|
// that it was delivered.
|
||||||
|
ServiceRecord.StartItem si = r.findDeliveredStart(startId, false);
|
||||||
|
if (si != null) {
|
||||||
|
si.deliveryCount = 0;
|
||||||
|
si.doneExecutingCount++;
|
||||||
|
// Don't stop if killed.
|
||||||
|
r.stopIfKilled = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
"Unknown service start result: " + res);
|
||||||
|
}
|
||||||
|
if (res == Service.START_STICKY_COMPATIBILITY) {
|
||||||
|
r.callStart = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final long origId = Binder.clearCallingIdentity();
|
final long origId = Binder.clearCallingIdentity();
|
||||||
serviceDoneExecutingLocked(r, inStopping);
|
serviceDoneExecutingLocked(r, inStopping);
|
||||||
Binder.restoreCallingIdentity(origId);
|
Binder.restoreCallingIdentity(origId);
|
||||||
|
@ -64,7 +64,28 @@ class ServiceRecord extends Binder {
|
|||||||
final HashMap<IBinder, ConnectionRecord> connections
|
final HashMap<IBinder, ConnectionRecord> connections
|
||||||
= new HashMap<IBinder, ConnectionRecord>();
|
= new HashMap<IBinder, ConnectionRecord>();
|
||||||
// IBinder -> ConnectionRecord of all bound clients
|
// IBinder -> ConnectionRecord of all bound clients
|
||||||
final List<Intent> startArgs = new ArrayList<Intent>();
|
|
||||||
|
// Maximum number of delivery attempts before giving up.
|
||||||
|
static final int MAX_DELIVERY_COUNT = 3;
|
||||||
|
|
||||||
|
// Maximum number of times it can fail during execution before giving up.
|
||||||
|
static final int MAX_DONE_EXECUTING_COUNT = 6;
|
||||||
|
|
||||||
|
static class StartItem {
|
||||||
|
final int id;
|
||||||
|
final Intent intent;
|
||||||
|
long deliveredTime;
|
||||||
|
int deliveryCount;
|
||||||
|
int doneExecutingCount;
|
||||||
|
|
||||||
|
StartItem(int _id, Intent _intent) {
|
||||||
|
id = _id;
|
||||||
|
intent = _intent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final ArrayList<StartItem> deliveredStarts = new ArrayList<StartItem>();
|
||||||
|
// start() arguments which been delivered.
|
||||||
|
final ArrayList<StartItem> pendingStarts = new ArrayList<StartItem>();
|
||||||
// start() arguments that haven't yet been delivered.
|
// start() arguments that haven't yet been delivered.
|
||||||
|
|
||||||
ProcessRecord app; // where this service is running or null.
|
ProcessRecord app; // where this service is running or null.
|
||||||
@ -73,6 +94,8 @@ class ServiceRecord extends Binder {
|
|||||||
Notification foregroundNoti; // Notification record of foreground state.
|
Notification foregroundNoti; // Notification record of foreground state.
|
||||||
long lastActivity; // last time there was some activity on the service.
|
long lastActivity; // last time there was some activity on the service.
|
||||||
boolean startRequested; // someone explicitly called start?
|
boolean startRequested; // someone explicitly called start?
|
||||||
|
boolean stopIfKilled; // last onStart() said to stop if service killed?
|
||||||
|
boolean callStart; // last onStart() has asked to alway be called on restart.
|
||||||
int lastStartId; // identifier of most recent start request.
|
int lastStartId; // identifier of most recent start request.
|
||||||
int executeNesting; // number of outstanding operations keeping foreground.
|
int executeNesting; // number of outstanding operations keeping foreground.
|
||||||
long executingStart; // start time of last execute request.
|
long executingStart; // start time of last execute request.
|
||||||
@ -85,6 +108,25 @@ class ServiceRecord extends Binder {
|
|||||||
|
|
||||||
String stringName; // caching of toString
|
String stringName; // caching of toString
|
||||||
|
|
||||||
|
void dumpStartList(PrintWriter pw, String prefix, List<StartItem> list, long now) {
|
||||||
|
final int N = list.size();
|
||||||
|
for (int i=0; i<N; i++) {
|
||||||
|
StartItem si = list.get(i);
|
||||||
|
pw.print(prefix); pw.print("#"); pw.print(i);
|
||||||
|
pw.print(" id="); pw.print(si.id);
|
||||||
|
if (now != 0) pw.print(" dur="); pw.print(now-si.deliveredTime);
|
||||||
|
if (si.deliveryCount != 0) {
|
||||||
|
pw.print(" dc="); pw.print(si.deliveryCount);
|
||||||
|
}
|
||||||
|
if (si.doneExecutingCount != 0) {
|
||||||
|
pw.print(" dxc="); pw.print(si.doneExecutingCount);
|
||||||
|
}
|
||||||
|
pw.print(" ");
|
||||||
|
if (si.intent != null) pw.println(si.intent.toString());
|
||||||
|
else pw.println("null");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void dump(PrintWriter pw, String prefix) {
|
void dump(PrintWriter pw, String prefix) {
|
||||||
pw.print(prefix); pw.print("intent={");
|
pw.print(prefix); pw.print("intent={");
|
||||||
pw.print(intent.getIntent().toShortString(true, false));
|
pw.print(intent.getIntent().toShortString(true, false));
|
||||||
@ -108,6 +150,8 @@ class ServiceRecord extends Binder {
|
|||||||
pw.print(" restartTime="); pw.println(restartTime);
|
pw.print(" restartTime="); pw.println(restartTime);
|
||||||
if (startRequested || lastStartId != 0) {
|
if (startRequested || lastStartId != 0) {
|
||||||
pw.print(prefix); pw.print("startRequested="); pw.print(startRequested);
|
pw.print(prefix); pw.print("startRequested="); pw.print(startRequested);
|
||||||
|
pw.print(" stopIfKilled="); pw.print(stopIfKilled);
|
||||||
|
pw.print(" callStart="); pw.print(callStart);
|
||||||
pw.print(" lastStartId="); pw.println(lastStartId);
|
pw.print(" lastStartId="); pw.println(lastStartId);
|
||||||
}
|
}
|
||||||
if (executeNesting != 0 || crashCount != 0 || restartCount != 0
|
if (executeNesting != 0 || crashCount != 0 || restartCount != 0
|
||||||
@ -118,8 +162,17 @@ class ServiceRecord extends Binder {
|
|||||||
pw.print(" nextRestartTime="); pw.print(nextRestartTime);
|
pw.print(" nextRestartTime="); pw.print(nextRestartTime);
|
||||||
pw.print(" crashCount="); pw.println(crashCount);
|
pw.print(" crashCount="); pw.println(crashCount);
|
||||||
}
|
}
|
||||||
|
if (deliveredStarts.size() > 0) {
|
||||||
|
pw.print(prefix); pw.println("Delivered Starts:");
|
||||||
|
dumpStartList(pw, prefix, deliveredStarts, SystemClock.uptimeMillis());
|
||||||
|
}
|
||||||
|
if (pendingStarts.size() > 0) {
|
||||||
|
pw.print(prefix); pw.println("Pending Starts:");
|
||||||
|
dumpStartList(pw, prefix, pendingStarts, 0);
|
||||||
|
}
|
||||||
if (bindings.size() > 0) {
|
if (bindings.size() > 0) {
|
||||||
Iterator<IntentBindRecord> it = bindings.values().iterator();
|
Iterator<IntentBindRecord> it = bindings.values().iterator();
|
||||||
|
pw.print(prefix); pw.println("Bindings:");
|
||||||
while (it.hasNext()) {
|
while (it.hasNext()) {
|
||||||
IntentBindRecord b = it.next();
|
IntentBindRecord b = it.next();
|
||||||
pw.print(prefix); pw.print("* IntentBindRecord{");
|
pw.print(prefix); pw.print("* IntentBindRecord{");
|
||||||
@ -180,6 +233,19 @@ class ServiceRecord extends Binder {
|
|||||||
restartTime = 0;
|
restartTime = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public StartItem findDeliveredStart(int id, boolean remove) {
|
||||||
|
final int N = deliveredStarts.size();
|
||||||
|
for (int i=0; i<N; i++) {
|
||||||
|
StartItem si = deliveredStarts.get(i);
|
||||||
|
if (si.id == id) {
|
||||||
|
if (remove) deliveredStarts.remove(i);
|
||||||
|
return si;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public void postNotification() {
|
public void postNotification() {
|
||||||
if (foregroundId != 0 && foregroundNoti != null) {
|
if (foregroundId != 0 && foregroundNoti != null) {
|
||||||
INotificationManager inm = NotificationManager.getService();
|
INotificationManager inm = NotificationManager.getService();
|
||||||
|
Reference in New Issue
Block a user