keunyoung | ca51507 | 2015-07-10 12:21:47 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2015 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.car; |
| 18 | |
Keun-young Park | 4aeb4bf | 2015-12-08 18:31:33 -0800 | [diff] [blame] | 19 | import android.content.Context; |
| 20 | import android.content.pm.ApplicationInfo; |
Keun-young Park | 4aeb4bf | 2015-12-08 18:31:33 -0800 | [diff] [blame] | 21 | import android.content.pm.PackageManager.NameNotFoundException; |
| 22 | import android.os.Binder; |
Keun-young Park | 43cfff2 | 2016-02-19 12:49:52 -0800 | [diff] [blame] | 23 | import android.os.Handler; |
Keun young Park | b241d02 | 2020-04-20 20:31:34 -0700 | [diff] [blame] | 24 | import android.os.HandlerThread; |
Keun-young Park | 43cfff2 | 2016-02-19 12:49:52 -0800 | [diff] [blame] | 25 | import android.os.Looper; |
Keun young Park | 401479c | 2020-02-19 14:15:51 -0800 | [diff] [blame] | 26 | import android.os.SystemClock; |
Keun young Park | b241d02 | 2020-04-20 20:31:34 -0700 | [diff] [blame] | 27 | import android.util.ArrayMap; |
Eric Jeong | bd5fb56 | 2020-12-21 13:49:40 -0800 | [diff] [blame] | 28 | import android.util.Slog; |
Keun-young Park | 4aeb4bf | 2015-12-08 18:31:33 -0800 | [diff] [blame] | 29 | |
Keun young Park | 1d07618 | 2020-05-21 09:40:59 -0700 | [diff] [blame] | 30 | import com.android.internal.annotations.VisibleForTesting; |
| 31 | |
| 32 | import java.util.ArrayList; |
Pavel Maltsev | 0d07c76 | 2016-11-03 16:40:15 -0700 | [diff] [blame] | 33 | import java.util.List; |
| 34 | |
keunyoung | ca51507 | 2015-07-10 12:21:47 -0700 | [diff] [blame] | 35 | /** Utility class */ |
Keun-young Park | 43cfff2 | 2016-02-19 12:49:52 -0800 | [diff] [blame] | 36 | public final class CarServiceUtils { |
keunyoung | ca51507 | 2015-07-10 12:21:47 -0700 | [diff] [blame] | 37 | |
Mayank Garg | 72c71d2 | 2021-02-03 23:54:45 -0800 | [diff] [blame] | 38 | private static final String TAG = CarLog.tagFor(CarServiceUtils.class); |
Keun young Park | 1fd33fe | 2019-12-19 18:25:14 -0800 | [diff] [blame] | 39 | /** Empty int array */ |
| 40 | public static final int[] EMPTY_INT_ARRAY = new int[0]; |
| 41 | |
Keun-young Park | 4aeb4bf | 2015-12-08 18:31:33 -0800 | [diff] [blame] | 42 | private static final String PACKAGE_NOT_FOUND = "Package not found:"; |
| 43 | |
Keun young Park | b241d02 | 2020-04-20 20:31:34 -0700 | [diff] [blame] | 44 | /** K: class name, V: HandlerThread */ |
| 45 | private static final ArrayMap<String, HandlerThread> sHandlerThreads = new ArrayMap<>(); |
| 46 | |
Keun-young Park | 43cfff2 | 2016-02-19 12:49:52 -0800 | [diff] [blame] | 47 | /** do not construct. static only */ |
| 48 | private CarServiceUtils() {}; |
| 49 | |
Keun-young Park | 4aeb4bf | 2015-12-08 18:31:33 -0800 | [diff] [blame] | 50 | /** |
| 51 | * Check if package name passed belongs to UID for the current binder call. |
| 52 | * @param context |
| 53 | * @param packageName |
| 54 | */ |
Enrico Granata | 3da3c61 | 2017-01-20 15:24:30 -0800 | [diff] [blame] | 55 | public static void assertPackageName(Context context, String packageName) |
Keun-young Park | 4aeb4bf | 2015-12-08 18:31:33 -0800 | [diff] [blame] | 56 | throws IllegalArgumentException, SecurityException { |
| 57 | if (packageName == null) { |
| 58 | throw new IllegalArgumentException("Package name null"); |
| 59 | } |
| 60 | ApplicationInfo appInfo = null; |
| 61 | try { |
| 62 | appInfo = context.getPackageManager().getApplicationInfo(packageName, |
| 63 | 0); |
| 64 | } catch (NameNotFoundException e) { |
| 65 | String msg = PACKAGE_NOT_FOUND + packageName; |
Eric Jeong | bd5fb56 | 2020-12-21 13:49:40 -0800 | [diff] [blame] | 66 | Slog.w(CarLog.TAG_SERVICE, msg, e); |
Keun-young Park | 4aeb4bf | 2015-12-08 18:31:33 -0800 | [diff] [blame] | 67 | throw new SecurityException(msg, e); |
| 68 | } |
| 69 | if (appInfo == null) { |
| 70 | throw new SecurityException(PACKAGE_NOT_FOUND + packageName); |
| 71 | } |
| 72 | int uid = Binder.getCallingUid(); |
| 73 | if (uid != appInfo.uid) { |
| 74 | throw new SecurityException("Wrong package name:" + packageName + |
| 75 | ", The package does not belong to caller's uid:" + uid); |
| 76 | } |
| 77 | } |
Keun-young Park | 43cfff2 | 2016-02-19 12:49:52 -0800 | [diff] [blame] | 78 | |
| 79 | /** |
| 80 | * Execute a runnable on the main thread |
| 81 | * |
| 82 | * @param action The code to run on the main thread. |
| 83 | */ |
| 84 | public static void runOnMain(Runnable action) { |
| 85 | runOnLooper(Looper.getMainLooper(), action); |
| 86 | } |
| 87 | |
| 88 | /** |
| 89 | * Execute a runnable in the given looper |
| 90 | * @param looper Looper to run the action. |
| 91 | * @param action The code to run. |
| 92 | */ |
| 93 | public static void runOnLooper(Looper looper, Runnable action) { |
| 94 | new Handler(looper).post(action); |
| 95 | } |
| 96 | |
| 97 | /** |
| 98 | * Execute a call on the application's main thread, blocking until it is |
| 99 | * complete. Useful for doing things that are not thread-safe, such as |
| 100 | * looking at or modifying the view hierarchy. |
| 101 | * |
| 102 | * @param action The code to run on the main thread. |
| 103 | */ |
| 104 | public static void runOnMainSync(Runnable action) { |
| 105 | runOnLooperSync(Looper.getMainLooper(), action); |
| 106 | } |
| 107 | |
| 108 | /** |
| 109 | * Execute a call on the given Looper thread, blocking until it is |
| 110 | * complete. |
| 111 | * |
| 112 | * @param looper Looper to run the action. |
| 113 | * @param action The code to run on the main thread. |
| 114 | */ |
| 115 | public static void runOnLooperSync(Looper looper, Runnable action) { |
| 116 | if (Looper.myLooper() == looper) { |
| 117 | // requested thread is the same as the current thread. call directly. |
| 118 | action.run(); |
| 119 | } else { |
| 120 | Handler handler = new Handler(looper); |
| 121 | SyncRunnable sr = new SyncRunnable(action); |
| 122 | handler.post(sr); |
| 123 | sr.waitForComplete(); |
| 124 | } |
| 125 | } |
| 126 | |
| 127 | private static final class SyncRunnable implements Runnable { |
| 128 | private final Runnable mTarget; |
| 129 | private volatile boolean mComplete = false; |
| 130 | |
| 131 | public SyncRunnable(Runnable target) { |
| 132 | mTarget = target; |
| 133 | } |
| 134 | |
| 135 | @Override |
| 136 | public void run() { |
| 137 | mTarget.run(); |
| 138 | synchronized (this) { |
| 139 | mComplete = true; |
| 140 | notifyAll(); |
| 141 | } |
| 142 | } |
| 143 | |
| 144 | public void waitForComplete() { |
| 145 | synchronized (this) { |
| 146 | while (!mComplete) { |
| 147 | try { |
| 148 | wait(); |
| 149 | } catch (InterruptedException e) { |
| 150 | } |
| 151 | } |
| 152 | } |
| 153 | } |
| 154 | } |
Keun-young Park | 71b2f5c | 2016-03-10 18:44:40 -0800 | [diff] [blame] | 155 | |
| 156 | public static float[] toFloatArray(List<Float> list) { |
Ashutosh Agarwal | 9743335 | 2021-10-04 18:14:58 +0000 | [diff] [blame] | 157 | int size = list.size(); |
| 158 | float[] array = new float[size]; |
| 159 | for (int i = 0; i < size; ++i) { |
| 160 | array[i] = list.get(i); |
| 161 | } |
| 162 | return array; |
| 163 | } |
| 164 | |
| 165 | public static long[] toLongArray(List<Long> list) { |
| 166 | int size = list.size(); |
| 167 | long[] array = new long[size]; |
Keun-young Park | 71b2f5c | 2016-03-10 18:44:40 -0800 | [diff] [blame] | 168 | for (int i = 0; i < size; ++i) { |
| 169 | array[i] = list.get(i); |
| 170 | } |
| 171 | return array; |
| 172 | } |
| 173 | |
| 174 | public static int[] toIntArray(List<Integer> list) { |
Ashutosh Agarwal | 9743335 | 2021-10-04 18:14:58 +0000 | [diff] [blame] | 175 | int size = list.size(); |
| 176 | int[] array = new int[size]; |
Keun-young Park | 71b2f5c | 2016-03-10 18:44:40 -0800 | [diff] [blame] | 177 | for (int i = 0; i < size; ++i) { |
| 178 | array[i] = list.get(i); |
| 179 | } |
| 180 | return array; |
| 181 | } |
Pavel Maltsev | 0d07c76 | 2016-11-03 16:40:15 -0700 | [diff] [blame] | 182 | |
| 183 | public static byte[] toByteArray(List<Byte> list) { |
Ashutosh Agarwal | 9743335 | 2021-10-04 18:14:58 +0000 | [diff] [blame] | 184 | int size = list.size(); |
| 185 | byte[] array = new byte[size]; |
Pavel Maltsev | 0d07c76 | 2016-11-03 16:40:15 -0700 | [diff] [blame] | 186 | for (int i = 0; i < size; ++i) { |
| 187 | array[i] = list.get(i); |
| 188 | } |
| 189 | return array; |
| 190 | } |
Keun young Park | 401479c | 2020-02-19 14:15:51 -0800 | [diff] [blame] | 191 | |
| 192 | /** |
| 193 | * Returns delta between elapsed time to uptime = {@link SystemClock#elapsedRealtime()} - |
| 194 | * {@link SystemClock#uptimeMillis()}. Note that this value will be always >= 0. |
| 195 | */ |
| 196 | public static long getUptimeToElapsedTimeDeltaInMillis() { |
| 197 | int retry = 0; |
| 198 | int max_retry = 2; // try only up to twice |
| 199 | while (true) { |
| 200 | long elapsed1 = SystemClock.elapsedRealtime(); |
| 201 | long uptime = SystemClock.uptimeMillis(); |
| 202 | long elapsed2 = SystemClock.elapsedRealtime(); |
| 203 | if (elapsed1 == elapsed2) { // avoid possible 1 ms fluctuation. |
| 204 | return elapsed1 - uptime; |
| 205 | } |
| 206 | retry++; |
| 207 | if (retry >= max_retry) { |
| 208 | return elapsed1 - uptime; |
| 209 | } |
| 210 | } |
| 211 | } |
Keun young Park | b241d02 | 2020-04-20 20:31:34 -0700 | [diff] [blame] | 212 | |
| 213 | /** |
| 214 | * Gets a static instance of {@code HandlerThread} for the given {@code name}. If the thread |
| 215 | * does not exist, create one and start it before returning. |
| 216 | */ |
| 217 | public static HandlerThread getHandlerThread(String name) { |
| 218 | synchronized (sHandlerThreads) { |
| 219 | HandlerThread thread = sHandlerThreads.get(name); |
Zhomart Mukhamejanov | e65a339 | 2020-06-18 14:33:47 -0700 | [diff] [blame] | 220 | if (thread == null || !thread.isAlive()) { |
Eric Jeong | bd5fb56 | 2020-12-21 13:49:40 -0800 | [diff] [blame] | 221 | Slog.i(TAG, "Starting HandlerThread:" + name); |
Keun young Park | b241d02 | 2020-04-20 20:31:34 -0700 | [diff] [blame] | 222 | thread = new HandlerThread(name); |
| 223 | thread.start(); |
| 224 | sHandlerThreads.put(name, thread); |
| 225 | } |
| 226 | return thread; |
| 227 | } |
| 228 | } |
Keun young Park | 1d07618 | 2020-05-21 09:40:59 -0700 | [diff] [blame] | 229 | |
| 230 | /** |
| 231 | * Finishes all queued {@code Handler} tasks for {@code HandlerThread} created via |
| 232 | * {@link #getHandlerThread(String)}. This is useful only for testing. |
| 233 | */ |
| 234 | @VisibleForTesting |
| 235 | public static void finishAllHandlerTasks() { |
| 236 | ArrayList<HandlerThread> threads; |
| 237 | synchronized (sHandlerThreads) { |
| 238 | threads = new ArrayList<>(sHandlerThreads.values()); |
| 239 | } |
| 240 | ArrayList<SyncRunnable> syncs = new ArrayList<>(threads.size()); |
| 241 | for (int i = 0; i < threads.size(); i++) { |
| 242 | Handler handler = new Handler(threads.get(i).getLooper()); |
| 243 | SyncRunnable sr = new SyncRunnable(() -> { }); |
| 244 | handler.post(sr); |
| 245 | syncs.add(sr); |
| 246 | } |
| 247 | for (int i = 0; i < syncs.size(); i++) { |
| 248 | syncs.get(i).waitForComplete(); |
| 249 | } |
| 250 | } |
keunyoung | ca51507 | 2015-07-10 12:21:47 -0700 | [diff] [blame] | 251 | } |