Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5c84e052 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Move module files to packages/modules/CrashRecovery" into main

parents da096658 40bdf563
Loading
Loading
Loading
Loading
+0 −359
Original line number Diff line number Diff line
/*
 * 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 static android.os.Parcelable.Creator;

import android.annotation.CallbackExecutor;
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SdkConstant;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.app.Service;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.crashrecovery.flags.Flags;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.util.Log;

import com.android.internal.util.Preconditions;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;

/**
 * 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 PackageManager#getServicesSystemSharedLibraryPackageName()}.
 * For example:</p>
 * <pre>
 *     &lt;service android:name=".FooExplicitHealthCheckService"
 *             android:exported="true"
 *             android:priority="100"
 *             android:permission="android.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE"&gt;
 *         &lt;intent-filter&gt;
 *             &lt;action android:name="android.service.watchdog.ExplicitHealthCheckService" /&gt;
 *         &lt;/intent-filter&gt;
 *     &lt;/service&gt;
 * </pre>
 * @hide
 */
@SystemApi
public abstract class ExplicitHealthCheckService extends Service {

    private static final String TAG = "ExplicitHealthCheckService";

    /**
     * {@link Bundle} key for a {@link List} of {@link PackageConfig} 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.
     */
    @FlaggedApi(Flags.FLAG_ENABLE_CRASHRECOVERY)
    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<PackageConfig> 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 = Handler.createAsync(Looper.getMainLooper());
    @Nullable private Consumer<Bundle> mHealthCheckResultCallback;
    @Nullable private Executor mCallbackExecutor;

    @Override
    @NonNull
    public final IBinder onBind(@NonNull Intent intent) {
        return mWrapper;
    }

    /**
     * Sets a callback to be invoked when an explicit health check passes for a package.
     * <p>
     * The callback will receive a {@link Bundle} containing the package name that passed the
     * health check, identified by the key {@link #EXTRA_HEALTH_CHECK_PASSED_PACKAGE}.
     * <p>
     * <b>Note:</b> This API is primarily intended for testing purposes. Calling this outside of a
     * test environment will override the default callback mechanism used to notify the system
     * about health check results. Use with caution in production code.
     *
     * @param executor The executor on which the callback should be invoked. If {@code null}, the
     *                 callback will be executed on the main thread.
     * @param callback A callback that receives a {@link Bundle} containing the package name that
     *                 passed the health check.
     */
    @FlaggedApi(Flags.FLAG_ENABLE_CRASHRECOVERY)
    public final void setHealthCheckPassedCallback(@CallbackExecutor @Nullable Executor executor,
            @Nullable Consumer<Bundle> callback) {
        mCallbackExecutor = executor;
        mHealthCheckResultCallback = callback;
    }

