1 /* 2 * Copyright (C) 2019 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.internal.infra; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.content.ComponentName; 22 import android.util.ArrayMap; 23 import android.util.ArraySet; 24 import android.util.Log; 25 26 import java.io.PrintWriter; 27 import java.util.List; 28 import java.util.Objects; 29 30 /** 31 * Helper class for keeping track of allowlisted packages/activities. 32 * 33 * <p><b>NOTE: </b>this class is not thread safe. 34 * @hide 35 */ 36 public final class WhitelistHelper { 37 38 private static final String TAG = "WhitelistHelper"; 39 40 /** 41 * Map of allowlisted packages/activities. The whole package is allowlisted if its 42 * corresponding value is {@code null}. 43 */ 44 @Nullable 45 private ArrayMap<String, ArraySet<ComponentName>> mWhitelistedPackages; 46 47 /** 48 * Sets the allowlist with the given packages and activities. The list is cleared if both 49 * packageNames and components are {@code null}. 50 * 51 * @param packageNames packages to be allowlisted. 52 * @param components activities to be allowlisted. 53 * 54 * @throws IllegalArgumentException if packages or components are empty. 55 */ setWhitelist(@ullable ArraySet<String> packageNames, @Nullable ArraySet<ComponentName> components)56 public void setWhitelist(@Nullable ArraySet<String> packageNames, 57 @Nullable ArraySet<ComponentName> components) { 58 mWhitelistedPackages = null; 59 if (packageNames == null && components == null) return; 60 61 if ((packageNames != null && packageNames.isEmpty()) 62 || (components != null && components.isEmpty())) { 63 throw new IllegalArgumentException("Packages or Components cannot be empty."); 64 } 65 66 mWhitelistedPackages = new ArrayMap<>(); 67 68 if (packageNames != null) { 69 for (int i = 0; i < packageNames.size(); i++) { 70 mWhitelistedPackages.put(packageNames.valueAt(i), null); 71 } 72 } 73 74 if (components != null) { 75 for (int i = 0; i < components.size(); i++) { 76 final ComponentName component = components.valueAt(i); 77 if (component == null) { 78 Log.w(TAG, "setWhitelist(): component is null"); 79 continue; 80 } 81 82 final String packageName = component.getPackageName(); 83 ArraySet<ComponentName> set = mWhitelistedPackages.get(packageName); 84 if (set == null) { 85 set = new ArraySet<>(); 86 mWhitelistedPackages.put(packageName, set); 87 } 88 set.add(component); 89 } 90 } 91 } 92 93 /** 94 * Helper to use {@link #setWhitelist(ArraySet, ArraySet)} with {@link List Lists}. 95 */ setWhitelist(@ullable List<String> packageNames, @Nullable List<ComponentName> components)96 public void setWhitelist(@Nullable List<String> packageNames, 97 @Nullable List<ComponentName> components) { 98 final ArraySet<String> packageNamesSet = packageNames == null ? null 99 : new ArraySet<>(packageNames); 100 final ArraySet<ComponentName> componentsSet = components == null ? null 101 : new ArraySet<>(components); 102 setWhitelist(packageNamesSet, componentsSet); 103 } 104 105 /** 106 * Returns {@code true} if the entire package is allowlisted. 107 */ isWhitelisted(@onNull String packageName)108 public boolean isWhitelisted(@NonNull String packageName) { 109 Objects.requireNonNull(packageName); 110 111 if (mWhitelistedPackages == null) return false; 112 113 return mWhitelistedPackages.containsKey(packageName) 114 && mWhitelistedPackages.get(packageName) == null; 115 } 116 117 /** 118 * Returns {@code true} if the specified activity is allowlisted. 119 */ isWhitelisted(@onNull ComponentName componentName)120 public boolean isWhitelisted(@NonNull ComponentName componentName) { 121 Objects.requireNonNull(componentName); 122 123 final String packageName = componentName.getPackageName(); 124 final ArraySet<ComponentName> whitelistedComponents = getWhitelistedComponents(packageName); 125 if (whitelistedComponents != null) { 126 return whitelistedComponents.contains(componentName); 127 } 128 129 return isWhitelisted(packageName); 130 } 131 132 /** 133 * Returns a set of allowlisted components with the given package, or null if nothing is 134 * allowlisted. 135 */ 136 @Nullable getWhitelistedComponents(@onNull String packageName)137 public ArraySet<ComponentName> getWhitelistedComponents(@NonNull String packageName) { 138 Objects.requireNonNull(packageName); 139 140 return mWhitelistedPackages == null ? null : mWhitelistedPackages.get(packageName); 141 } 142 143 /** 144 * Returns a set of all packages that are either entirely allowlisted or have components that 145 * are allowlisted. 146 */ 147 @Nullable getWhitelistedPackages()148 public ArraySet<String> getWhitelistedPackages() { 149 return mWhitelistedPackages == null ? null : new ArraySet<>(mWhitelistedPackages.keySet()); 150 } 151 152 @Override toString()153 public String toString() { 154 return "WhitelistHelper[" + mWhitelistedPackages + ']'; 155 } 156 157 /** 158 * Dumps it! 159 */ dump(@onNull String prefix, @NonNull String message, @NonNull PrintWriter pw)160 public void dump(@NonNull String prefix, @NonNull String message, @NonNull PrintWriter pw) { 161 if (mWhitelistedPackages == null || mWhitelistedPackages.size() == 0) { 162 pw.print(prefix); pw.print(message); pw.println(": (no whitelisted packages)"); 163 return; 164 } 165 166 final String prefix2 = prefix + " "; 167 final int size = mWhitelistedPackages.size(); 168 pw.print(prefix); pw.print(message); pw.print(": "); pw.print(size); 169 pw.println(" packages"); 170 for (int i = 0; i < mWhitelistedPackages.size(); i++) { 171 final String packageName = mWhitelistedPackages.keyAt(i); 172 final ArraySet<ComponentName> components = mWhitelistedPackages.valueAt(i); 173 pw.print(prefix2); pw.print(i); pw.print("."); pw.print(packageName); pw.print(": "); 174 if (components == null) { 175 pw.println("(whole package)"); 176 continue; 177 } 178 179 pw.print("["); pw.print(components.valueAt(0)); 180 for (int j = 1; j < components.size(); j++) { 181 pw.print(", "); pw.print(components.valueAt(j)); 182 } 183 pw.println("]"); 184 } 185 } 186 } 187