blob: 2dddeeb11783f4c4993394c40b658eaf05911419 [file] [log] [blame]
/*
* 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<String, HandlerThread> 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<Float> list) {
final int size = list.size();
final float[] array = new float[size];
for (int i = 0; i < size; ++i) {
array[i] = list.get(i);
}
return array;
}
public static int[] toIntArray(List<Integer> list) {
final int size = list.size();
final int[] array = new int[size];
for (int i = 0; i < size; ++i) {
array[i] = list.get(i);
}
return array;
}
public static byte[] toByteArray(List<Byte> list) {
final int size = list.size();
final 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<HandlerThread> threads;
synchronized (sHandlerThreads) {
threads = new ArrayList<>(sHandlerThreads.values());
}
ArrayList<SyncRunnable> 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();
}
}
}