    private void executeCallback(@NonNull String packageName) {
        if (mHealthCheckResultCallback != 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);
            mHealthCheckResultCallback.accept(bundle);
        } else {
            Log.wtf(TAG, "System missed explicit health check result for " + packageName);
        }
    }

    /**
     * Implementors should call this to notify the system when explicit health check passes
     * for {@code packageName};
     */
    public final void notifyHealthCheckPassed(@NonNull String packageName) {
        if (mCallbackExecutor != null) {
            mCallbackExecutor.execute(() -> executeCallback(packageName));
        } else {
            mHandler.post(() -> executeCallback(packageName));
        }
    }

    /**
     * A PackageConfig contains a package supporting explicit health checks and the
     * timeout in {@link System#uptimeMillis} across reboots after which health
     * check requests from clients are failed.
     *
     * @hide
     */
    @SystemApi
    public static final class PackageConfig implements Parcelable {
        private static final long DEFAULT_HEALTH_CHECK_TIMEOUT_MILLIS = TimeUnit.HOURS.toMillis(1);

        private final String mPackageName;
        private final long mHealthCheckTimeoutMillis;

        /**
         * Creates a new instance.
         *
         * @param packageName the package name
         * @param durationMillis the duration in milliseconds, must be greater than or
         * equal to 0. If it is 0, it will use a system default value.
         */
        public PackageConfig(@NonNull String packageName, long healthCheckTimeoutMillis) {
            mPackageName = Preconditions.checkNotNull(packageName);
            if (healthCheckTimeoutMillis == 0) {
                mHealthCheckTimeoutMillis = DEFAULT_HEALTH_CHECK_TIMEOUT_MILLIS;
            } else {
                mHealthCheckTimeoutMillis = Preconditions.checkArgumentNonnegative(
                        healthCheckTimeoutMillis);
            }
        }

        private PackageConfig(Parcel parcel) {
            mPackageName = parcel.readString();
            mHealthCheckTimeoutMillis = parcel.readLong();
        }

        /**
         * Gets the package name.
         *
         * @return the package name
         */
        public @NonNull String getPackageName() {
            return mPackageName;
        }

        /**
         * Gets the timeout in milliseconds to evaluate an explicit health check result after a
         * request.
         *
         * @return the duration in {@link System#uptimeMillis} across reboots
         */
        public long getHealthCheckTimeoutMillis() {
            return mHealthCheckTimeoutMillis;
        }

        @NonNull
        @Override
        public String toString() {
            return "PackageConfig{" + mPackageName + ", " + mHealthCheckTimeoutMillis + "}";
        }

        @Override
        public boolean equals(@Nullable Object other) {
            if (other == this) {
                return true;
            }
            if (!(other instanceof PackageConfig)) {
                return false;
            }

            PackageConfig otherInfo = (PackageConfig) other;
            return Objects.equals(otherInfo.getHealthCheckTimeoutMillis(),
                    mHealthCheckTimeoutMillis)
                    && Objects.equals(otherInfo.getPackageName(), mPackageName);
        }

        @Override
        public int hashCode() {
            return Objects.hash(mPackageName, mHealthCheckTimeoutMillis);
        }

        @Override
        public int describeContents() {
            return 0;
        }

        @Override
        public void writeToParcel(@SuppressLint({"MissingNullability"}) Parcel parcel, int flags) {
            parcel.writeString(mPackageName);
            parcel.writeLong(mHealthCheckTimeoutMillis);
        }

        public static final @NonNull Creator<PackageConfig> CREATOR = new Creator<PackageConfig>() {
                @Override
                public PackageConfig createFromParcel(Parcel source) {
                    return new PackageConfig(source);
                }

                @Override
                public PackageConfig[] newArray(int size) {
                    return new PackageConfig[size];
                }
            };
    }


    private class ExplicitHealthCheckServiceWrapper extends IExplicitHealthCheckService.Stub {
        @Override
        public void setCallback(RemoteCallback callback) throws RemoteException {
            mHandler.post(() -> mHealthCheckResultCallback = callback::sendResult);
        }

        @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(() -> {
                List<PackageConfig> packages =
                        ExplicitHealthCheckService.this.onGetSupportedPackages();
                Objects.requireNonNull(packages, "Supported package list must be non-null");
                Bundle bundle = new Bundle();
                bundle.putParcelableArrayList(EXTRA_SUPPORTED_PACKAGES, new ArrayList<>(packages));
                callback.sendResult(bundle);
            });
        }

        @Override
        public void getRequestedPackages(RemoteCallback callback) throws RemoteException {
            mHandler.post(() -> {
                List<String> packages =
                        ExplicitHealthCheckService.this.onGetRequestedPackages();
                Objects.requireNonNull(packages, "Requested  package list must be non-null");
                Bundle bundle = new Bundle();
                bundle.putStringArrayList(EXTRA_REQUESTED_PACKAGES, new ArrayList<>(packages));
                callback.sendResult(bundle);
            });
        }
    }
}
+0 −32
Original line number Diff line number Diff line
/*
 * 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
 */
@PermissionManuallyEnforced
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);
}
+0 −3
Original line number Diff line number Diff line
narayan@google.com
nandana@google.com
olilan@google.com
+0 −22
Original line number Diff line number Diff line
/*
 * 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;

/**
 * @hide
 */
parcelable PackageConfig;
+0 −447

File deleted.

Preview size limit exceeded, changes collapsed.

Loading