1 /*
2  * Copyright (C) 2024 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 android.app.admin;
18 
19 import android.content.ComponentName;
20 import android.os.Bundle;
21 import android.os.Parcelable;
22 import android.os.PersistableBundle;
23 
24 import com.android.internal.util.Preconditions;
25 
26 import java.util.ArrayDeque;
27 import java.util.Queue;
28 
29 /**
30  * Utility class containing methods to verify the max allowed size of certain policy types.
31  *
32  * @hide
33  */
34 public class PolicySizeVerifier {
35 
36     // Binary XML serializer doesn't support longer strings
37     public static final int MAX_POLICY_STRING_LENGTH = 65535;
38     // FrameworkParsingPackageUtils#MAX_FILE_NAME_SIZE, Android packages are used in dir names.
39     public static final int MAX_PACKAGE_NAME_LENGTH = 223;
40 
41     public static final int MAX_PROFILE_NAME_LENGTH = 200;
42     public static final int MAX_LONG_SUPPORT_MESSAGE_LENGTH = 20000;
43     public static final int MAX_SHORT_SUPPORT_MESSAGE_LENGTH = 200;
44     public static final int MAX_ORG_NAME_LENGTH = 200;
45 
46     /**
47      * Throw if string argument is too long to be serialized.
48      */
enforceMaxStringLength(String str, String argName)49     public static void enforceMaxStringLength(String str, String argName) {
50         Preconditions.checkArgument(
51                 str.length() <= MAX_POLICY_STRING_LENGTH, argName + " loo long");
52     }
53 
54     /**
55      * Throw if package name exceeds max size allowed by the system.
56      */
enforceMaxPackageNameLength(String pkg)57     public static void enforceMaxPackageNameLength(String pkg) {
58         Preconditions.checkArgument(
59                 pkg.length() <= MAX_PACKAGE_NAME_LENGTH, "Package name too long");
60     }
61 
62     /**
63      * Throw if persistable bundle contains any string that's too long to be serialized.
64      */
enforceMaxStringLength(PersistableBundle bundle, String argName)65     public static void enforceMaxStringLength(PersistableBundle bundle, String argName) {
66         // Persistable bundles can have other persistable bundles as values, traverse with a queue.
67         Queue<PersistableBundle> queue = new ArrayDeque<>();
68         queue.add(bundle);
69         while (!queue.isEmpty()) {
70             PersistableBundle current = queue.remove();
71             for (String key : current.keySet()) {
72                 enforceMaxStringLength(key, "key in " + argName);
73                 Object value = current.get(key);
74                 if (value instanceof String str) {
75                     enforceMaxStringLength(str, "string value in " + argName);
76                 } else if (value instanceof String[] strArray) {
77                     for (String str : strArray) {
78                         enforceMaxStringLength(str, "string value in " + argName);
79                     }
80                 } else if (value instanceof PersistableBundle persistableBundle) {
81                     queue.add(persistableBundle);
82                 }
83             }
84         }
85     }
86 
87     /**
88      * Throw if bundle contains any string that's too long to be serialized. This follows the
89      * serialization logic in BundlePolicySerializer#writeBundle.
90      */
enforceMaxBundleFieldsLength(Bundle bundle)91     public static void enforceMaxBundleFieldsLength(Bundle bundle) {
92         Queue<Bundle> queue = new ArrayDeque<>();
93         queue.add(bundle);
94         while (!queue.isEmpty()) {
95             Bundle current = queue.remove();
96             for (String key : current.keySet()) {
97                 enforceMaxStringLength(key, "key in Bundle");
98                 Object value = current.get(key);
99                 if (value instanceof String str) {
100                     enforceMaxStringLength(str, "string value in Bundle with "
101                             + "key" + key);
102                 } else if (value instanceof String[] strArray) {
103                     for (String str : strArray) {
104                         enforceMaxStringLength(str, "string value in Bundle with"
105                                 + " key" + key);
106                     }
107                 } else if (value instanceof Bundle b) {
108                     queue.add(b);
109                 }
110                 else if (value instanceof Parcelable[] parcelableArray) {
111                     for (Parcelable parcelable : parcelableArray) {
112                         if (!(parcelable instanceof Bundle)) {
113                             throw new IllegalArgumentException("bundle-array can only hold "
114                                     + "Bundles");
115                         }
116                         queue.add((Bundle) parcelable);
117                     }
118                 }
119             }
120         }
121     }
122 
123     /**
124      * Throw if ComponentName contains any string that's too long to be serialized.
125      */
enforceMaxComponentNameLength(ComponentName componentName)126     public static void enforceMaxComponentNameLength(ComponentName componentName) {
127         enforceMaxPackageNameLength(componentName.getPackageName());
128         enforceMaxStringLength(componentName.flattenToString(), "componentName");
129     }
130 
131     /**
132      * Truncates char sequence to maximum length, nulls are ignored.
133      */
truncateIfLonger(CharSequence input, int maxLength)134     public static CharSequence truncateIfLonger(CharSequence input, int maxLength) {
135         return input == null || input.length() <= maxLength
136                 ? input
137                 : input.subSequence(0, maxLength);
138     }
139 }
140