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 17 package com.android.server.wm; 18 19 import static android.provider.DeviceConfig.NAMESPACE_WINDOW_MANAGER; 20 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.content.pm.ApplicationInfo; 24 import android.os.Build; 25 import android.provider.DeviceConfig; 26 import android.util.Slog; 27 28 import com.android.internal.annotations.GuardedBy; 29 import com.android.internal.annotations.VisibleForTesting; 30 31 import java.util.HashSet; 32 import java.util.Locale; 33 import java.util.concurrent.Executor; 34 import java.util.function.Supplier; 35 36 /** 37 * Handles filtering the list of package that don't use the system splash screen. 38 * The list is backed by a {@link DeviceConfig} property. 39 * <p> 40 * An application can manually opt-out of the exception list by setting the <meta-data 41 * {@value OPT_OUT_METADATA_FLAG}="true"/> in the <code><application></code> section of the 42 * manifest. 43 */ 44 class SplashScreenExceptionList { 45 46 private static final boolean DEBUG = Build.isDebuggable(); 47 private static final String LOG_TAG = "SplashScreenExceptionList"; 48 private static final String KEY_SPLASH_SCREEN_EXCEPTION_LIST = "splash_screen_exception_list"; 49 private static final String NAMESPACE = NAMESPACE_WINDOW_MANAGER; 50 private static final String OPT_OUT_METADATA_FLAG = "android.splashscreen.exception_opt_out"; 51 52 @GuardedBy("mLock") 53 private final HashSet<String> mDeviceConfigExcludedPackages = new HashSet<>(); 54 private final Object mLock = new Object(); 55 56 @VisibleForTesting 57 final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener; 58 SplashScreenExceptionList(@onNull Executor executor)59 SplashScreenExceptionList(@NonNull Executor executor) { 60 updateDeviceConfig(DeviceConfig.getString(NAMESPACE, KEY_SPLASH_SCREEN_EXCEPTION_LIST, "")); 61 mOnPropertiesChangedListener = properties -> updateDeviceConfig( 62 properties.getString(KEY_SPLASH_SCREEN_EXCEPTION_LIST, "")); 63 DeviceConfig.addOnPropertiesChangedListener(NAMESPACE, executor, 64 mOnPropertiesChangedListener); 65 } 66 67 @VisibleForTesting updateDeviceConfig(String values)68 void updateDeviceConfig(String values) { 69 parseDeviceConfigPackageList(values); 70 } 71 72 /** 73 * Returns true if the packageName is in the list and the target sdk is before or including V. 74 * 75 * @param packageName The package name of the application to check 76 * @param targetSdk The target sdk of the application 77 * @param infoSupplier A {@link Supplier} that returns an {@link ApplicationInfo} used to 78 * check if the application wants to opt-out of the exception list in the 79 * manifest metadata. Evaluated only if the application is in the exception 80 * list. 81 */ 82 @SuppressWarnings("AndroidFrameworkCompatChange") // Target sdk check isException(@onNull String packageName, int targetSdk, @Nullable Supplier<ApplicationInfo> infoSupplier)83 public boolean isException(@NonNull String packageName, int targetSdk, 84 @Nullable Supplier<ApplicationInfo> infoSupplier) { 85 if (targetSdk > Build.VERSION_CODES.UPSIDE_DOWN_CAKE + 1) { 86 return false; 87 } 88 89 synchronized (mLock) { 90 if (DEBUG) { 91 Slog.v(LOG_TAG, String.format(Locale.US, 92 "SplashScreen checking exception for package %s (target sdk:%d) -> %s", 93 packageName, targetSdk, 94 mDeviceConfigExcludedPackages.contains(packageName))); 95 } 96 if (!mDeviceConfigExcludedPackages.contains(packageName)) { 97 return false; 98 } 99 } 100 return !isOptedOut(infoSupplier); 101 } 102 103 /** 104 * An application can manually opt-out of the exception list by setting the meta-data 105 * {@value OPT_OUT_METADATA_FLAG} = true in the <code>application</code> section of the manifest 106 */ isOptedOut(@ullable Supplier<ApplicationInfo> infoProvider)107 private static boolean isOptedOut(@Nullable Supplier<ApplicationInfo> infoProvider) { 108 if (infoProvider == null) { 109 return false; 110 } 111 ApplicationInfo info = infoProvider.get(); 112 return info != null && info.metaData != null && info.metaData.getBoolean( 113 OPT_OUT_METADATA_FLAG, false); 114 } 115 parseDeviceConfigPackageList(String rawList)116 private void parseDeviceConfigPackageList(String rawList) { 117 synchronized (mLock) { 118 mDeviceConfigExcludedPackages.clear(); 119 String[] packages = rawList.split(","); 120 for (String packageName : packages) { 121 String packageNameTrimmed = packageName.trim(); 122 if (!packageNameTrimmed.isEmpty()) { 123 mDeviceConfigExcludedPackages.add(packageNameTrimmed); 124 } 125 } 126 } 127 } 128 } 129