1 /*
2  * Copyright (C) 2023 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.adservices.service.adselection;
18 
19 import android.annotation.NonNull;
20 
21 import com.android.adservices.LoggerFactory;
22 import com.android.adservices.service.profiling.Tracing;
23 
24 import java.io.ByteArrayInputStream;
25 import java.io.ByteArrayOutputStream;
26 import java.io.IOException;
27 import java.io.UncheckedIOException;
28 import java.util.Objects;
29 import java.util.zip.GZIPInputStream;
30 import java.util.zip.GZIPOutputStream;
31 
32 /** Wrapper class for data compression and decompression using GZIP */
33 public class AuctionServerDataCompressorGzip implements AuctionServerDataCompressor {
34     private static final LoggerFactory.Logger sLogger = LoggerFactory.getFledgeLogger();
35     public static final int VERSION = 2;
36     private static final String IO_ERROR_DURING_COMPRESSION = "IOException when compressing data.";
37     private static final String IO_ERROR_DURING_DECOMPRESSION =
38             "IOException when decompressing data.";
39 
40     /** Compresses given data using GZIP */
compress(@onNull UncompressedData uncompressedData)41     public CompressedData compress(@NonNull UncompressedData uncompressedData) {
42         Objects.requireNonNull(uncompressedData);
43 
44         int traceCookie = Tracing.beginAsyncSection(Tracing.AUCTION_SERVER_GZIP_COMPRESS);
45         sLogger.v("Compression request for each BuyerInput with version " + VERSION);
46 
47         ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
48         try {
49             GZIPOutputStream gzipOutputStream = new GZIPOutputStream(byteArrayOutputStream);
50             gzipOutputStream.write(uncompressedData.getData());
51             gzipOutputStream.close();
52         } catch (IOException e) {
53             sLogger.e(IO_ERROR_DURING_COMPRESSION);
54             throw new UncheckedIOException(e);
55         }
56         CompressedData compressedData = CompressedData.create(byteArrayOutputStream.toByteArray());
57         Tracing.endAsyncSection(Tracing.AUCTION_SERVER_GZIP_COMPRESS, traceCookie);
58         return compressedData;
59     }
60 
61     /** Decompresses data compressed by GZIP */
decompress(@onNull CompressedData uncompressedData)62     public UncompressedData decompress(@NonNull CompressedData uncompressedData) {
63         Objects.requireNonNull(uncompressedData);
64 
65         ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
66         try {
67             ByteArrayInputStream byteArrayInputStream =
68                     new ByteArrayInputStream(uncompressedData.getData());
69             GZIPInputStream gzipInputStream = new GZIPInputStream(byteArrayInputStream);
70             byte[] buffer = new byte[BUFFER_SIZE];
71             int bytesRead;
72             while ((bytesRead = gzipInputStream.read(buffer)) > 0) {
73                 byteArrayOutputStream.write(buffer, 0, bytesRead);
74             }
75             gzipInputStream.close();
76         } catch (IOException e) {
77             sLogger.e(IO_ERROR_DURING_DECOMPRESSION);
78             throw new UncheckedIOException(e);
79         }
80 
81         return UncompressedData.create(byteArrayOutputStream.toByteArray());
82     }
83 }
84