Add ExplicitHealthCheckService API
The ExplicitHealthCheckService implementation will live in ExtServices
so it can be updated outside of a platform update. It will contain
logic to assist the PackageWatchdog with observing packages, e.g
ensuring that the device can successfully connect to WiFi after a
mainline update.
Test: Builds
Bug: 120598832
Change-Id: If658567ca41bb0328e279735897fc50ab76abc11
diff --git a/Android.bp b/Android.bp
index f4e8b63..6388c81 100644
--- a/Android.bp
+++ b/Android.bp
@@ -318,6 +318,7 @@
"core/java/android/service/vr/IVrListener.aidl",
"core/java/android/service/vr/IVrManager.aidl",
"core/java/android/service/vr/IVrStateCallbacks.aidl",
+ "core/java/android/service/watchdog/IExplicitHealthCheckService.aidl",
"core/java/android/print/ILayoutResultCallback.aidl",
"core/java/android/print/IPrinterDiscoveryObserver.aidl",
"core/java/android/print/IPrintDocumentAdapter.aidl",
diff --git a/api/system-current.txt b/api/system-current.txt
index 42b8a5d..13a25ee 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -6891,6 +6891,22 @@
}
+package android.service.watchdog {
+
+ public abstract class ExplicitHealthCheckService extends android.app.Service {
+ ctor public ExplicitHealthCheckService();
+ method public final void notifyHealthCheckPassed(@NonNull String);
+ method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
+ method public abstract void onCancelHealthCheck(@NonNull String);
+ method @NonNull public abstract java.util.List<java.lang.String> onGetRequestedPackages();
+ method @NonNull public abstract java.util.List<java.lang.String> onGetSupportedPackages();
+ method public abstract void onRequestHealthCheck(@NonNull String);
+ field public static final String BIND_PERMISSION = "android.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE";
+ field public static final String SERVICE_INTERFACE = "android.service.watchdog.ExplicitHealthCheckService";
+ }
+
+}
+
package android.telecom {
@Deprecated public class AudioState implements android.os.Parcelable {
diff --git a/core/java/android/service/watchdog/ExplicitHealthCheckService.java b/core/java/android/service/watchdog/ExplicitHealthCheckService.java
new file mode 100644
index 0000000..015fba1
--- /dev/null
+++ b/core/java/android/service/watchdog/ExplicitHealthCheckService.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2019 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.service.watchdog;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SdkConstant;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteCallback;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * A service to provide packages supporting explicit health checks and route checks to these
+ * packages on behalf of the package watchdog.
+ *
+ * <p>To extend this class, you must declare the service in your manifest file with the
+ * {@link android.Manifest.permission#BIND_EXPLICIT_HEALTH_CHECK_SERVICE} permission,
+ * and include an intent filter with the {@link #SERVICE_INTERFACE} action. In adddition,
+ * your implementation must live in {@link PackageManger#SYSTEM_SHARED_LIBRARY_SERVICES}.
+ * For example:</p>
+ * <pre>
+ * <service android:name=".FooExplicitHealthCheckService"
+ * android:exported="true"
+ * android:priority="100"
+ * android:permission="android.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE">
+ * <intent-filter>
+ * <action android:name="android.service.watchdog.ExplicitHealthCheckService" />
+ * </intent-filter>
+ * </service>
+ * </pre>
+ * @hide
+ */
+@SystemApi
+public abstract class ExplicitHealthCheckService extends Service {
+
+ private static final String TAG = "ExplicitHealthCheckService";
+
+ /**
+ * {@link Bundle} key for a {@link List} of {@link String} value.
+ *
+ * {@hide}
+ */
+ public static final String EXTRA_SUPPORTED_PACKAGES =
+ "android.service.watchdog.extra.supported_packages";
+
+ /**
+ * {@link Bundle} key for a {@link List} of {@link String} value.
+ *
+ * {@hide}
+ */
+ public static final String EXTRA_REQUESTED_PACKAGES =
+ "android.service.watchdog.extra.requested_packages";
+
+ /**
+ * {@link Bundle} key for a {@link String} value.
+ *
+ * {@hide}
+ */
+ public static final String EXTRA_HEALTH_CHECK_PASSED_PACKAGE =
+ "android.service.watchdog.extra.health_check_passed_package";
+
+ /**
+ * The Intent action that a service must respond to. Add it to the intent filter of the service
+ * in its manifest.
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+ public static final String SERVICE_INTERFACE =
+ "android.service.watchdog.ExplicitHealthCheckService";
+
+ /**
+ * The permission that a service must require to ensure that only Android system can bind to it.
+ * If this permission is not enforced in the AndroidManifest of the service, the system will
+ * skip that service.
+ */
+ public static final String BIND_PERMISSION =
+ "android.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE";
+
+ private final ExplicitHealthCheckServiceWrapper mWrapper =
+ new ExplicitHealthCheckServiceWrapper();
+
+ /**
+ * Called when the system requests an explicit health check for {@code packageName}.
+ *
+ * <p> When {@code packageName} passes the check, implementors should call
+ * {@link #notifyHealthCheckPassed} to inform the system.
+ *
+ * <p> It could take many hours before a {@code packageName} passes a check and implementors
+ * should never drop requests unless {@link onCancel} is called or the service dies.
+ *
+ * <p> Requests should not be queued and additional calls while expecting a result for
+ * {@code packageName} should have no effect.
+ */
+ public abstract void onRequestHealthCheck(@NonNull String packageName);
+
+ /**
+ * Called when the system cancels the explicit health check request for {@code packageName}.
+ * Should do nothing if there are is no active request for {@code packageName}.
+ */
+ public abstract void onCancelHealthCheck(@NonNull String packageName);
+
+ /**
+ * Called when the system requests for all the packages supporting explicit health checks. The
+ * system may request an explicit health check for any of these packages with
+ * {@link #onRequestHealthCheck}.
+ *
+ * @return all packages supporting explicit health checks
+ */
+ @NonNull public abstract List<String> onGetSupportedPackages();
+
+ /**
+ * Called when the system requests for all the packages that it has currently requested
+ * an explicit health check for.
+ *
+ * @return all packages expecting an explicit health check result
+ */
+ @NonNull public abstract List<String> onGetRequestedPackages();
+
+ private final Handler mHandler = new Handler(Looper.getMainLooper(), null, true);
+ @Nullable private RemoteCallback mCallback;
+
+ @Override
+ @NonNull
+ public final IBinder onBind(@NonNull Intent intent) {
+ return mWrapper;
+ }
+
+ /**
+ * Implementors should call this to notify the system when explicit health check passes
+ * for {@code packageName};
+ */
+ public final void notifyHealthCheckPassed(@NonNull String packageName) {
+ mHandler.post(() -> {
+ if (mCallback != null) {
+ Objects.requireNonNull(packageName,
+ "Package passing explicit health check must be non-null");
+ Bundle bundle = new Bundle();
+ bundle.putString(EXTRA_HEALTH_CHECK_PASSED_PACKAGE, packageName);
+ mCallback.sendResult(bundle);
+ } else {
+ Log.wtf(TAG, "System missed explicit health check result for " + packageName);
+ }
+ });
+ }
+
+ private class ExplicitHealthCheckServiceWrapper extends IExplicitHealthCheckService.Stub {
+ @Override
+ public void setCallback(RemoteCallback callback) throws RemoteException {
+ mHandler.post(() -> {
+ mCallback = callback;
+ });
+ }
+
+ @Override
+ public void request(String packageName) throws RemoteException {
+ mHandler.post(() -> ExplicitHealthCheckService.this.onRequestHealthCheck(packageName));
+ }
+
+ @Override
+ public void cancel(String packageName) throws RemoteException {
+ mHandler.post(() -> ExplicitHealthCheckService.this.onCancelHealthCheck(packageName));
+ }
+
+ @Override
+ public void getSupportedPackages(RemoteCallback callback) throws RemoteException {
+ mHandler.post(() -> sendPackages(callback, EXTRA_SUPPORTED_PACKAGES,
+ ExplicitHealthCheckService.this.onGetSupportedPackages()));
+ }
+
+ @Override
+ public void getRequestedPackages(RemoteCallback callback) throws RemoteException {
+ mHandler.post(() -> sendPackages(callback, EXTRA_REQUESTED_PACKAGES,
+ ExplicitHealthCheckService.this.onGetRequestedPackages()));
+ }
+
+ private void sendPackages(RemoteCallback callback, String key, List<String> packages) {
+ Objects.requireNonNull(packages,
+ "Supported and requested package list must be non-null");
+ Bundle bundle = new Bundle();
+ bundle.putStringArrayList(key, new ArrayList<>(packages));
+ callback.sendResult(bundle);
+ }
+ }
+}
diff --git a/core/java/android/service/watchdog/IExplicitHealthCheckService.aidl b/core/java/android/service/watchdog/IExplicitHealthCheckService.aidl
new file mode 100644
index 0000000..78c0328
--- /dev/null
+++ b/core/java/android/service/watchdog/IExplicitHealthCheckService.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2019 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.service.watchdog;
+
+import android.os.RemoteCallback;
+
+/**
+ * @hide
+ */
+oneway interface IExplicitHealthCheckService
+{
+ void setCallback(in @nullable RemoteCallback callback);
+ void request(String packageName);
+ void cancel(String packageName);
+ void getSupportedPackages(in RemoteCallback callback);
+ void getRequestedPackages(in RemoteCallback callback);
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index c7417bf..4150bdd 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4418,6 +4418,13 @@
<permission android:name="android.permission.BIND_CARRIER_MESSAGING_CLIENT_SERVICE"
android:protectionLevel="signature" />
+ <!-- Must be required by an {@link android.service.watchdog.ExplicitHealthCheckService} to
+ ensure that only the system can bind to it.
+ @hide This is not a third-party API (intended for OEMs and system apps).
+ -->
+ <permission android:name="android.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE"
+ android:protectionLevel="signature|privileged" />
+
<!-- @hide Permission that allows configuring appops.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.MANAGE_APPOPS"