• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 android.content.pm.verify.domain;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.os.IBinder;
22 import android.os.Parcel;
23 import android.util.ArraySet;
24 
25 import java.util.Collections;
26 import java.util.Map;
27 import java.util.Set;
28 
29 /**
30  * @hide
31  */
32 public class DomainVerificationUtils {
33 
34     private static final int STRINGS_TARGET_BYTE_SIZE = IBinder.getSuggestedMaxIpcSizeBytes() / 2;
35 
36     /**
37      * Write a map containing web hosts to the given parcel, using {@link Parcel#writeBlob(byte[])}
38      * if the limit exceeds {@link IBinder#getSuggestedMaxIpcSizeBytes()} / 2. This assumes that the
39      * written map is the only data structure in the caller that varies based on the host data set.
40      * Other data that will be written to the parcel after this method will not be considered in the
41      * calculation.
42      */
writeHostMap(@onNull Parcel dest, @NonNull Map<String, ?> map)43     public static void writeHostMap(@NonNull Parcel dest, @NonNull Map<String, ?> map) {
44         boolean targetSizeExceeded = false;
45         int totalSize = dest.dataSize();
46         for (String host : map.keySet()) {
47             totalSize += estimatedByteSizeOf(host);
48             if (totalSize > STRINGS_TARGET_BYTE_SIZE) {
49                 targetSizeExceeded = true;
50                 break;
51             }
52         }
53 
54         dest.writeBoolean(targetSizeExceeded);
55 
56         if (!targetSizeExceeded) {
57             dest.writeMap(map);
58             return;
59         }
60 
61         Parcel data = Parcel.obtain();
62         try {
63             data.writeMap(map);
64             dest.writeBlob(data.marshall());
65         } finally {
66             data.recycle();
67         }
68     }
69 
70     /**
71      * Retrieve a map previously written by {@link #writeHostMap(Parcel, Map)}.
72      */
73     @NonNull
74     @SuppressWarnings("rawtypes")
readHostMap(@onNull Parcel in, @NonNull T map, @NonNull ClassLoader classLoader)75     public static <T extends Map> T readHostMap(@NonNull Parcel in, @NonNull T map,
76             @NonNull ClassLoader classLoader) {
77         boolean targetSizeExceeded = in.readBoolean();
78 
79         if (!targetSizeExceeded) {
80             in.readMap(map, classLoader);
81             return map;
82         }
83 
84         Parcel data = Parcel.obtain();
85         try {
86             byte[] blob = in.readBlob();
87             data.unmarshall(blob, 0, blob.length);
88             data.setDataPosition(0);
89             data.readMap(map, classLoader);
90         } finally {
91             data.recycle();
92         }
93 
94         return map;
95     }
96 
97     /**
98      * {@link ArraySet} variant of {@link #writeHostMap(Parcel, Map)}.
99      */
writeHostSet(@onNull Parcel dest, @NonNull Set<String> set)100     public static void writeHostSet(@NonNull Parcel dest, @NonNull Set<String> set) {
101         boolean targetSizeExceeded = false;
102         int totalSize = dest.dataSize();
103         for (String host : set) {
104             totalSize += estimatedByteSizeOf(host);
105             if (totalSize > STRINGS_TARGET_BYTE_SIZE) {
106                 targetSizeExceeded = true;
107                 break;
108             }
109         }
110 
111         dest.writeBoolean(targetSizeExceeded);
112 
113         if (!targetSizeExceeded) {
114             writeSet(dest, set);
115             return;
116         }
117 
118         Parcel data = Parcel.obtain();
119         try {
120             writeSet(data, set);
121             dest.writeBlob(data.marshall());
122         } finally {
123             data.recycle();
124         }
125     }
126 
127     /**
128      * {@link ArraySet} variant of {@link #readHostMap(Parcel, Map, ClassLoader)}.
129      */
130     @NonNull
readHostSet(@onNull Parcel in)131     public static Set<String> readHostSet(@NonNull Parcel in) {
132         boolean targetSizeExceeded = in.readBoolean();
133 
134         if (!targetSizeExceeded) {
135             return readSet(in);
136         }
137 
138         Parcel data = Parcel.obtain();
139         try {
140             byte[] blob = in.readBlob();
141             data.unmarshall(blob, 0, blob.length);
142             data.setDataPosition(0);
143             return readSet(data);
144         } finally {
145             data.recycle();
146         }
147     }
148 
writeSet(@onNull Parcel dest, @Nullable Set<String> set)149     private static void writeSet(@NonNull Parcel dest, @Nullable Set<String> set) {
150         if (set == null) {
151             dest.writeInt(-1);
152             return;
153         }
154         dest.writeInt(set.size());
155         for (String string : set) {
156             dest.writeString(string);
157         }
158     }
159 
160     @NonNull
readSet(@onNull Parcel in)161     private static Set<String> readSet(@NonNull Parcel in) {
162         int size = in.readInt();
163         if (size == -1) {
164             return Collections.emptySet();
165         }
166 
167         ArraySet<String> set = new ArraySet<>(size);
168         for (int count = 0; count < size; count++) {
169             set.add(in.readString());
170         }
171         return set;
172     }
173 
174     /**
175      * Ballpark the size of domains to avoid unnecessary allocation of ashmem when sending domains
176      * across the client-server API.
177      */
estimatedByteSizeOf(String string)178     public static int estimatedByteSizeOf(String string) {
179         return string.length() * 2 + 12;
180     }
181 }
182