1 /*
2  * Copyright (C) 2017 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.server.wifi.hotspot2.anqp;
18 
19 import android.net.Uri;
20 
21 import java.io.ByteArrayOutputStream;
22 import java.io.IOException;
23 import java.nio.charset.StandardCharsets;
24 import java.util.Arrays;
25 import java.util.List;
26 import java.util.Locale;
27 
28 /**
29  * Utility class containing test data and object for {@link OsuProviderInfo}.
30  */
31 public class OsuProviderInfoTestUtil {
32     // Test data
33     private static final List<I18Name> TEST_FRIENDLY_NAMES =
34             Arrays.asList(new I18Name("en", Locale.forLanguageTag("en"), "FriendlyName"));
35     private static final String TEST_SERVER_URI = "https://test.server.com";
36     private static final List<Integer> TEST_METHOD_LIST = Arrays.asList(0, 1);
37     private static final List<IconInfo> TEST_ICON_INFO_LIST =
38             Arrays.asList(IconInfoTestUtil.TEST_ICON_INFO);
39     private static final String TEST_NAI = "network_access@test.com";
40     private static final List<I18Name> TEST_SERVICE_DESCRIPTIONS =
41             Arrays.asList(new I18Name("en", Locale.forLanguageTag("en"), "Test Service"));
42 
43     /**
44      * {@link IconInfo} object with pre-defined test data.
45      */
46     public static final OsuProviderInfo TEST_OSU_PROVIDER_INFO =
47             new OsuProviderInfo(TEST_FRIENDLY_NAMES, Uri.parse(TEST_SERVER_URI), TEST_METHOD_LIST,
48                     TEST_ICON_INFO_LIST, TEST_NAI, TEST_SERVICE_DESCRIPTIONS);
49 
50     /**
51      * Raw bytes of icon info with pre-defined test data.
52      */
53     public static final byte[] TEST_OSU_PROVIDER_INFO_RAW_BYTES = getTestData();
54 
55     public static final byte[] TEST_OSU_PROVIDER_INFO_RAW_BYTES_WITH_INVALID_LENGTH =
56             getTestDataWithInvalidLength();
57 
58     /**
59      * Generate and return the raw data based on pre-defined test data.
60      *
61      * @return byte[]
62      */
getTestData()63     private static byte[] getTestData() {
64         try {
65             ByteArrayOutputStream out = new ByteArrayOutputStream();
66             byte[] payload = getTestPayload();
67             writeShortLE(out, payload.length);
68             out.write(payload);
69             return out.toByteArray();
70         } catch (Exception e) {
71             return null;
72         }
73     }
74 
75     /**
76      * Generate and return the raw data based on pre-defined test data.
77      *
78      * @return byte[]
79      */
getTestDataWithInvalidLength()80     private static byte[] getTestDataWithInvalidLength() {
81         try {
82             ByteArrayOutputStream out = new ByteArrayOutputStream();
83             byte[] payload = getTestPayload();
84             // Set length to less than the minimum required.
85             writeShortLE(out, OsuProviderInfo.MINIMUM_LENGTH - 1);
86             out.write(payload);
87             return out.toByteArray();
88         } catch (Exception e) {
89             return null;
90         }
91     }
92 
93     /**
94      * Generate and return the payload containing OSU provider test data, excluding the length
95      * field.
96      *
97      * @return byte[]
98      * @throws IOException
99      */
getTestPayload()100     private static byte[] getTestPayload() throws IOException {
101         ByteArrayOutputStream out = new ByteArrayOutputStream();
102 
103         // Write friendly name list.
104         byte[] friendlyNamesData = getI18NameListData(TEST_FRIENDLY_NAMES);
105         writeShortLE(out, friendlyNamesData.length);
106         out.write(friendlyNamesData);
107 
108         // Write server URI.
109         writeByteArrayWithLength(out, TEST_SERVER_URI.getBytes(StandardCharsets.UTF_8));
110 
111         // Write method list.
112         out.write((byte) TEST_METHOD_LIST.size());
113         for (Integer method : TEST_METHOD_LIST) {
114             out.write((byte) method.intValue());
115         }
116 
117         // Write icon info list.
118         writeShortLE(out, IconInfoTestUtil.TEST_ICON_INFO_RAW_BYTES.length);
119         out.write(IconInfoTestUtil.TEST_ICON_INFO_RAW_BYTES);
120 
121         // Write NAI.
122         writeByteArrayWithLength(out, TEST_NAI.getBytes(StandardCharsets.UTF_8));
123 
124         // Write service descriptions.
125         byte[] serviceDescriptionsData = getI18NameListData(TEST_SERVICE_DESCRIPTIONS);
126         writeShortLE(out, serviceDescriptionsData.length);
127         out.write(serviceDescriptionsData);
128 
129         return out.toByteArray();
130     }
131 
getI18NameListData(List<I18Name> nameList)132     private static byte[] getI18NameListData(List<I18Name> nameList) throws IOException {
133         ByteArrayOutputStream out = new ByteArrayOutputStream();
134         for (I18Name name : nameList) {
135             byte[] data = getI18NameData(name);
136             out.write((byte) data.length);
137             out.write(data);
138         }
139         return out.toByteArray();
140     }
141 
142     /**
143      * Format the raw bytes for the given {@link I18Name}.
144      *
145      * @param value The {@link I18Name} to serialize
146      * @return byte[]
147      * @throws IOException
148      */
getI18NameData(I18Name value)149     private static byte[] getI18NameData(I18Name value) throws IOException {
150         ByteArrayOutputStream out = new ByteArrayOutputStream();
151         out.write(value.getLanguage().getBytes(StandardCharsets.US_ASCII));
152         out.write((byte) 0);    // Padding for language code.
153         out.write(value.getText().getBytes(StandardCharsets.UTF_8));
154         return out.toByteArray();
155     }
156 
157     /**
158      * Write the lower 2-bytes of an integer to the given output stream in Little-Endian.
159      *
160      * @param out The output stream to write to
161      * @param value The integer value to write
162      */
writeShortLE(ByteArrayOutputStream out, int value)163     private static void writeShortLE(ByteArrayOutputStream out, int value) {
164         out.write(value & 0xFF);
165         out.write((value >> 8) & 0xFF);
166     }
167 
168     /**
169      * Write the given byte array to the given output stream, the array data is prefixed with a
170      * byte specifying the length of the byte array.
171      *
172      * @param out The output stream to write to
173      * @param data The byte array to write
174      * @throws IOException
175      */
writeByteArrayWithLength(ByteArrayOutputStream out, byte[] data)176     private static void writeByteArrayWithLength(ByteArrayOutputStream out, byte[] data)
177             throws IOException {
178         out.write((byte) data.length);
179         out.write(data);
180     }
181 }
182