From 0549b1f782f0aa63bf5ef6c26fbf83f297c429a5 Mon Sep 17 00:00:00 2001 From: Makoto Onuki Date: Wed, 17 Feb 2021 11:43:50 -0800 Subject: [PATCH] Throw more descriptive exceptions when service start isn't allowed. Fix: 180518252 Test: atest cts/tests/app/src/android/app/cts/ActivityManagerFgsBgStartTest.java Change-Id: Ia38a159f9ededdc9885ce6b8dc61640c3af5ef72 --- core/api/current.txt | 17 +++++ ...groundServiceStartNotAllowedException.java | 62 +++++++++++++++++++ core/java/android/app/ContextImpl.java | 2 +- ...groundServiceStartNotAllowedException.java | 62 +++++++++++++++++++ core/java/android/app/Service.java | 9 ++- .../app/ServiceStartNotAllowedException.java | 43 +++++++++++++ core/java/android/content/Context.java | 17 ++++- .../com/android/server/am/ActiveServices.java | 5 +- 8 files changed, 210 insertions(+), 7 deletions(-) create mode 100644 core/java/android/app/BackgroundServiceStartNotAllowedException.java create mode 100644 core/java/android/app/ForegroundServiceStartNotAllowedException.java create mode 100644 core/java/android/app/ServiceStartNotAllowedException.java diff --git a/core/api/current.txt b/core/api/current.txt index 53be53c62786a..2cfd2382a5baa 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -4704,6 +4704,13 @@ package android.app { field @NonNull public static final android.os.Parcelable.Creator CREATOR; } + public final class BackgroundServiceStartNotAllowedException extends android.app.ServiceStartNotAllowedException implements android.os.Parcelable { + ctor public BackgroundServiceStartNotAllowedException(@NonNull String); + method public int describeContents(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator CREATOR; + } + public class DatePickerDialog extends android.app.AlertDialog implements android.widget.DatePicker.OnDateChangedListener android.content.DialogInterface.OnClickListener { ctor public DatePickerDialog(@NonNull android.content.Context); ctor public DatePickerDialog(@NonNull android.content.Context, @StyleRes int); @@ -4949,6 +4956,13 @@ package android.app { method @Deprecated public void setSelectedGroup(int); } + public final class ForegroundServiceStartNotAllowedException extends android.app.ServiceStartNotAllowedException implements android.os.Parcelable { + ctor public ForegroundServiceStartNotAllowedException(@NonNull String); + method public int describeContents(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator CREATOR; + } + @Deprecated public class Fragment implements android.content.ComponentCallbacks2 android.view.View.OnCreateContextMenuListener { ctor @Deprecated public Fragment(); method @Deprecated public void dump(String, java.io.FileDescriptor, java.io.PrintWriter, String[]); @@ -6524,6 +6538,9 @@ package android.app { field public static final int STOP_FOREGROUND_REMOVE = 1; // 0x1 } + public abstract class ServiceStartNotAllowedException extends java.lang.IllegalStateException { + } + public abstract class SharedElementCallback { ctor public SharedElementCallback(); method public android.os.Parcelable onCaptureSharedElementSnapshot(android.view.View, android.graphics.Matrix, android.graphics.RectF); diff --git a/core/java/android/app/BackgroundServiceStartNotAllowedException.java b/core/java/android/app/BackgroundServiceStartNotAllowedException.java new file mode 100644 index 0000000000000..f6361b52bf9d0 --- /dev/null +++ b/core/java/android/app/BackgroundServiceStartNotAllowedException.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.app; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Exception thrown when an app tries to start a background {@link Service} when it's not allowed to + * do so. + */ +public final class BackgroundServiceStartNotAllowedException + extends ServiceStartNotAllowedException implements Parcelable { + /** + * Constructor. + */ + public BackgroundServiceStartNotAllowedException(@NonNull String message) { + super(message); + } + + BackgroundServiceStartNotAllowedException(@NonNull Parcel source) { + super(source.readString()); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeString(getMessage()); + } + + public static final @NonNull Creator + CREATOR = new Creator() { + @NonNull + public android.app.BackgroundServiceStartNotAllowedException createFromParcel( + Parcel source) { + return new android.app.BackgroundServiceStartNotAllowedException(source); + } + + @NonNull + public android.app.BackgroundServiceStartNotAllowedException[] newArray(int size) { + return new android.app.BackgroundServiceStartNotAllowedException[size]; + } + }; +} diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 9a20e0fefd331..12886016f0c42 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -1794,7 +1794,7 @@ class ContextImpl extends Context { "Unable to start service " + service + ": " + cn.getClassName()); } else if (cn.getPackageName().equals("?")) { - throw new IllegalStateException( + throw ServiceStartNotAllowedException.newInstance(requireForeground, "Not allowed to start service " + service + ": " + cn.getClassName()); } } diff --git a/core/java/android/app/ForegroundServiceStartNotAllowedException.java b/core/java/android/app/ForegroundServiceStartNotAllowedException.java new file mode 100644 index 0000000000000..41eeada2df6b8 --- /dev/null +++ b/core/java/android/app/ForegroundServiceStartNotAllowedException.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.app; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Exception thrown when an app tries to start a foreground {@link Service} when it's not allowed to + * do so. + */ +public final class ForegroundServiceStartNotAllowedException + extends ServiceStartNotAllowedException implements Parcelable { + /** + * Constructor. + */ + public ForegroundServiceStartNotAllowedException(@NonNull String message) { + super(message); + } + + ForegroundServiceStartNotAllowedException(@NonNull Parcel source) { + super(source.readString()); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeString(getMessage()); + } + + public static final @NonNull Creator + CREATOR = new Creator() { + @NonNull + public android.app.ForegroundServiceStartNotAllowedException createFromParcel( + Parcel source) { + return new android.app.ForegroundServiceStartNotAllowedException(source); + } + + @NonNull + public android.app.ForegroundServiceStartNotAllowedException[] newArray(int size) { + return new android.app.ForegroundServiceStartNotAllowedException[size]; + } + }; +} diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java index 3798de921dc71..2ceea7f1a6a8d 100644 --- a/core/java/android/app/Service.java +++ b/core/java/android/app/Service.java @@ -697,7 +697,8 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac * service element of manifest file. The value of attribute * {@link android.R.attr#foregroundServiceType} can be multiple flags ORed together.

