1 /* 2 * Copyright (C) 2021 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 package com.android.car.util; 17 18 import static android.car.user.CarUserManager.lifecycleEventTypeToString; 19 20 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.BOILERPLATE_CODE; 21 22 import android.annotation.NonNull; 23 import android.annotation.Nullable; 24 import android.annotation.SuppressLint; 25 import android.annotation.UserIdInt; 26 import android.app.ActivityManager; 27 import android.car.builtin.util.Slogf; 28 import android.car.user.CarUserManager.UserLifecycleEvent; 29 import android.content.ContentResolver; 30 import android.content.Context; 31 import android.content.pm.PackageManager; 32 import android.os.Binder; 33 import android.os.UserHandle; 34 35 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport; 36 import com.android.car.internal.common.CommonConstants.UserLifecycleEventType; 37 38 import java.io.ByteArrayOutputStream; 39 import java.io.IOException; 40 import java.nio.ByteBuffer; 41 import java.nio.ByteOrder; 42 import java.util.Arrays; 43 import java.util.UUID; 44 import java.util.concurrent.ThreadLocalRandom; 45 import java.util.stream.Collectors; 46 47 /** 48 * Some potentially useful static methods. 49 */ 50 public final class Utils { 51 static final Boolean DBG = false; 52 // https://developer.android.com/reference/java/util/UUID 53 private static final int UUID_LENGTH = 16; 54 55 /** 56 * Returns a byte buffer corresponding to the passed long argument. 57 * 58 * @param primitive data to convert format. 59 */ longToBytes(long primitive)60 public static byte[] longToBytes(long primitive) { 61 ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES); 62 buffer.putLong(primitive); 63 return buffer.array(); 64 } 65 66 /** 67 * Returns a byte buffer corresponding to the passed long argument. 68 * 69 * @param array data to convert format. 70 */ bytesToLong(byte[] array)71 public static long bytesToLong(byte[] array) { 72 ByteBuffer buffer = ByteBuffer.allocate(Long.SIZE / Byte.SIZE); 73 buffer.put(array); 74 buffer.flip(); 75 long value = buffer.getLong(); 76 return value; 77 } 78 79 /** 80 * Returns a String in Hex format that is formed from the bytes in the byte array 81 * Useful for debugging 82 * 83 * @param array the byte array 84 * @return the Hex string version of the input byte array 85 */ byteArrayToHexString(byte[] array)86 public static String byteArrayToHexString(byte[] array) { 87 StringBuilder sb = new StringBuilder(array.length * 2); 88 for (byte b : array) { 89 sb.append(String.format("%02x", b)); 90 } 91 return sb.toString(); 92 } 93 94 /** 95 * Convert UUID to Big Endian byte array 96 * 97 * @param uuid UUID to convert 98 * @return the byte array representing the UUID 99 */ 100 @NonNull uuidToBytes(@onNull UUID uuid)101 public static byte[] uuidToBytes(@NonNull UUID uuid) { 102 103 return ByteBuffer.allocate(UUID_LENGTH) 104 .order(ByteOrder.BIG_ENDIAN) 105 .putLong(uuid.getMostSignificantBits()) 106 .putLong(uuid.getLeastSignificantBits()) 107 .array(); 108 } 109 110 /** 111 * Convert Big Endian byte array to UUID 112 * 113 * @param bytes byte array to convert 114 * @return the UUID representing the byte array, or null if not a valid UUID 115 */ 116 @Nullable bytesToUUID(@onNull byte[] bytes)117 public static UUID bytesToUUID(@NonNull byte[] bytes) { 118 if (bytes.length != UUID_LENGTH) { 119 return null; 120 } 121 122 ByteBuffer buffer = ByteBuffer.wrap(bytes); 123 return new UUID(buffer.getLong(), buffer.getLong()); 124 } 125 126 /** 127 * Generate a random zero-filled string of given length 128 * 129 * @param length of string 130 * @return generated string 131 */ 132 @SuppressLint("DefaultLocale") // Should always have the same format regardless of locale generateRandomNumberString(int length)133 public static String generateRandomNumberString(int length) { 134 return String.format("%0" + length + "d", 135 ThreadLocalRandom.current().nextInt((int) Math.pow(10, length))); 136 } 137 138 139 /** 140 * Concatentate the given 2 byte arrays 141 * 142 * @param a input array 1 143 * @param b input array 2 144 * @return concatenated array of arrays 1 and 2 145 */ 146 @Nullable concatByteArrays(@ullable byte[] a, @Nullable byte[] b)147 public static byte[] concatByteArrays(@Nullable byte[] a, @Nullable byte[] b) { 148 ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 149 try { 150 if (a != null) { 151 outputStream.write(a); 152 } 153 if (b != null) { 154 outputStream.write(b); 155 } 156 } catch (IOException e) { 157 return null; 158 } 159 return outputStream.toByteArray(); 160 } 161 162 @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE, 163 details = "private constructor") Utils()164 private Utils() { 165 throw new UnsupportedOperationException("contains only static methods"); 166 } 167 168 /** 169 * Returns the content resolver for the given user. This can be used to put/get the 170 * user's settings. 171 * 172 * @param context The context of the package. 173 * @param userId The id of the user which the content resolver is being requested for. It also 174 * accepts {@link UserHandle#USER_CURRENT}. 175 */ getContentResolverForUser(Context context, @UserIdInt int userId)176 public static ContentResolver getContentResolverForUser(Context context, 177 @UserIdInt int userId) { 178 if (userId == UserHandle.CURRENT.getIdentifier()) { 179 userId = ActivityManager.getCurrentUser(); 180 } 181 return context 182 .createContextAsUser( 183 UserHandle.of(userId), /* flags= */ 0) 184 .getContentResolver(); 185 } 186 187 /** 188 * Checks if the type of the {@code event} matches {@code expectedType}. 189 * 190 * @param tag The tag for logging. 191 * @param event The event to check the type against {@code expectedType}. 192 * @param expectedType The expected event type. 193 * @return true if {@code event}'s type matches {@code expectedType}. 194 * Otherwise, log a wtf and return false. 195 */ isEventOfType(String tag, UserLifecycleEvent event, @UserLifecycleEventType int expectedType)196 public static boolean isEventOfType(String tag, UserLifecycleEvent event, 197 @UserLifecycleEventType int expectedType) { 198 if (event.getEventType() == expectedType) { 199 return true; 200 } 201 Slogf.wtf(tag, "Received an unexpected event: %s. Expected type: %s.", event, 202 lifecycleEventTypeToString(expectedType)); 203 return false; 204 } 205 206 /** 207 * Checks if the type of the {@code event} is one of the types in {@code expectedTypes}. 208 * 209 * @param tag The tag for logging. 210 * @param event The event to check the type against {@code expectedTypes}. 211 * @param expectedTypes The expected event types. Must not be empty. 212 * @return true if {@code event}'s type can be found in {@code expectedTypes}. 213 * Otherwise, log a wtf and return false. 214 */ isEventAnyOfTypes(String tag, UserLifecycleEvent event, @UserLifecycleEventType int... expectedTypes)215 public static boolean isEventAnyOfTypes(String tag, UserLifecycleEvent event, 216 @UserLifecycleEventType int... expectedTypes) { 217 for (int i = 0; i < expectedTypes.length; i++) { 218 if (event.getEventType() == expectedTypes[i]) { 219 return true; 220 } 221 } 222 Slogf.wtf(tag, "Received an unexpected event: %s. Expected types: [%s]", event, 223 Arrays.stream(expectedTypes).mapToObj(t -> lifecycleEventTypeToString(t)).collect( 224 Collectors.joining(","))); 225 return false; 226 } 227 228 /** 229 * Checks if the calling UID owns the give package. 230 * 231 * @throws SecurityException if the calling UID doesn't own the given package. 232 */ checkCalledByPackage(Context context, String packageName)233 public static void checkCalledByPackage(Context context, String packageName) { 234 int callingUid = Binder.getCallingUid(); 235 PackageManager pm = context.getPackageManager(); 236 String[] packages = pm.getPackagesForUid(callingUid); 237 if (packages != null) { 238 for (String candidate: packages) { 239 if (candidate.equals(packageName)) { 240 return; 241 } 242 } 243 } 244 throw new SecurityException( 245 "Package " + packageName + " is not associated to UID " + callingUid); 246 } 247 248 } 249