1 /* 2 * Copyright (C) 2020 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.server.blob; 18 19 import static com.android.server.blob.BlobStoreConfig.TAG; 20 21 import android.annotation.IdRes; 22 import android.annotation.NonNull; 23 import android.annotation.Nullable; 24 import android.content.Context; 25 import android.content.pm.PackageManager; 26 import android.content.res.Resources; 27 import android.os.Handler; 28 import android.os.HandlerThread; 29 import android.os.UserHandle; 30 import android.text.format.TimeMigrationUtils; 31 import android.util.Slog; 32 33 class BlobStoreUtils { 34 private static final String DESC_RES_TYPE_STRING = "string"; 35 36 @Nullable getPackageResources(@onNull Context context, @NonNull String packageName, int userId)37 static Resources getPackageResources(@NonNull Context context, 38 @NonNull String packageName, int userId) { 39 try { 40 return context.createContextAsUser(UserHandle.of(userId), /* flags */ 0) 41 .getPackageManager().getResourcesForApplication(packageName); 42 } catch (PackageManager.NameNotFoundException e) { 43 Slog.d(TAG, "Unknown package in user " + userId + ": " 44 + packageName, e); 45 return null; 46 } 47 } 48 49 @IdRes getDescriptionResourceId(@onNull Resources resources, @NonNull String resourceEntryName, @NonNull String packageName)50 static int getDescriptionResourceId(@NonNull Resources resources, 51 @NonNull String resourceEntryName, @NonNull String packageName) { 52 return resources.getIdentifier(resourceEntryName, DESC_RES_TYPE_STRING, packageName); 53 } 54 55 @IdRes getDescriptionResourceId(@onNull Context context, @NonNull String resourceEntryName, @NonNull String packageName, int userId)56 static int getDescriptionResourceId(@NonNull Context context, 57 @NonNull String resourceEntryName, @NonNull String packageName, int userId) { 58 final Resources resources = getPackageResources(context, packageName, userId); 59 return resources == null 60 ? Resources.ID_NULL 61 : getDescriptionResourceId(resources, resourceEntryName, packageName); 62 } 63 64 @NonNull formatTime(long timeMs)65 static String formatTime(long timeMs) { 66 return TimeMigrationUtils.formatMillisWithFixedFormat(timeMs); 67 } 68 69 private static Handler sRevocableFdHandler; 70 private static final Object sLock = new Object(); 71 72 // By default, when using a RevocableFileDescriptor, callbacks will be sent to the process' 73 // main looper. In this case that would be system_server's main looper, which is a heavily 74 // contended thread. It can also cause deadlocks, because the volume daemon 'vold' holds a lock 75 // while making these callbacks to the system_server, while at the same time the system_server 76 // main thread can make a call into vold, which requires that same vold lock. To avoid these 77 // issues, use a separate thread for the RevocableFileDescriptor's requests, so that it can 78 // make progress independently of system_server. getRevocableFdHandler()79 static @NonNull Handler getRevocableFdHandler() { 80 synchronized (sLock) { 81 if (sRevocableFdHandler != null) { 82 return sRevocableFdHandler; 83 } 84 final HandlerThread t = new HandlerThread("BlobFuseLooper"); 85 t.start(); 86 sRevocableFdHandler = new Handler(t.getLooper()); 87 88 return sRevocableFdHandler; 89 } 90 } 91 } 92