blob: 718ffaf7255884dd7b17997757e9871acedb9700 [file] [log] [blame]
/*
* Copyright (C) 2023 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 com.android.server.healthconnect;
import android.annotation.NonNull;
import android.app.ActivityManager;
import android.content.Context;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* A scheduler class to schedule task on the most relevant thread-pool.
*
* @hide
*/
public final class HealthConnectThreadScheduler {
private static final int NUM_EXECUTOR_THREADS_INTERNAL_BACKGROUND = 1;
private static final long KEEP_ALIVE_TIME_INTERNAL_BACKGROUND = 60L;
// Executor to run HC background tasks
private static final Executor INTERNAL_BACKGROUND_EXECUTOR =
new ThreadPoolExecutor(
NUM_EXECUTOR_THREADS_INTERNAL_BACKGROUND,
NUM_EXECUTOR_THREADS_INTERNAL_BACKGROUND,
KEEP_ALIVE_TIME_INTERNAL_BACKGROUND,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>());
private static final int NUM_EXECUTOR_THREADS_BACKGROUND = 1;
private static final long KEEP_ALIVE_TIME_BACKGROUND = 60L;
// Executor to run HC background tasks
static final Executor BACKGROUND_EXECUTOR =
new ThreadPoolExecutor(
NUM_EXECUTOR_THREADS_BACKGROUND,
NUM_EXECUTOR_THREADS_BACKGROUND,
KEEP_ALIVE_TIME_BACKGROUND,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>());
private static final int NUM_EXECUTOR_THREADS_FOREGROUND = 1;
private static final long KEEP_ALIVE_TIME_SHARED = 60L;
// Executor to run HC tasks for clients
private static final Executor FOREGROUND_EXECUTOR =
new ThreadPoolExecutor(
NUM_EXECUTOR_THREADS_FOREGROUND,
NUM_EXECUTOR_THREADS_FOREGROUND,
KEEP_ALIVE_TIME_SHARED,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>());
private static final int NUM_EXECUTOR_THREADS_CONTROLLER = 1;
private static final long KEEP_ALIVE_TIME_CONTROLLER = 60L;
// Executor to run HC controller tasks
private static final Executor CONTROLLER_EXECUTOR =
new ThreadPoolExecutor(
NUM_EXECUTOR_THREADS_CONTROLLER,
NUM_EXECUTOR_THREADS_CONTROLLER,
KEEP_ALIVE_TIME_CONTROLLER,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>());
// Scheduler to run the tasks in a RR fashion based on client package names.
private static final HealthConnectRoundRobinScheduler
HEALTH_CONNECT_BACKGROUND_ROUND_ROBIN_SCHEDULER =
new HealthConnectRoundRobinScheduler();
/** Schedules the task on the executor dedicated for performing internal tasks */
static void scheduleInternalTask(Runnable task) {
INTERNAL_BACKGROUND_EXECUTOR.execute(task);
}
/** Schedules the task on the executor dedicated for performing controller tasks */
static void scheduleControllerTask(Runnable task) {
CONTROLLER_EXECUTOR.execute(task);
}
/** Schedules the task on the best possible executor based on the parameters */
static void schedule(Context context, @NonNull Runnable task, int uid, boolean isController) {
if (isController) {
CONTROLLER_EXECUTOR.execute(task);
return;
}
if (isUidInForeground(context, uid)) {
FOREGROUND_EXECUTOR.execute(
() -> {
try {
if (!isUidInForeground(context, uid)) {
// The app is no longer in foreground so move the task to background
// thread. This is because foreground thread should only be used by
// the foreground app and since the request of this task is no
// longer in foreground we don't want it to consume foreground
// resource anymore.
HEALTH_CONNECT_BACKGROUND_ROUND_ROBIN_SCHEDULER.addTask(uid, task);
BACKGROUND_EXECUTOR.execute(
() ->
HEALTH_CONNECT_BACKGROUND_ROUND_ROBIN_SCHEDULER
.getNextTask()
.run());
return;
}
} catch (Exception exception) {
// This is very unlikely, nonetheless we were unable to push the task to
// the background thread, ignore this and try to run it on foreground
// thread
}
task.run();
});
} else {
HEALTH_CONNECT_BACKGROUND_ROUND_ROBIN_SCHEDULER.addTask(uid, task);
BACKGROUND_EXECUTOR.execute(
() -> HEALTH_CONNECT_BACKGROUND_ROUND_ROBIN_SCHEDULER.getNextTask().run());
}
}
private static boolean isUidInForeground(Context context, int uid) {
ActivityManager activityManager = context.getSystemService(ActivityManager.class);
Objects.requireNonNull(activityManager);
List<ActivityManager.RunningAppProcessInfo> runningAppProcesses =
activityManager.getRunningAppProcesses();
for (ActivityManager.RunningAppProcessInfo info : runningAppProcesses) {
if (info.uid == uid
&& info.importance
== ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
return true;
}
}
return false;
}
}