Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d89243bf authored by Ricky Wai's avatar Ricky Wai
Browse files

Generate network watchlist report as proto

Bug: 63908748
Test: runtest frameworks-services -p com.android.server.net.watchlist
Change-Id: I814abeb7700dd4fb0b74ee1fc95bac32718db739
parent 899715b9
Loading
Loading
Loading
Loading
+36 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

syntax = "proto2";
package com.android.service;

option java_multiple_files = true;

// It will be used by com.android.server.net.watchlist.ReportEncoder to
// generate network watchlist report.
message NetworkWatchlistReportProto {

  optional int32 report_version = 1;

  optional string watchlist_config_hash = 2;

  repeated NetworkWatchlistAppResultProto app_result = 3;
}

message NetworkWatchlistAppResultProto {
  optional string app_digest = 1;
  optional bool encoded_result = 2;
}
+22 −47
Original line number Diff line number Diff line
@@ -19,38 +19,31 @@ package com.android.server.net.watchlist;

import android.annotation.Nullable;
import android.util.Log;
import android.util.proto.ProtoOutputStream;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.HexDump;
import com.android.service.NetworkWatchlistReportProto;
import com.android.service.NetworkWatchlistAppResultProto;

import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * Helper class to encode and generate serialized DP encoded watchlist report.
 *
 * <p>Serialized report data structure:
 * [4 bytes magic number][4_bytes_report_version_code][32_bytes_watchlist_hash]
 * [app_1_digest_byte_array][app_1_encoded_visited_cnc_byte]
 * [app_2_digest_byte_array][app_2_encoded_visited_cnc_byte]
 * ...
 *
 * Total size: 4 + 4 + 32 + (32+1)*N, where N = number of digests
 * Helper class to encode and generate serialized DP encoded watchlist proto report.
 */
class ReportEncoder {

    private static final String TAG = "ReportEncoder";

    // Report header magic number
    private static final byte[] MAGIC_NUMBER = {(byte) 0x8D, (byte) 0x37, (byte) 0x0A, (byte) 0xAC};
    // Report version number, as file format / parameters can be changed in later version, we need
    // to have versioning on watchlist report format
    private static final byte[] REPORT_VERSION = {(byte) 0x00, (byte) 0x01};
    private static final int REPORT_VERSION = 1;

    private static final int WATCHLIST_HASH_SIZE = 32;
    private static final int APP_DIGEST_SIZE = 32;

    /**
     * Apply DP on watchlist results, and generate a serialized watchlist report ready to store
@@ -64,11 +57,10 @@ class ReportEncoder {
    }

    /**
     * Convert DP encoded watchlist report into byte[] format.
     * TODO: Serialize it using protobuf
     * Convert DP encoded watchlist report into proto format.
     *
     * @param encodedReportMap DP encoded watchlist report.
     * @return Watchlist report in byte[] format, which will be shared in Dropbox. Null if
     * @return Watchlist report in proto format, which will be shared in Dropbox. Null if
     * watchlist report cannot be generated.
     */
    @Nullable
@@ -85,42 +77,25 @@ class ReportEncoder {
            Log.e(TAG, "Unexpected hash length");
            return null;
        }
        final int reportMapSize = encodedReportMap.size();
        final byte[] outputReport =
                new byte[MAGIC_NUMBER.length + REPORT_VERSION.length + WATCHLIST_HASH_SIZE
                        + reportMapSize * (APP_DIGEST_SIZE + /* Result */ 1)];
        final List<String> sortedKeys = new ArrayList(encodedReportMap.keySet());
        Collections.sort(sortedKeys);

        int offset = 0;

        // Set magic number to report
        System.arraycopy(MAGIC_NUMBER, 0, outputReport, offset, MAGIC_NUMBER.length);
        offset += MAGIC_NUMBER.length;
        final ByteArrayOutputStream reportOutputStream = new ByteArrayOutputStream();
        final ProtoOutputStream proto = new ProtoOutputStream(reportOutputStream);

        // Set report version to report
        System.arraycopy(REPORT_VERSION, 0, outputReport, offset, REPORT_VERSION.length);
        offset += REPORT_VERSION.length;

        // Set watchlist hash to report
        System.arraycopy(watchlistHash, 0, outputReport, offset, watchlistHash.length);
        offset += watchlistHash.length;
        proto.write(NetworkWatchlistReportProto.REPORT_VERSION, REPORT_VERSION);
        proto.write(NetworkWatchlistReportProto.WATCHLIST_CONFIG_HASH,
                HexDump.toHexString(watchlistHash));

