1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.ondevicepersonalization.services; 18 19 import android.annotation.NonNull; 20 import android.os.Handler; 21 import android.os.HandlerThread; 22 import android.os.Process; 23 import android.os.StrictMode; 24 import android.os.StrictMode.ThreadPolicy; 25 26 import com.google.common.util.concurrent.ListeningExecutorService; 27 import com.google.common.util.concurrent.MoreExecutors; 28 import com.google.common.util.concurrent.ThreadFactoryBuilder; 29 30 import java.util.Optional; 31 import java.util.concurrent.Executors; 32 import java.util.concurrent.ThreadFactory; 33 34 /** 35 * All executors of the OnDevicePersonalization module. 36 */ 37 public final class OnDevicePersonalizationExecutors { 38 private static final ListeningExecutorService sBackgroundExecutor = 39 MoreExecutors.listeningDecorator(Executors.newFixedThreadPool( 40 /* nThreads */ 4, 41 createThreadFactory("BG Thread", Process.THREAD_PRIORITY_BACKGROUND, 42 Optional.of(getIoThreadPolicy())))); 43 44 private static final ListeningExecutorService sLightweightExecutor = 45 MoreExecutors.listeningDecorator(Executors.newFixedThreadPool( 46 /* nThreads */ Math.max(2, Runtime.getRuntime().availableProcessors() - 2), 47 createThreadFactory("Lite Thread", Process.THREAD_PRIORITY_DEFAULT, 48 Optional.of(getAsyncThreadPolicy())))); 49 50 private static final ListeningExecutorService sBlockingExecutor = 51 MoreExecutors.listeningDecorator(Executors.newCachedThreadPool( 52 createThreadFactory("Blocking Thread", Process.THREAD_PRIORITY_BACKGROUND 53 + Process.THREAD_PRIORITY_LESS_FAVORABLE, Optional.empty()))); 54 55 private static final HandlerThread sHandlerThread = createHandlerThread(); 56 57 private static final Handler sHandler = new Handler(sHandlerThread.getLooper()); 58 OnDevicePersonalizationExecutors()59 private OnDevicePersonalizationExecutors() { 60 } 61 62 /** 63 * Returns an executor suitable for long-running tasks, like database operations, file I/O, or 64 * heavy CPU-bound computation. 65 */ 66 @NonNull getBackgroundExecutor()67 public static ListeningExecutorService getBackgroundExecutor() { 68 return sBackgroundExecutor; 69 } 70 71 /** 72 * Returns an executor for tasks that don't do direct I/O and that are fast (<10ms). 73 */ 74 @NonNull getLightweightExecutor()75 public static ListeningExecutorService getLightweightExecutor() { 76 return sLightweightExecutor; 77 } 78 79 /** 80 * Returns an executor suitable for tasks which block for indeterminate amounts of time and 81 * are not CPU bound. 82 */ 83 @NonNull getBlockingExecutor()84 public static ListeningExecutorService getBlockingExecutor() { 85 return sBlockingExecutor; 86 } 87 88 /** 89 * Returns a Handler that can post messages to a HandlerThread. 90 */ getHandler()91 public static Handler getHandler() { 92 return sHandler; 93 } 94 createThreadFactory( final String name, final int priority, final Optional<StrictMode.ThreadPolicy> policy)95 private static ThreadFactory createThreadFactory( 96 final String name, final int priority, final Optional<StrictMode.ThreadPolicy> policy) { 97 return new ThreadFactoryBuilder() 98 .setDaemon(true) 99 .setNameFormat(name + " #%d") 100 .setThreadFactory( 101 new ThreadFactory() { 102 @Override 103 public Thread newThread(final Runnable runnable) { 104 return new Thread(new Runnable() { 105 @Override 106 public void run() { 107 if (policy.isPresent()) { 108 StrictMode.setThreadPolicy(policy.get()); 109 } 110 // Process class operates on the current thread. 111 Process.setThreadPriority(priority); 112 runnable.run(); 113 } 114 }); 115 } 116 }) 117 .build(); 118 } 119 120 private static ThreadPolicy getAsyncThreadPolicy() { 121 return new ThreadPolicy.Builder().detectAll().penaltyLog().build(); 122 } 123 124 private static ThreadPolicy getIoThreadPolicy() { 125 return new ThreadPolicy.Builder() 126 .detectNetwork() 127 .detectResourceMismatches() 128 .detectUnbufferedIo() 129 .penaltyLog() 130 .build(); 131 } 132 133 private static HandlerThread createHandlerThread() { 134 HandlerThread handlerThread = new HandlerThread("DisplayThread"); 135 handlerThread.start(); 136 return handlerThread; 137 } 138 } 139