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
17package com.android.server.wifi.hotspot2.anqp;
18
19import android.net.Uri;
20
21import java.io.ByteArrayOutputStream;
22import java.io.IOException;
23import java.nio.charset.StandardCharsets;
24import java.util.Arrays;
25import java.util.List;
26import java.util.Locale;
27
28/**
29 * Utility class containing test data and object for {@link OsuProviderInfo}.
30 */
31public 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 = "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     */
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     */
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     */
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
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     */
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     */
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     */
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