        // Set app digest, encoded_isPha pair to report
        for (int i = 0; i < reportMapSize; i++) {
            String key = sortedKeys.get(i);
        for (Map.Entry<String, Boolean> entry : encodedReportMap.entrySet()) {
            String key = entry.getKey();
            byte[] digest = HexDump.hexStringToByteArray(key);
            boolean isPha = encodedReportMap.get(key);
            System.arraycopy(digest, 0, outputReport, offset, APP_DIGEST_SIZE);
            offset += digest.length;
            outputReport[offset] = (byte) (isPha ? 1 : 0);
            offset += 1;
        }
        if (outputReport.length != offset) {
            Log.e(TAG, "Watchlist report size does not match! Offset: " + offset + ", report size: "
                    + outputReport.length);

            boolean encodedResult = entry.getValue();
            long token = proto.start(NetworkWatchlistReportProto.APP_RESULT);
            proto.write(NetworkWatchlistAppResultProto.APP_DIGEST, key);
            proto.write(NetworkWatchlistAppResultProto.ENCODED_RESULT, encodedResult);
            proto.end(token);
        }
        return outputReport;
        proto.flush();
        return reportOutputStream.toByteArray();
    }
}
+2 −1
Original line number Diff line number Diff line
@@ -27,7 +27,8 @@ LOCAL_STATIC_JAVA_LIBRARIES := \
    ShortcutManagerTestUtils \
    truth-prebuilt \
    testables \
    testng
    testng \
    platformprotosnano

LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/aidl

+28 −25
Original line number Diff line number Diff line
/*
 * Copyright 2017 The Android Open Source Project
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
@@ -16,15 +16,13 @@

package com.android.server.net.watchlist;

import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;

import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;

import com.android.internal.util.HexDump;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -37,18 +35,21 @@ import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;

import com.android.service.nano.NetworkWatchlistReportProto;
import com.android.service.nano.NetworkWatchlistAppResultProto;

/**
 * runtest frameworks-services -c com.android.server.net.watchlist.ReportUtilsTests
 * runtest frameworks-services -c com.android.server.net.watchlist.ReportEncoderTests
 */
@RunWith(AndroidJUnit4.class)
@SmallTest
public class ReportUtilsTests {
public class ReportEncoderTests {

    private static final String TEST_XML_1 = "NetworkWatchlistTest/watchlist_config_test1.xml";
    private static final String TEST_XML_1_HASH =
            "C99F27A08B1FDB15B101098E12BB2A0AA0D474E23C50F24920A52AB2322BFD94";
    private static final String REPORT_HEADER_MAGIC = "8D370AAC";
    private static final String REPORT_HEADER_VERSION = "0001";
    private static final int REPORT_VERSION = 1;
    private static final int EXPECTED_REPORT_VERSION = 1;

    private Context mContext;
    private File mTestXmlFile;
@@ -67,26 +68,28 @@ public class ReportUtilsTests {

    @Test
    public void testReportUtils_serializeReport() throws Exception {
        final byte[] expectedResult = HexDump.hexStringToByteArray(
                REPORT_HEADER_MAGIC + REPORT_HEADER_VERSION + TEST_XML_1_HASH
                        + "B86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB43" + "01"
                        + "C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB44" + "00"
                        + "D86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB45" + "00"
                        + "E86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB46" + "01"
                        + "F86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB47" + "01"
        );
        HashMap<String, Boolean> input = new HashMap<>();
        input.put("C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB44", false);
        input.put("B86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB43", true);
        input.put("D86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB45", false);
        input.put("E86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB46", true);
        input.put("F86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB47", true);
        HashMap<String, Boolean> map = new HashMap<>();
        map.put("C86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB44", false);
        map.put("B86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB43", true);
        map.put("D86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB45", false);
        map.put("E86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB46", true);
        map.put("F86F9D37425340B635F43D6BC2506630761ADA71F5E6BBDBCA4651C479F9FB47", true);

        copyWatchlistConfigXml(mContext, TEST_XML_1, mTestXmlFile);
        WatchlistConfig config = new WatchlistConfig(mTestXmlFile);

        byte[] result = ReportEncoder.serializeReport(config, input);
        assertArrayEquals(expectedResult, result);
        final byte[] result = ReportEncoder.serializeReport(config, map);

        // Parse result back to NetworkWatchlistReportDumpProto
        final NetworkWatchlistReportProto reportProto =
                NetworkWatchlistReportProto.parseFrom(result);
        assertEquals(REPORT_VERSION, reportProto.reportVersion);
        assertEquals(TEST_XML_1_HASH, reportProto.watchlistConfigHash);
        assertEquals(map.size(), reportProto.appResult.length);
        for (NetworkWatchlistAppResultProto appProto : reportProto.appResult) {
            final String digest = appProto.appDigest;
            assertEquals(map.get(digest), appProto.encodedResult);
        }
    }

    private static void copyWatchlistConfigXml(Context context, String xmlAsset, File outFile)