* - * @throws IllegalStateException If the app targeting API is + * @throws ForegroundServiceStartNotAllowedException + * If the app targeting API is * {@link android.os.Build.VERSION_CODES#S} or later, and the service is restricted from * becoming foreground service due to background restriction. * @@ -738,8 +739,14 @@ public abstract class Service extends ContextWrapper implements ComponentCallbac * @param notification The Notification to be displayed. * @param foregroundServiceType must be a subset flags of manifest attribute * {@link android.R.attr#foregroundServiceType} flags. + * * @throws IllegalArgumentException if param foregroundServiceType is not subset of manifest * attribute {@link android.R.attr#foregroundServiceType}. + * @throws ForegroundServiceStartNotAllowedException + * If the app targeting API is + * {@link android.os.Build.VERSION_CODES#S} or later, and the service is restricted from + * becoming foreground service due to background restriction. + * * @see android.content.pm.ServiceInfo#FOREGROUND_SERVICE_TYPE_MANIFEST */ public final void startForeground(int id, @NonNull Notification notification, diff --git a/core/java/android/app/ServiceStartNotAllowedException.java b/core/java/android/app/ServiceStartNotAllowedException.java new file mode 100644 index 0000000000000..33285b2190eb5 --- /dev/null +++ b/core/java/android/app/ServiceStartNotAllowedException.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app; + +import android.annotation.NonNull; + +/** + * Exception thrown when an app tries to start a {@link Service} when it's not allowed to do so. + */ +public abstract class ServiceStartNotAllowedException extends IllegalStateException { + ServiceStartNotAllowedException(@NonNull String message) { + super(message); + } + + /** + * Return either {@link ForegroundServiceStartNotAllowedException} or + * {@link BackgroundServiceStartNotAllowedException} + * @hide + */ + @NonNull + public static ServiceStartNotAllowedException newInstance(boolean foreground, + @NonNull String message) { + if (foreground) { + return new ForegroundServiceStartNotAllowedException(message); + } else { + return new BackgroundServiceStartNotAllowedException(message); + } + } +} diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 2a402b204cb7b..0a6fcd194aa01 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -3117,8 +3117,18 @@ public abstract class Context { * * @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). + * @throws IllegalStateException + * Before Android {@link android.os.Build.VERSION_CODES#S}, + * 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), + * {@link IllegalStateException} was thrown. + * @throws android.app.BackgroundServiceStartNotAllowedException + * On Android {@link android.os.Build.VERSION_CODES#S} and later, + * 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), + * {@link android.app.BackgroundServiceStartNotAllowedException} is thrown + * This excemption extends {@link IllegalStateException}, so apps can + * use {@code catch (IllegalStateException)} to catch both. * * @see #stopService * @see #bindService @@ -3149,7 +3159,8 @@ public abstract class Context { * @throws SecurityException If the caller does not have permission to access the service * or the service can not be found. * - * @throws IllegalStateException If the caller app's targeting API is + * @throws android.app.ForegroundServiceStartNotAllowedException + * If the caller app's targeting API is * {@link android.os.Build.VERSION_CODES#S} or later, and the foreground service is restricted * from start due to background restriction. * diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index a4ff230b06788..04f203f4b9b49 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -52,6 +52,7 @@ import android.app.ActivityThread; import android.app.AppGlobals; import android.app.AppOpsManager; import android.app.BroadcastOptions; +import android.app.ForegroundServiceStartNotAllowedException; import android.app.IApplicationThread; import android.app.IServiceConnection; import android.app.Notification; @@ -693,7 +694,7 @@ public final class ActiveServices { + "could not resolve client package " + callingPackage); } if (CompatChanges.isChangeEnabled(FGS_START_EXCEPTION_CHANGE_ID, aInfo.uid)) { - throw new IllegalStateException(msg); + throw new ForegroundServiceStartNotAllowedException(msg); } return null; } @@ -1778,7 +1779,7 @@ public final class ActiveServices { ignoreForeground = true; if (CompatChanges.isChangeEnabled(FGS_START_EXCEPTION_CHANGE_ID, r.appInfo.uid)) { - throw new IllegalStateException(msg); + throw new ForegroundServiceStartNotAllowedException(msg); } } }