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

Commit bf92d73a authored by Zimuzo Ezeozue's avatar Zimuzo Ezeozue Committed by Android (Google) Code Review
Browse files

Merge "Implement ExplicitHealthCheckService in ExtServices"

parents 0a0a49be e680d60f
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@

    <uses-permission android:name="android.permission.MONITOR_DEFAULT_SMS_PACKAGE" />
    <uses-permission android:name="android.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

    <uses-sdk
        android:targetSdkVersion="28"
@@ -78,6 +79,13 @@
            </intent-filter>
        </service>

        <service android:name=".watchdog.ExplicitHealthCheckServiceImpl"
                 android:permission="android.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE">
            <intent-filter>
                <action android:name="android.service.watchdog.ExplicitHealthCheckService" />
            </intent-filter>
        </service>

        <activity android:name=".notification.CopyCodeActivity"
                  android:exported="false"
                  android:theme="@android:style/Theme.NoDisplay"/>
+104 −0
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.ext.services.watchdog;

import android.content.ComponentName;
import android.content.Intent;
import android.service.watchdog.ExplicitHealthCheckService;
import android.util.Log;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Routes explicit health check requests to the appropriate {@link ExplicitHealthChecker}.
 */
public final class ExplicitHealthCheckServiceImpl extends ExplicitHealthCheckService {
    private static final String TAG = "ExplicitHealthCheckServiceImpl";
    // TODO: Add build dependency on NetworkStack stable AIDL so we can stop hard coding class name
    private static final String NETWORK_STACK_CONNECTOR_CLASS =
            "android.net.INetworkStackConnector";
    // Modified only #onCreate, using concurrent collection to ensure thread visibility
    private final Map<String, ExplicitHealthChecker> mSupportedCheckers = new ConcurrentHashMap<>();

    @Override
    public void onCreate() {
        super.onCreate();
        initHealthCheckers();
    }

    @Override
    public void onRequestHealthCheck(String packageName) {
        ExplicitHealthChecker checker = mSupportedCheckers.get(packageName);
        if (checker != null) {
            checker.request();
        } else {
            Log.w(TAG, "Ignoring request for explicit health check for unsupported package "
                    + packageName);
        }
    }

    @Override
    public void onCancelHealthCheck(String packageName) {
        ExplicitHealthChecker checker = mSupportedCheckers.get(packageName);
        if (checker != null) {
            checker.cancel();
        } else {
            Log.w(TAG, "Ignoring request to cancel explicit health check for unsupported package "
                    + packageName);
        }
    }

    @Override
    public List<String> onGetSupportedPackages() {
        return new ArrayList<>(mSupportedCheckers.keySet());
    }

    @Override
    public List<String> onGetRequestedPackages() {
        List<String> packages = new ArrayList<>();
        Iterator<ExplicitHealthChecker> it = mSupportedCheckers.values().iterator();
        // Could potentially race, where we read a checker#isPending and it changes before we
        // return list. However, if it races and it is in the list, the caller might call #cancel
        // which would fail, but that is fine. If it races and it ends up *not* in the list, it was
        // already cancelled, so there's no need for the caller to cancel it
        while (it.hasNext()) {
            ExplicitHealthChecker checker = it.next();
            if (checker.isPending()) {
                packages.add(checker.getPackageName());
            }
        }
        return packages;
    }

    private void initHealthCheckers() {
        Intent intent = new Intent(NETWORK_STACK_CONNECTOR_CLASS);
        ComponentName comp = intent.resolveSystemService(getPackageManager(), 0);
        if (comp != null) {
            String networkStackPackageName = comp.getPackageName();
            mSupportedCheckers.put(networkStackPackageName,
                    new NetworkChecker(this, networkStackPackageName));
        } else {
            // On Go devices, or any device that does not ship the network stack module.
            // The network stack will live in system_server process, so no need to monitor.
            Log.i(TAG, "Network stack module not found");
        }
    }
}
+44 −0
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.ext.services.watchdog;

/**
 * A type of explicit health check that can be performed on a device, e.g network health check
 */
interface ExplicitHealthChecker {
    /**
     * Requests a checker to listen to explicit health checks for {@link #getPackageName}.
     *  {@link #isPending} will now return {@code true}.
     */
    void request();

    /**
     * Cancels a pending explicit health check request for {@link #getPackageName}.
     * {@link #isPending} will now return {@code false}.
     */
    void cancel();

    /**
     * Returns {@code true} if a request is pending, {@code false} otherwise.
     */
    boolean isPending();

    /**
     * Returns the package name this checker can make requests for.
     */
    String getPackageName();
}
+92 −0
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.ext.services.watchdog;

import android.net.ConnectivityManager;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.service.watchdog.ExplicitHealthCheckService;

import com.android.internal.annotations.GuardedBy;

/**
 * Observes the network stack via the ConnectivityManager.
 */
final class NetworkChecker extends ConnectivityManager.NetworkCallback
        implements ExplicitHealthChecker {
    private static final String TAG = "NetworkChecker";

    private final Object mLock = new Object();
    private final ExplicitHealthCheckService mService;
    private final String mPackageName;
    @GuardedBy("mLock")
    private boolean mIsPending;

    NetworkChecker(ExplicitHealthCheckService service, String packageName) {
        mService = service;
        mPackageName = packageName;
    }

    @Override
    public void request() {
        synchronized (mLock) {
            if (mIsPending) {
                return;
            }
            mService.getSystemService(ConnectivityManager.class).registerNetworkCallback(
                    new NetworkRequest.Builder().build(), this);
            mIsPending = true;
        }
    }

    @Override
    public void cancel() {
        synchronized (mLock) {
            if (!mIsPending) {
                return;
            }
            mService.getSystemService(ConnectivityManager.class).unregisterNetworkCallback(this);
            mIsPending = false;
        }
    }

    @Override
    public boolean isPending() {
        synchronized (mLock) {
            return mIsPending;
        }
    }

    @Override
    public String getPackageName() {
        return mPackageName;
    }

    // TODO(b/120598832): Also monitor NetworkCallback#onAvailable to see if we have any
    // available networks that may be unusable. This could be additional signal to our heuristics
    @Override
    public void onCapabilitiesChanged(Network network, NetworkCapabilities capabilities) {
        synchronized (mLock) {
            if (mIsPending
                    && capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)) {
                mService.notifyHealthCheckPassed(mPackageName);
                cancel();
            }
        }
    }
}