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

Commit 066b551e authored by Ricky Wai's avatar Ricky Wai Committed by Android (Google) Code Review
Browse files

Merge "Generate network watchlist report as proto"

parents 56ac8ce0 d89243bf
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)