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 19 import android.content.Context; 20 import android.content.pm.ApplicationInfo; 21 import android.content.pm.PackageManager.NameNotFoundException; 22 import android.os.Binder; 23 import android.os.Handler; 24 import android.os.HandlerThread; 25 import android.os.Looper; 26 import android.os.SystemClock; 27 import android.util.ArrayMap; 28 import android.util.Slog; 29 30 import com.android.internal.annotations.VisibleForTesting; 31 32 import java.util.ArrayList; 33 import java.util.List; 34 35 /** Utility class */ 36 public final class CarServiceUtils { 37 38 private static final String TAG = CarLog.tagFor(CarServiceUtils.class); 39 /** Empty int array */ 40 public static final int[] EMPTY_INT_ARRAY = new int[0]; 41 42 private static final String PACKAGE_NOT_FOUND = "Package not found:"; 43 44 /** K: class name, V: HandlerThread */ 45 private static final ArrayMap<String, HandlerThread> sHandlerThreads = new ArrayMap<>(); 46 47 /** do not construct. static only */ CarServiceUtils()48 private CarServiceUtils() {}; 49 50 /** 51 * Check if package name passed belongs to UID for the current binder call. 52 * @param context 53 * @param packageName 54 */ assertPackageName(Context context, String packageName)55 public static void assertPackageName(Context context, String packageName) 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; 66 Slog.w(CarLog.TAG_SERVICE, msg, e); 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 } 78 79 /** 80 * Execute a runnable on the main thread 81 * 82 * @param action The code to run on the main thread. 83 */ runOnMain(Runnable action)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 */ runOnLooper(Looper looper, Runnable action)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 */ runOnMainSync(Runnable action)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 */ runOnLooperSync(Looper looper, Runnable action)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 SyncRunnable(Runnable target)131 public SyncRunnable(Runnable target) { 132 mTarget = target; 133 } 134 135 @Override run()136 public void run() { 137 mTarget.run(); 138 synchronized (this) { 139 mComplete = true; 140 notifyAll(); 141 } 142 } 143 waitForComplete()144 public void waitForComplete() { 145 synchronized (this) { 146 while (!mComplete) { 147 try { 148 wait(); 149 } catch (InterruptedException e) { 150 } 151 } 152 } 153 } 154 } 155 toFloatArray(List<Float> list)156 public static float[] toFloatArray(List<Float> list) { 157 final int size = list.size(); 158 final 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 toIntArray(List<Integer> list)165 public static int[] toIntArray(List<Integer> list) { 166 final int size = list.size(); 167 final int[] array = new int[size]; 168 for (int i = 0; i < size; ++i) { 169 array[i] = list.get(i); 170 } 171 return array; 172 } 173 toByteArray(List<Byte> list)174 public static byte[] toByteArray(List<Byte> list) { 175 final int size = list.size(); 176 final byte[] array = new byte[size]; 177 for (int i = 0; i < size; ++i) { 178 array[i] = list.get(i); 179 } 180 return array; 181 } 182 183 /** 184 * Returns delta between elapsed time to uptime = {@link SystemClock#elapsedRealtime()} - 185 * {@link SystemClock#uptimeMillis()}. Note that this value will be always >= 0. 186 */ getUptimeToElapsedTimeDeltaInMillis()187 public static long getUptimeToElapsedTimeDeltaInMillis() { 188 int retry = 0; 189 int max_retry = 2; // try only up to twice 190 while (true) { 191 long elapsed1 = SystemClock.elapsedRealtime(); 192 long uptime = SystemClock.uptimeMillis(); 193 long elapsed2 = SystemClock.elapsedRealtime(); 194 if (elapsed1 == elapsed2) { // avoid possible 1 ms fluctuation. 195 return elapsed1 - uptime; 196 } 197 retry++; 198 if (retry >= max_retry) { 199 return elapsed1 - uptime; 200 } 201 } 202 } 203 204 /** 205 * Gets a static instance of {@code HandlerThread} for the given {@code name}. If the thread 206 * does not exist, create one and start it before returning. 207 */ getHandlerThread(String name)208 public static HandlerThread getHandlerThread(String name) { 209 synchronized (sHandlerThreads) { 210 HandlerThread thread = sHandlerThreads.get(name); 211 if (thread == null || !thread.isAlive()) { 212 Slog.i(TAG, "Starting HandlerThread:" + name); 213 thread = new HandlerThread(name); 214 thread.start(); 215 sHandlerThreads.put(name, thread); 216 } 217 return thread; 218 } 219 } 220 221 /** 222 * Finishes all queued {@code Handler} tasks for {@code HandlerThread} created via 223 * {@link #getHandlerThread(String)}. This is useful only for testing. 224 */ 225 @VisibleForTesting finishAllHandlerTasks()226 public static void finishAllHandlerTasks() { 227 ArrayList<HandlerThread> threads; 228 synchronized (sHandlerThreads) { 229 threads = new ArrayList<>(sHandlerThreads.values()); 230 } 231 ArrayList<SyncRunnable> syncs = new ArrayList<>(threads.size()); 232 for (int i = 0; i < threads.size(); i++) { 233 Handler handler = new Handler(threads.get(i).getLooper()); 234 SyncRunnable sr = new SyncRunnable(() -> { }); 235 handler.post(sr); 236 syncs.add(sr); 237 } 238 for (int i = 0; i < syncs.size(); i++) { 239 syncs.get(i).waitForComplete(); 240 } 241 } 242 } 243