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