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.Log;
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 = "CAR.UTIL";
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             Log.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                 Log.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