/* * Copyright (C) 2015 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.car; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager.NameNotFoundException; import android.os.Binder; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; import android.os.SystemClock; import android.util.ArrayMap; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import java.util.ArrayList; import java.util.List; /** Utility class */ public final class CarServiceUtils { private static final String TAG = CarLog.tagFor(CarServiceUtils.class); /** Empty int array */ public static final int[] EMPTY_INT_ARRAY = new int[0]; private static final String PACKAGE_NOT_FOUND = "Package not found:"; /** K: class name, V: HandlerThread */ private static final ArrayMap sHandlerThreads = new ArrayMap<>(); /** do not construct. static only */ private CarServiceUtils() {}; /** * Check if package name passed belongs to UID for the current binder call. * @param context * @param packageName */ public static void assertPackageName(Context context, String packageName) throws IllegalArgumentException, SecurityException { if (packageName == null) { throw new IllegalArgumentException("Package name null"); } ApplicationInfo appInfo = null; try { appInfo = context.getPackageManager().getApplicationInfo(packageName, 0); } catch (NameNotFoundException e) { String msg = PACKAGE_NOT_FOUND + packageName; Slog.w(CarLog.TAG_SERVICE, msg, e); throw new SecurityException(msg, e); } if (appInfo == null) { throw new SecurityException(PACKAGE_NOT_FOUND + packageName); } int uid = Binder.getCallingUid(); if (uid != appInfo.uid) { throw new SecurityException("Wrong package name:" + packageName + ", The package does not belong to caller's uid:" + uid); } } /** * Execute a runnable on the main thread * * @param action The code to run on the main thread. */ public static void runOnMain(Runnable action) { runOnLooper(Looper.getMainLooper(), action); } /** * Execute a runnable in the given looper * @param looper Looper to run the action. * @param action The code to run. */ public static void runOnLooper(Looper looper, Runnable action) { new Handler(looper).post(action); } /** * Execute a call on the application's main thread, blocking until it is * complete. Useful for doing things that are not thread-safe, such as * looking at or modifying the view hierarchy. * * @param action The code to run on the main thread. */ public static void runOnMainSync(Runnable action) { runOnLooperSync(Looper.getMainLooper(), action); } /** * Execute a call on the given Looper thread, blocking until it is * complete. * * @param looper Looper to run the action. * @param action The code to run on the main thread. */ public static void runOnLooperSync(Looper looper, Runnable action) { if (Looper.myLooper() == looper) { // requested thread is the same as the current thread. call directly. action.run(); } else { Handler handler = new Handler(looper); SyncRunnable sr = new SyncRunnable(action); handler.post(sr); sr.waitForComplete(); } } private static final class SyncRunnable implements Runnable { private final Runnable mTarget; private volatile boolean mComplete = false; public SyncRunnable(Runnable target) { mTarget = target; } @Override public void run() { mTarget.run(); synchronized (this) { mComplete = true; notifyAll(); } } public void waitForComplete() { synchronized (this) { while (!mComplete) { try { wait(); } catch (InterruptedException e) { } } } } } public static float[] toFloatArray(List list) { int size = list.size(); float[] array = new float[size]; for (int i = 0; i < size; ++i) { array[i] = list.get(i); } return array; } public static long[] toLongArray(List list) { int size = list.size(); long[] array = new long[size]; for (int i = 0; i < size; ++i) { array[i] = list.get(i); } return array; } public static int[] toIntArray(List list) { int size = list.size(); int[] array = new int[size]; for (int i = 0; i < size; ++i) { array[i] = list.get(i); } return array; } public static byte[] toByteArray(List list) { int size = list.size(); byte[] array = new byte[size]; for (int i = 0; i < size; ++i) { array[i] = list.get(i); } return array; } /** * Returns delta between elapsed time to uptime = {@link SystemClock#elapsedRealtime()} - * {@link SystemClock#uptimeMillis()}. Note that this value will be always >= 0. */ public static long getUptimeToElapsedTimeDeltaInMillis() { int retry = 0; int max_retry = 2; // try only up to twice while (true) { long elapsed1 = SystemClock.elapsedRealtime(); long uptime = SystemClock.uptimeMillis(); long elapsed2 = SystemClock.elapsedRealtime(); if (elapsed1 == elapsed2) { // avoid possible 1 ms fluctuation. return elapsed1 - uptime; } retry++; if (retry >= max_retry) { return elapsed1 - uptime; } } } /** * Gets a static instance of {@code HandlerThread} for the given {@code name}. If the thread * does not exist, create one and start it before returning. */ public static HandlerThread getHandlerThread(String name) { synchronized (sHandlerThreads) { HandlerThread thread = sHandlerThreads.get(name); if (thread == null || !thread.isAlive()) { Slog.i(TAG, "Starting HandlerThread:" + name); thread = new HandlerThread(name); thread.start(); sHandlerThreads.put(name, thread); } return thread; } } /** * Finishes all queued {@code Handler} tasks for {@code HandlerThread} created via * {@link #getHandlerThread(String)}. This is useful only for testing. */ @VisibleForTesting public static void finishAllHandlerTasks() { ArrayList threads; synchronized (sHandlerThreads) { threads = new ArrayList<>(sHandlerThreads.values()); } ArrayList syncs = new ArrayList<>(threads.size()); for (int i = 0; i < threads.size(); i++) { Handler handler = new Handler(threads.get(i).getLooper()); SyncRunnable sr = new SyncRunnable(() -> { }); handler.post(sr); syncs.add(sr); } for (int i = 0; i < syncs.size(); i++) { syncs.get(i).waitForComplete(); } } }