diff --git a/api/current.txt b/api/current.txt index e1ca18c8cbbe0..b51c2d33c9e32 100644 --- a/api/current.txt +++ b/api/current.txt @@ -5586,7 +5586,7 @@ package android.app { method public boolean removeAutomaticZenRule(java.lang.String); method public final void setInterruptionFilter(int); method public void setNotificationPolicy(android.app.NotificationManager.Policy); - method public android.content.ComponentName startServiceInForeground(android.content.Intent, int, android.app.Notification); + method public deprecated android.content.ComponentName startServiceInForeground(android.content.Intent, int, android.app.Notification); method public boolean updateAutomaticZenRule(java.lang.String, android.app.AutomaticZenRule); field public static final java.lang.String ACTION_INTERRUPTION_FILTER_CHANGED = "android.app.action.INTERRUPTION_FILTER_CHANGED"; field public static final java.lang.String ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED = "android.app.action.NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED"; @@ -5641,6 +5641,7 @@ package android.app { method public java.lang.String getCreatorPackage(); method public int getCreatorUid(); method public android.os.UserHandle getCreatorUserHandle(); + method public static android.app.PendingIntent getForegroundService(android.content.Context, int, android.content.Intent, int); method public android.content.IntentSender getIntentSender(); method public static android.app.PendingIntent getService(android.content.Context, int, android.content.Intent, int); method public deprecated java.lang.String getTargetPackage(); @@ -8858,6 +8859,7 @@ package android.content { method public abstract void startActivities(android.content.Intent[], android.os.Bundle); method public abstract void startActivity(android.content.Intent); method public abstract void startActivity(android.content.Intent, android.os.Bundle); + method public abstract android.content.ComponentName startForegroundService(android.content.Intent); method public abstract boolean startInstrumentation(android.content.ComponentName, java.lang.String, android.os.Bundle); method public abstract void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int) throws android.content.IntentSender.SendIntentException; method public abstract void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException; @@ -9049,6 +9051,7 @@ package android.content { method public void startActivities(android.content.Intent[], android.os.Bundle); method public void startActivity(android.content.Intent); method public void startActivity(android.content.Intent, android.os.Bundle); + method public android.content.ComponentName startForegroundService(android.content.Intent); method public boolean startInstrumentation(android.content.ComponentName, java.lang.String, android.os.Bundle); method public void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int) throws android.content.IntentSender.SendIntentException; method public void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException; @@ -40679,6 +40682,7 @@ package android.test.mock { method public void startActivities(android.content.Intent[], android.os.Bundle); method public void startActivity(android.content.Intent); method public void startActivity(android.content.Intent, android.os.Bundle); + method public android.content.ComponentName startForegroundService(android.content.Intent); method public boolean startInstrumentation(android.content.ComponentName, java.lang.String, android.os.Bundle); method public void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int) throws android.content.IntentSender.SendIntentException; method public void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException; diff --git a/api/system-current.txt b/api/system-current.txt index 92e2a50b40d52..9e8b67c573b67 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -5780,7 +5780,7 @@ package android.app { method public boolean removeAutomaticZenRule(java.lang.String); method public final void setInterruptionFilter(int); method public void setNotificationPolicy(android.app.NotificationManager.Policy); - method public android.content.ComponentName startServiceInForeground(android.content.Intent, int, android.app.Notification); + method public deprecated android.content.ComponentName startServiceInForeground(android.content.Intent, int, android.app.Notification); method public boolean updateAutomaticZenRule(java.lang.String, android.app.AutomaticZenRule); field public static final java.lang.String ACTION_INTERRUPTION_FILTER_CHANGED = "android.app.action.INTERRUPTION_FILTER_CHANGED"; field public static final java.lang.String ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED = "android.app.action.NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED"; @@ -5835,6 +5835,7 @@ package android.app { method public java.lang.String getCreatorPackage(); method public int getCreatorUid(); method public android.os.UserHandle getCreatorUserHandle(); + method public static android.app.PendingIntent getForegroundService(android.content.Context, int, android.content.Intent, int); method public android.content.IntentSender getIntentSender(); method public static android.app.PendingIntent getService(android.content.Context, int, android.content.Intent, int); method public deprecated java.lang.String getTargetPackage(); @@ -9353,6 +9354,7 @@ package android.content { method public abstract void startActivities(android.content.Intent[], android.os.Bundle); method public abstract void startActivity(android.content.Intent); method public abstract void startActivity(android.content.Intent, android.os.Bundle); + method public abstract android.content.ComponentName startForegroundService(android.content.Intent); method public abstract boolean startInstrumentation(android.content.ComponentName, java.lang.String, android.os.Bundle); method public abstract void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int) throws android.content.IntentSender.SendIntentException; method public abstract void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException; @@ -9558,6 +9560,7 @@ package android.content { method public void startActivities(android.content.Intent[], android.os.Bundle); method public void startActivity(android.content.Intent); method public void startActivity(android.content.Intent, android.os.Bundle); + method public android.content.ComponentName startForegroundService(android.content.Intent); method public boolean startInstrumentation(android.content.ComponentName, java.lang.String, android.os.Bundle); method public void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int) throws android.content.IntentSender.SendIntentException; method public void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException; @@ -44117,6 +44120,7 @@ package android.test.mock { method public void startActivities(android.content.Intent[], android.os.Bundle); method public void startActivity(android.content.Intent); method public void startActivity(android.content.Intent, android.os.Bundle); + method public android.content.ComponentName startForegroundService(android.content.Intent); method public boolean startInstrumentation(android.content.ComponentName, java.lang.String, android.os.Bundle); method public void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int) throws android.content.IntentSender.SendIntentException; method public void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException; diff --git a/api/test-current.txt b/api/test-current.txt index 3d184b23004ca..3bf9dc89dce7a 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -5599,7 +5599,7 @@ package android.app { method public boolean removeAutomaticZenRule(java.lang.String); method public final void setInterruptionFilter(int); method public void setNotificationPolicy(android.app.NotificationManager.Policy); - method public android.content.ComponentName startServiceInForeground(android.content.Intent, int, android.app.Notification); + method public deprecated android.content.ComponentName startServiceInForeground(android.content.Intent, int, android.app.Notification); method public boolean updateAutomaticZenRule(java.lang.String, android.app.AutomaticZenRule); field public static final java.lang.String ACTION_INTERRUPTION_FILTER_CHANGED = "android.app.action.INTERRUPTION_FILTER_CHANGED"; field public static final java.lang.String ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED = "android.app.action.NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED"; @@ -5654,6 +5654,7 @@ package android.app { method public java.lang.String getCreatorPackage(); method public int getCreatorUid(); method public android.os.UserHandle getCreatorUserHandle(); + method public static android.app.PendingIntent getForegroundService(android.content.Context, int, android.content.Intent, int); method public android.content.IntentSender getIntentSender(); method public static android.app.PendingIntent getService(android.content.Context, int, android.content.Intent, int); method public deprecated java.lang.String getTargetPackage(); @@ -8890,6 +8891,7 @@ package android.content { method public abstract void startActivities(android.content.Intent[], android.os.Bundle); method public abstract void startActivity(android.content.Intent); method public abstract void startActivity(android.content.Intent, android.os.Bundle); + method public abstract android.content.ComponentName startForegroundService(android.content.Intent); method public abstract boolean startInstrumentation(android.content.ComponentName, java.lang.String, android.os.Bundle); method public abstract void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int) throws android.content.IntentSender.SendIntentException; method public abstract void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException; @@ -9082,6 +9084,7 @@ package android.content { method public void startActivities(android.content.Intent[], android.os.Bundle); method public void startActivity(android.content.Intent); method public void startActivity(android.content.Intent, android.os.Bundle); + method public android.content.ComponentName startForegroundService(android.content.Intent); method public boolean startInstrumentation(android.content.ComponentName, java.lang.String, android.os.Bundle); method public void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int) throws android.content.IntentSender.SendIntentException; method public void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException; @@ -40883,6 +40886,7 @@ package android.test.mock { method public void startActivities(android.content.Intent[], android.os.Bundle); method public void startActivity(android.content.Intent); method public void startActivity(android.content.Intent, android.os.Bundle); + method public android.content.ComponentName startForegroundService(android.content.Intent); method public boolean startInstrumentation(android.content.ComponentName, java.lang.String, android.os.Bundle); method public void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int) throws android.content.IntentSender.SendIntentException; method public void startIntentSender(android.content.IntentSender, android.content.Intent, int, int, int, android.os.Bundle) throws android.content.IntentSender.SendIntentException; diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 9f2f669b64c8f..4004bd6686b1f 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -405,6 +405,13 @@ public class ActivityManager { */ public static final int INTENT_SENDER_SERVICE = 4; + /** + * Type for IActivityManaqer.getIntentSender: this PendingIntent is + * for a startForegroundService operation. + * @hide + */ + public static final int INTENT_SENDER_FOREGROUND_SERVICE = 5; + /** @hide User operation call: success! */ public static final int USER_OP_SUCCESS = 0; diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 4c080c9e6a953..467ba996ad4f9 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -1447,14 +1447,21 @@ class ContextImpl extends Context { @Override public ComponentName startService(Intent service) { warnIfCallingFromSystemProcess(); - return startServiceCommon(service, -1, null, mUser); + return startServiceCommon(service, -1, null, false, mUser); } + @Override + public ComponentName startForegroundService(Intent service) { + warnIfCallingFromSystemProcess(); + return startServiceCommon(service, -1, null, true, mUser); + } + + // STOPSHIP: remove when NotificationManager.startServiceInForeground() is retired @Override public ComponentName startServiceInForeground(Intent service, int id, Notification notification) { warnIfCallingFromSystemProcess(); - return startServiceCommon(service, id, notification, mUser); + return startServiceCommon(service, id, notification, false, mUser); } @Override @@ -1465,24 +1472,30 @@ class ContextImpl extends Context { @Override public ComponentName startServiceAsUser(Intent service, UserHandle user) { - return startServiceCommon(service, -1, null, user); + return startServiceCommon(service, -1, null, false, user); } + @Override + public ComponentName startForegroundServiceAsUser(Intent service, UserHandle user) { + return startServiceCommon(service, -1, null, true, user); + } + + // STOPSHIP: remove when NotificationManager.startServiceInForeground() is retired @Override public ComponentName startServiceInForegroundAsUser(Intent service, int id, Notification notification, UserHandle user) { - return startServiceCommon(service, id, notification, user); + return startServiceCommon(service, id, notification, false, user); } private ComponentName startServiceCommon(Intent service, int id, Notification notification, - UserHandle user) { + boolean requireForeground, UserHandle user) { try { validateServiceIntent(service); service.prepareToLeaveProcess(this); ComponentName cn = ActivityManager.getService().startService( mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded( - getContentResolver()), id, notification, getOpPackageName(), - user.getIdentifier()); + getContentResolver()), id, notification, requireForeground, + getOpPackageName(), user.getIdentifier()); if (cn != null) { if (cn.getPackageName().equals("!")) { throw new SecurityException( diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index b9d1d91fabdaf..0a5e4bef8dca0 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -130,7 +130,7 @@ interface IActivityManager { PendingIntent getRunningServiceControlPanel(in ComponentName service); ComponentName startService(in IApplicationThread caller, in Intent service, in String resolvedType, int id, in Notification notification, - in String callingPackage, int userId); + boolean requireForeground, in String callingPackage, int userId); int stopService(in IApplicationThread caller, in Intent service, in String resolvedType, int userId); int bindService(in IApplicationThread caller, in IBinder token, in Intent service, diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index 75998f2eb36d3..72c59781ae801 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -1171,8 +1171,11 @@ public class NotificationManager * @return If the service is being started or is already running, the * {@link ComponentName} of the actual service that was started is * returned; else if the service does not exist null is returned. + * + * @deprecated STOPSHIP transition away from this for O */ @Nullable + @Deprecated public ComponentName startServiceInForeground(Intent service, int id, Notification notification) { return mContext.startServiceInForeground(service, id, notification); diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java index 7d1a16ab1045b..dc432afb5c06d 100644 --- a/core/java/android/app/PendingIntent.java +++ b/core/java/android/app/PendingIntent.java @@ -596,6 +596,42 @@ public final class PendingIntent implements Parcelable { */ public static PendingIntent getService(Context context, int requestCode, @NonNull Intent intent, @Flags int flags) { + return buildServicePendingIntent(context, requestCode, intent, flags, + ActivityManager.INTENT_SENDER_SERVICE); + } + + /** + * Retrieve a PendingIntent that will start a foreground service, like calling + * {@link Context#startService Context.startForegroundService()}. The start + * arguments given to the service will come from the extras of the Intent. + * + *
For security reasons, the {@link android.content.Intent} + * you supply here should almost always be an explicit intent, + * that is specify an explicit component to be delivered to through + * {@link Intent#setClass(android.content.Context, Class) Intent.setClass}
+ * + * @param context The Context in which this PendingIntent should start + * the service. + * @param requestCode Private request code for the sender + * @param intent An Intent describing the service to be started. + * @param flags May be {@link #FLAG_ONE_SHOT}, {@link #FLAG_NO_CREATE}, + * {@link #FLAG_CANCEL_CURRENT}, {@link #FLAG_UPDATE_CURRENT}, + * {@link #FLAG_IMMUTABLE} or any of the flags as supported by + * {@link Intent#fillIn Intent.fillIn()} to control which unspecified parts + * of the intent that can be supplied when the actual send happens. + * + * @return Returns an existing or new PendingIntent matching the given + * parameters. May return null only if {@link #FLAG_NO_CREATE} has been + * supplied. + */ + public static PendingIntent getForegroundService(Context context, int requestCode, + @NonNull Intent intent, @Flags int flags) { + return buildServicePendingIntent(context, requestCode, intent, flags, + ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE); + } + + private static PendingIntent buildServicePendingIntent(Context context, int requestCode, + Intent intent, int flags, int serviceKind) { String packageName = context.getPackageName(); String resolvedType = intent != null ? intent.resolveTypeIfNeeded( context.getContentResolver()) : null; @@ -603,7 +639,7 @@ public final class PendingIntent implements Parcelable { intent.prepareToLeaveProcess(context); IIntentSender target = ActivityManager.getService().getIntentSender( - ActivityManager.INTENT_SENDER_SERVICE, packageName, + serviceKind, packageName, null, null, requestCode, new Intent[] { intent }, resolvedType != null ? new String[] { resolvedType } : null, flags, null, UserHandle.myUserId()); diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 3a8a4206e105b..1803bbe246104 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -2576,7 +2576,7 @@ public abstract class Context { * {@link ComponentName} of the actual service that was started is * returned; else if the service does not exist null is returned. * - * @throws SecurityException If the caller does not permission to access the service + * @throws SecurityException If the caller does not have permission to access the service * or the service can not be found. * @throws IllegalStateException If the application is in a state where the service * can not be started (such as not in the foreground in a state when services are allowed). @@ -2587,12 +2587,48 @@ public abstract class Context { @Nullable public abstract ComponentName startService(Intent service); + /** + * Similar to {@link #startService(Intent)}, but with an implicit promise that the + * Service will call {@link android.app.Service#startForeground(int, Notification) + * startForeground(int, Notification)} once it begins running. The service is given + * an amount of time comparable to the ANR interval to do this, otherwise the system + * will automatically stop the service and declare the app ANR. + * + *Unlike the ordinary {@link #startService(Intent)}, this method can be used + * at any time, regardless of whether the app hosting the service is in a foreground + * state. + * + * @param service Identifies the service to be started. The Intent must be + * fully explicit (supplying a component name). Additional values + * may be included in the Intent extras to supply arguments along with + * this specific start call. + * + * @return If the service is being started or is already running, the + * {@link ComponentName} of the actual service that was started is + * returned; else if the service does not exist null is returned. + * + * @throws SecurityException If the caller does not have permission to access the service + * or the service can not be found. + * + * @see #stopService + * @see android.app.Service#startForeground(int, Notification) + */ + @Nullable + public abstract ComponentName startForegroundService(Intent service); + + /** + * @hide like {@link #startForegroundService(Intent)} but for a specific user. + */ + @Nullable + public abstract ComponentName startForegroundServiceAsUser(Intent service, UserHandle user); + /** * Start a service directly into the "foreground service" state. Unlike {@link #startService}, * this method can be used from within background operations like broadcast receivers * or scheduled jobs. The API entry point for this is in NotificationManager in order to * preserve appropriate public package layering. * @hide + * @deprecated STOPSHIP remove in favor of two-step startForegroundService() + startForeground() */ @Nullable public abstract ComponentName startServiceInForeground(Intent service, @@ -2620,7 +2656,7 @@ public abstract class Context { * @return If there is a service matching the given Intent that is already * running, then it is stopped and {@code true} is returned; else {@code false} is returned. * - * @throws SecurityException If the caller does not permission to access the service + * @throws SecurityException If the caller does not have permission to access the service * or the service can not be found. * @throws IllegalStateException If the application is in a state where the service * can not be started (such as not in the foreground in a state when services are allowed). @@ -2638,7 +2674,9 @@ public abstract class Context { /** * @hide like {@link #startServiceInForeground(Intent, int, Notification)} * but for a specific user. + * @deprecated STOPSHIP remove when trial API is turned off */ + @Deprecated @Nullable public abstract ComponentName startServiceInForegroundAsUser(Intent service, int id, Notification notification, UserHandle user); @@ -2685,7 +2723,7 @@ public abstract class Context { * {@code false} is returned if the connection is not made so you will not * receive the service object. * - * @throws SecurityException If the caller does not permission to access the service + * @throws SecurityException If the caller does not have permission to access the service * or the service can not be found. * * @see #unbindService diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java index 6b0bbfaedc3eb..75784a69c74c6 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -644,7 +644,12 @@ public class ContextWrapper extends Context { return mBase.startService(service); } - /** @hide */ + @Override + public ComponentName startForegroundService(Intent service) { + return mBase.startForegroundService(service); + } + + /** @hide STOPSHIP remove when trial API is turned down */ @Override public ComponentName startServiceInForeground(Intent service, int id, Notification notification) { @@ -664,6 +669,12 @@ public class ContextWrapper extends Context { /** @hide */ @Override + public ComponentName startForegroundServiceAsUser(Intent service, UserHandle user) { + return mBase.startForegroundServiceAsUser(service, user); + } + + /** @hide STOPSHIP removed when trial API is turned down */ + @Override public ComponentName startServiceInForegroundAsUser(Intent service, int id, Notification notification, UserHandle user) { return mBase.startServiceInForegroundAsUser(service, id, notification, user); diff --git a/core/java/android/view/ContextThemeWrapper.java b/core/java/android/view/ContextThemeWrapper.java index 86318e91b8854..d3cc175d3e1c4 100644 --- a/core/java/android/view/ContextThemeWrapper.java +++ b/core/java/android/view/ContextThemeWrapper.java @@ -36,8 +36,8 @@ public class ContextThemeWrapper extends ContextWrapper { /** * Creates a new context wrapper with no theme and no base context. - *
- *
+ * Note: A base context must be attached
* using {@link #attachBaseContext(Context)} before calling any other
* method on the newly constructed context wrapper.
*/
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index b4f8f61971f89..8b0665c6d3121 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -17,7 +17,6 @@
package com.android.server.am;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
import static com.android.server.am.ActivityManagerDebugConfig.*;
import java.io.FileDescriptor;
@@ -96,6 +95,10 @@ public final class ActiveServices {
// How long we wait for a service to finish executing.
static final int SERVICE_BACKGROUND_TIMEOUT = SERVICE_TIMEOUT * 10;
+ // How long the startForegroundService() grace period is to get around to
+ // calling startForeground() before we ANR + stop it.
+ static final int SERVICE_START_FOREGROUND_TIMEOUT = 5*1000;
+
// How long a service needs to be running until restarting its process
// is no longer considered to be a relaunch of the service.
static final int SERVICE_RESTART_DURATION = 1*1000;
@@ -307,8 +310,8 @@ public final class ActiveServices {
}
ComponentName startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,
- int id, Notification notification,
- int callingPid, int callingUid, String callingPackage, final int userId)
+ int id, Notification notification, int callingPid, int callingUid,
+ boolean fgRequired, String callingPackage, final int userId)
throws TransactionTooLargeException {
if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "startService: " + service
+ " type=" + resolvedType + " args=" + service.getExtras());
@@ -345,8 +348,9 @@ public final class ActiveServices {
return null;
}
- // Non-null notification means this is a start directly into the foreground
- if (!r.startRequested && notification == null) {
+ // If this isn't a direct-to-foreground start, check our ability to kick off an
+ // arbitrary service
+ if (!r.startRequested && !fgRequired) {
final long token = Binder.clearCallingIdentity();
try {
// Before going further -- if this app is not allowed to start services in the
@@ -392,12 +396,13 @@ public final class ActiveServices {
r.lastActivity = SystemClock.uptimeMillis();
r.startRequested = true;
r.delayedStop = false;
+ r.fgRequired = fgRequired;
r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
service, neededGrants));
final ServiceMap smap = getServiceMapLocked(r.userId);
boolean addToStarting = false;
- if (!callerFg && r.app == null
+ if (!callerFg && !fgRequired && r.app == null
&& mAm.mUserController.hasStartedUserState(r.userId)) {
ProcessRecord proc = mAm.getProcessRecordLocked(r.processName, r.appInfo.uid, false);
if (proc == null || proc.curProcState > ActivityManager.PROCESS_STATE_RECEIVER) {
@@ -449,9 +454,9 @@ public final class ActiveServices {
Slog.v(TAG_SERVICE, sb.toString());
}
} else if (DEBUG_DELAYED_STARTS) {
- if (callerFg) {
+ if (callerFg || fgRequired) {
Slog.v(TAG_SERVICE, "Not potential delay (callerFg=" + callerFg + " uid="
- + callingUid + " pid=" + callingPid + "): " + r);
+ + callingUid + " pid=" + callingPid + " fgRequired=" + fgRequired + "): " + r);
} else if (r.app != null) {
Slog.v(TAG_SERVICE, "Not potential delay (cur app=" + r.app + "): " + r);
} else {
@@ -461,6 +466,7 @@ public final class ActiveServices {
}
ComponentName cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting);
+ // STOPSHIP deprecated; remove when NotificationManager.startServiceInForeground is retired
if (notification != null) {
setServiceForegroundInnerLocked(r, id, notification, 0);
}
@@ -540,7 +546,7 @@ public final class ActiveServices {
if (first) {
smap.rescheduleDelayedStartsLocked();
}
- } else if (callerFg) {
+ } else if (callerFg || r.fgRequired) {
smap.ensureNotStartingBackgroundLocked(r);
}
@@ -756,8 +762,17 @@ public final class ActiveServices {
}
}
}
+ if (r.fgRequired) {
+ if (DEBUG_BACKGROUND_CHECK) {
+ Slog.i(TAG, "Service called startForeground() as required: " + r);
+ }
+ r.fgRequired = false;
+ r.fgWaiting = false;
+ mAm.mHandler.removeMessages(
+ ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG, r);
+ }
if (r.foregroundId != id) {
- cancelForegroudNotificationLocked(r);
+ cancelForegroundNotificationLocked(r);
r.foregroundId = id;
}
notification.flags |= Notification.FLAG_FOREGROUND_SERVICE;
@@ -779,7 +794,7 @@ public final class ActiveServices {
}
}
if ((flags & Service.STOP_FOREGROUND_REMOVE) != 0) {
- cancelForegroudNotificationLocked(r);
+ cancelForegroundNotificationLocked(r);
r.foregroundId = 0;
r.foregroundNoti = null;
} else if (r.appInfo.targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {
@@ -792,7 +807,7 @@ public final class ActiveServices {
}
}
- private void cancelForegroudNotificationLocked(ServiceRecord r) {
+ private void cancelForegroundNotificationLocked(ServiceRecord r) {
if (r.foregroundId != 0) {
// First check to see if this app has any other active foreground services
// with the same notification ID. If so, we shouldn't actually cancel it,
@@ -1631,7 +1646,7 @@ public final class ActiveServices {
r.makeRestarting(mAm.mProcessStats.getMemFactorLocked(), now);
}
- cancelForegroudNotificationLocked(r);
+ cancelForegroundNotificationLocked(r);
mAm.mHandler.removeCallbacks(r.restarter);
mAm.mHandler.postAtTime(r.restarter, r.nextRestartTime);
@@ -1718,7 +1733,9 @@ public final class ActiveServices {
return null;
}
- if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Bringing up " + r + " " + r.intent);
+ if (DEBUG_SERVICE) {
+ Slog.v(TAG_SERVICE, "Bringing up " + r + " " + r.intent + " fg=" + r.fgRequired);
+ }
// We are now bringing the service up, so no longer in the
// restarting state.
@@ -1944,8 +1961,10 @@ public final class ActiveServices {
ServiceRecord.StartItem si = null;
try {
si = r.pendingStarts.remove(0);
- if (DEBUG_SERVICE) Slog.v(TAG_SERVICE, "Sending arguments to: "
- + r + " " + r.intent + " args=" + si.intent);
+ if (DEBUG_SERVICE) {
+ Slog.v(TAG_SERVICE, "Sending arguments to: "
+ + r + " " + r.intent + " args=" + si.intent);
+ }
if (si.intent == null && N > 1) {
// If somehow we got a dummy null intent in the middle,
// then skip it. DO NOT skip a null intent when it is
@@ -1966,6 +1985,19 @@ public final class ActiveServices {
oomAdjusted = true;
mAm.updateOomAdjLocked(r.app);
}
+ if (r.fgRequired && !r.fgWaiting) {
+ if (!r.isForeground) {
+ if (DEBUG_BACKGROUND_CHECK) {
+ Slog.i(TAG, "Launched service must call startForeground() within timeout: " + r);
+ }
+ scheduleServiceForegroundTransitionTimeoutLocked(r);
+ } else {
+ if (DEBUG_BACKGROUND_CHECK) {
+ Slog.i(TAG, "Service already foreground; no new timeout: " + r);
+ }
+ r.fgRequired = false;
+ }
+ }
int flags = 0;
if (si.deliveryCount > 1) {
flags |= Service.START_FLAG_RETRY;
@@ -2101,7 +2133,7 @@ public final class ActiveServices {
}
}
- cancelForegroudNotificationLocked(r);
+ cancelForegroundNotificationLocked(r);
r.isForeground = false;
r.foregroundId = 0;
r.foregroundNoti = null;
@@ -2925,23 +2957,53 @@ public final class ActiveServices {
}
}
+ void serviceForegroundTimeout(ServiceRecord r) {
+ ProcessRecord app;
+ synchronized (mAm) {
+ if (!r.fgRequired) {
+ return;
+ }
+
+ if (DEBUG_BACKGROUND_CHECK) {
+ Slog.i(TAG, "Service foreground-required timeout for " + r);
+ }
+ app = r.app;
+ r.fgWaiting = false;
+ stopServiceLocked(r);
+ }
+
+ if (app != null) {
+ mAm.mAppErrors.appNotResponding(app, null, null, false,
+ "Context.startForegroundService() did not then call Service.startForeground()");
+ }
+ }
+
void scheduleServiceTimeoutLocked(ProcessRecord proc) {
if (proc.executingServices.size() == 0 || proc.thread == null) {
return;
}
- long now = SystemClock.uptimeMillis();
Message msg = mAm.mHandler.obtainMessage(
ActivityManagerService.SERVICE_TIMEOUT_MSG);
msg.obj = proc;
- mAm.mHandler.sendMessageAtTime(msg,
- proc.execServicesFg ? (now+SERVICE_TIMEOUT) : (now+ SERVICE_BACKGROUND_TIMEOUT));
+ mAm.mHandler.sendMessageDelayed(msg,
+ proc.execServicesFg ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
+ }
+
+ void scheduleServiceForegroundTransitionTimeoutLocked(ServiceRecord r) {
+ if (r.app.executingServices.size() == 0 || r.app.thread == null) {
+ return;
+ }
+ Message msg = mAm.mHandler.obtainMessage(
+ ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG);
+ msg.obj = r;
+ r.fgWaiting = true;
+ mAm.mHandler.sendMessageDelayed(msg, SERVICE_START_FOREGROUND_TIMEOUT);
}
final class ServiceDumper {
private final FileDescriptor fd;
private final PrintWriter pw;
private final String[] args;
- private final int opti;
private final boolean dumpAll;
private final String dumpPackage;
private final ItemMatcher matcher;
@@ -2962,7 +3024,6 @@ public final class ActiveServices {
this.fd = fd;
this.pw = pw;
this.args = args;
- this.opti = opti;
this.dumpAll = dumpAll;
this.dumpPackage = dumpPackage;
matcher = new ItemMatcher();
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index f602e43fe1e85..b4da152a18ae1 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1726,6 +1726,7 @@ public class ActivityManagerService extends IActivityManager.Stub
static final int HANDLE_TRUST_STORAGE_UPDATE_MSG = 63;
static final int REPORT_LOCKED_BOOT_COMPLETE_MSG = 64;
static final int NOTIFY_VR_SLEEPING_MSG = 65;
+ static final int SERVICE_FOREGROUND_TIMEOUT_MSG = 66;
static final int START_USER_SWITCH_FG_MSG = 712;
static final int FIRST_ACTIVITY_STACK_MSG = 100;
@@ -1991,6 +1992,9 @@ public class ActivityManagerService extends IActivityManager.Stub
}
mServices.serviceTimeout((ProcessRecord)msg.obj);
} break;
+ case SERVICE_FOREGROUND_TIMEOUT_MSG: {
+ mServices.serviceForegroundTimeout((ServiceRecord)msg.obj);
+ } break;
case UPDATE_TIME_ZONE: {
synchronized (ActivityManagerService.this) {
for (int i = mLruProcesses.size() - 1 ; i >= 0 ; i--) {
@@ -17898,7 +17902,7 @@ public class ActivityManagerService extends IActivityManager.Stub
@Override
public ComponentName startService(IApplicationThread caller, Intent service,
- String resolvedType, int id, Notification notification,
+ String resolvedType, int id, Notification notification, boolean requireForeground,
String callingPackage, int userId)
throws TransactionTooLargeException {
enforceNotIsolatedCaller("startService");
@@ -17912,28 +17916,28 @@ public class ActivityManagerService extends IActivityManager.Stub
}
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE,
- "startService: " + service + " type=" + resolvedType);
+ "*** startService: " + service + " type=" + resolvedType + " fg=" + requireForeground);
synchronized(this) {
final int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
ComponentName res = mServices.startServiceLocked(caller, service,
- resolvedType, id, notification,
- callingPid, callingUid, callingPackage, userId);
+ resolvedType, id, notification, callingPid, callingUid,
+ requireForeground, callingPackage, userId);
Binder.restoreCallingIdentity(origId);
return res;
}
}
ComponentName startServiceInPackage(int uid, Intent service, String resolvedType,
- String callingPackage, int userId)
+ boolean fgRequired, String callingPackage, int userId)
throws TransactionTooLargeException {
synchronized(this) {
if (DEBUG_SERVICE) Slog.v(TAG_SERVICE,
"startServiceInPackage: " + service + " type=" + resolvedType);
final long origId = Binder.clearCallingIdentity();
ComponentName res = mServices.startServiceLocked(null, service,
- resolvedType, 0, null, -1, uid, callingPackage, userId);
+ resolvedType, 0, null, -1, uid, fgRequired, callingPackage, userId);
Binder.restoreCallingIdentity(origId);
return res;
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 9b6d13a5d8a0e..a9bd872279293 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -519,7 +519,7 @@ final class ActivityManagerShellCommand extends ShellCommand {
pw.println("Starting service: " + intent);
pw.flush();
ComponentName cn = mInterface.startService(null, intent, intent.getType(),
- -1, null, SHELL_PACKAGE_NAME, mUserId);
+ -1, null, false, SHELL_PACKAGE_NAME, mUserId);
if (cn == null) {
err.println("Error: Not found; no service started.");
return -1;
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index c494171fbfbbd..f05bfb6174d3f 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -175,6 +175,8 @@ final class PendingIntentRecord extends IIntentSender.Stub {
return "broadcastIntent";
case ActivityManager.INTENT_SENDER_SERVICE:
return "startService";
+ case ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE:
+ return "startForegroundService";
case ActivityManager.INTENT_SENDER_ACTIVITY_RESULT:
return "activityResult";
}
@@ -318,9 +320,11 @@ final class PendingIntentRecord extends IIntentSender.Stub {
}
break;
case ActivityManager.INTENT_SENDER_SERVICE:
+ case ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE:
try {
- owner.startServiceInPackage(uid, finalIntent,
- resolvedType, key.packageName, userId);
+ owner.startServiceInPackage(uid, finalIntent, resolvedType,
+ key.type == ActivityManager.INTENT_SENDER_FOREGROUND_SERVICE,
+ key.packageName, userId);
} catch (RuntimeException e) {
Slog.w(TAG, "Unable to send startService intent", e);
} catch (TransactionTooLargeException e) {
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index dfbe59f1c8f22..44ebf50eb31a9 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -92,6 +92,8 @@ final class ServiceRecord extends Binder {
ServiceState restartTracker; // tracking service restart
boolean whitelistManager; // any bindings to this service have BIND_ALLOW_WHITELIST_MANAGEMENT?
boolean delayed; // are we waiting to start this service in the background?
+ boolean fgRequired; // is the service required to go foreground after starting?
+ boolean fgWaiting; // is a timeout for going foreground already scheduled?
boolean isForeground; // is service currently in foreground mode?
int foregroundId; // Notification ID of last foreground req.
Notification foregroundNoti; // Notification record of foreground state.
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 899847f177ebd..a8e096c59e1bc 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -12968,7 +12968,7 @@ public class PackageManagerService extends IPackageManager.Stub {
IActivityManager am = ActivityManager.getService();
if (am != null) {
try {
- am.startService(null, intent, null, -1, null, mContext.getOpPackageName(),
+ am.startService(null, intent, null, -1, null, false, mContext.getOpPackageName(),
UserHandle.USER_SYSTEM);
} catch (RemoteException e) {
}
diff --git a/test-runner/src/android/test/mock/MockContext.java b/test-runner/src/android/test/mock/MockContext.java
index da1d9984eea57..dca74ffdcc8c1 100644
--- a/test-runner/src/android/test/mock/MockContext.java
+++ b/test-runner/src/android/test/mock/MockContext.java
@@ -525,6 +525,12 @@ public class MockContext extends Context {
throw new UnsupportedOperationException();
}
+ @Override
+ public ComponentName startForegroundService(Intent service) {
+ throw new UnsupportedOperationException();
+ }
+
+ /** STOPSHIP remove when trial API is turned down */
@Override
public ComponentName startServiceInForeground(Intent service,
int id, Notification notification) {
@@ -544,6 +550,12 @@ public class MockContext extends Context {
/** @hide */
@Override
+ public ComponentName startForegroundServiceAsUser(Intent service, UserHandle user) {
+ throw new UnsupportedOperationException();
+ }
+
+ /** @hide STOPSHIP removed when trial API is turned down */
+ @Override
public ComponentName startServiceInForegroundAsUser(Intent service,
int id, Notification notification, UserHandle user) {
throw new UnsupportedOperationException();
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index 5c281507b4612..06272c80f99e1 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -1852,6 +1852,18 @@ public class BridgeContext extends Context {
return null;
}
+ @Override
+ public ComponentName startForegroundService(Intent service) {
+ // pass
+ return null;
+ }
+
+ @Override
+ public ComponentName startForegroundServiceAsUser(Intent service, UserHandle user) {
+ // pass
+ return null;
+ }
+
@Override
public ComponentName startServiceInForeground(Intent service,
int id, Notification notification) {